diff --git a/.github/ci-status.md b/.github/ci-status.md index f3e088fd6043..8d4cea10dba4 100644 --- a/.github/ci-status.md +++ b/.github/ci-status.md @@ -1,11 +1,15 @@ master : [![C/C++ CI](https://github.com/openssh/openssh-portable/actions/workflows/c-cpp.yml/badge.svg)](https://github.com/openssh/openssh-portable/actions/workflows/c-cpp.yml?query=branch:master) [![C/C++ CI self-hosted](https://github.com/openssh/openssh-portable-selfhosted/actions/workflows/selfhosted.yml/badge.svg)](https://github.com/openssh/openssh-portable-selfhosted/actions/workflows/selfhosted.yml?query=branch:master) [![Upstream self-hosted](https://github.com/openssh/openssh-portable-selfhosted/actions/workflows/upstream.yml/badge.svg)](https://github.com/openssh/openssh-portable-selfhosted/actions/workflows/upstream.yml?query=branch:master) [![CIFuzz](https://github.com/openssh/openssh-portable/actions/workflows/cifuzz.yml/badge.svg)](https://github.com/openssh/openssh-portable/actions/workflows/cifuzz.yml) [![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/openssh.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:openssh) [![Coverity Status](https://scan.coverity.com/projects/21341/badge.svg)](https://scan.coverity.com/projects/openssh-portable) +9.4 : +[![C/C++ CI](https://github.com/openssh/openssh-portable/actions/workflows/c-cpp.yml/badge.svg?branch=V_9_4)](https://github.com/openssh/openssh-portable/actions/workflows/c-cpp.yml?query=branch:V_9_4) +[![C/C++ CI self-hosted](https://github.com/openssh/openssh-portable-selfhosted/actions/workflows/selfhosted.yml/badge.svg?branch=V_9_4)](https://github.com/openssh/openssh-portable-selfhosted/actions/workflows/selfhosted.yml?query=branch:V_9_4) + 9.3 : [![C/C++ CI](https://github.com/openssh/openssh-portable/actions/workflows/c-cpp.yml/badge.svg?branch=V_9_3)](https://github.com/openssh/openssh-portable/actions/workflows/c-cpp.yml?query=branch:V_9_3) [![C/C++ CI self-hosted](https://github.com/openssh/openssh-portable-selfhosted/actions/workflows/selfhosted.yml/badge.svg?branch=V_9_3)](https://github.com/openssh/openssh-portable-selfhosted/actions/workflows/selfhosted.yml?query=branch:V_9_3) diff --git a/.github/configs b/.github/configs index e054eb3196b5..c7d6a55ab962 100755 --- a/.github/configs +++ b/.github/configs @@ -1,340 +1,351 @@ #!/bin/sh # # usage: configs vmname test_config (or '' for default) # # Sets the following variables: # CONFIGFLAGS options to ./configure # SSHD_CONFOPTS sshd_config options # TEST_TARGET make target used when testing. defaults to "tests". # LTESTS config=$1 if [ "$config" = "" ]; then config="default" fi unset CC CFLAGS CPPFLAGS LDFLAGS LTESTS SUDO TEST_TARGET="tests compat-tests" LTESTS="" SKIP_LTESTS="" SUDO=sudo # run with sudo by default TEST_SSH_UNSAFE_PERMISSIONS=1 # Stop on first test failure to minimize logs TEST_SSH_FAIL_FATAL=yes CONFIGFLAGS="" LIBCRYPTOFLAGS="" case "$config" in default|sol64) ;; c89) + # If we don't have LLONG_MAX, configure will figure out that it can + # get it by setting -std=gnu99, at which point we won't be testing + # C89 any more. To avoid this, feed it in via CFLAGS. + llong_max=`gcc -E -dM - /dev/null)" ]; then REGRESS_INTEROP_PUTTY=yes export REGRESS_INTEROP_PUTTY fi export CC CFLAGS CPPFLAGS LDFLAGS LTESTS SUDO export TEST_TARGET TEST_SSH_UNSAFE_PERMISSIONS TEST_SSH_FAIL_FATAL diff --git a/.github/setup_ci.sh b/.github/setup_ci.sh index 154f51bdc205..010a333a6642 100755 --- a/.github/setup_ci.sh +++ b/.github/setup_ci.sh @@ -1,216 +1,224 @@ #!/bin/sh PACKAGES="" . .github/configs $@ host=`./config.guess` echo "config.guess: $host" case "$host" in *cygwin) PACKAGER=setup echo Setting CYGWIN system environment variable. setx CYGWIN "binmode" echo Removing extended ACLs so umask works as expected. setfacl -b . regress PACKAGES="$PACKAGES,autoconf,automake,cygwin-devel,gcc-core" PACKAGES="$PACKAGES,make,openssl-devel,zlib-devel" ;; *-darwin*) PACKAGER=brew brew install automake exit 0 ;; *) PACKAGER=apt esac TARGETS=$@ INSTALL_FIDO_PPA="no" export DEBIAN_FRONTEND=noninteractive #echo "Setting up for '$TARGETS'" set -ex if [ -x "`which lsb_release 2>&1`" ]; then lsb_release -a fi # Ubuntu 22.04 defaults to private home dirs which prevent the # agent-getpeerid test from running ssh-add as nobody. See # https://github.com/actions/runner-images/issues/6106 if [ ! -z "$SUDO" ] && ! "$SUDO" -u nobody test -x ~; then echo ~ is not executable by nobody, adding perms. chmod go+x ~ fi if [ "${TARGETS}" = "kitchensink" ]; then TARGETS="krb5 libedit pam sk selinux" fi for flag in $CONFIGFLAGS; do case "$flag" in --with-pam) TARGETS="${TARGETS} pam" ;; --with-libedit) TARGETS="${TARGETS} libedit" ;; esac done for TARGET in $TARGETS; do case $TARGET in default|without-openssl|without-zlib|c89) # nothing to do ;; clang-sanitize*) PACKAGES="$PACKAGES clang-12" ;; cygwin-release) PACKAGES="$PACKAGES libcrypt-devel libfido2-devel libkrb5-devel" ;; gcc-sanitize*) ;; clang-*|gcc-*) compiler=$(echo $TARGET | sed 's/-Werror//') PACKAGES="$PACKAGES $compiler" ;; krb5) PACKAGES="$PACKAGES libkrb5-dev" ;; heimdal) PACKAGES="$PACKAGES heimdal-dev" ;; libedit) case "$PACKAGER" in setup) PACKAGES="$PACKAGES libedit-devel" ;; apt) PACKAGES="$PACKAGES libedit-dev" ;; esac ;; *pam) PACKAGES="$PACKAGES libpam0g-dev" ;; sk) INSTALL_FIDO_PPA="yes" PACKAGES="$PACKAGES libfido2-dev libu2f-host-dev libcbor-dev" ;; selinux) PACKAGES="$PACKAGES libselinux1-dev selinux-policy-dev" ;; hardenedmalloc) INSTALL_HARDENED_MALLOC=yes ;; musl) PACKAGES="$PACKAGES musl-tools" ;; tcmalloc) PACKAGES="$PACKAGES libgoogle-perftools-dev" ;; openssl-noec) INSTALL_OPENSSL=OpenSSL_1_1_1k SSLCONFOPTS="no-ec" ;; openssl-*) INSTALL_OPENSSL=$(echo ${TARGET} | cut -f2 -d-) case ${INSTALL_OPENSSL} in 1.1.1_stable) INSTALL_OPENSSL="OpenSSL_1_1_1-stable" ;; 1.*) INSTALL_OPENSSL="OpenSSL_$(echo ${INSTALL_OPENSSL} | tr . _)" ;; 3.*) INSTALL_OPENSSL="openssl-${INSTALL_OPENSSL}" ;; esac PACKAGES="${PACKAGES} putty-tools" ;; libressl-*) INSTALL_LIBRESSL=$(echo ${TARGET} | cut -f2 -d-) case ${INSTALL_LIBRESSL} in master) ;; *) INSTALL_LIBRESSL="$(echo ${TARGET} | cut -f2 -d-)" ;; esac PACKAGES="${PACKAGES} putty-tools" ;; boringssl) INSTALL_BORINGSSL=1 PACKAGES="${PACKAGES} cmake ninja-build" ;; valgrind*) PACKAGES="$PACKAGES valgrind" ;; + zlib-*) + ;; *) echo "Invalid option '${TARGET}'" exit 1 ;; esac done if [ "yes" = "$INSTALL_FIDO_PPA" ]; then sudo apt update -qq sudo apt install -qy software-properties-common sudo apt-add-repository -y ppa:yubico/stable fi tries=3 while [ ! -z "$PACKAGES" ] && [ "$tries" -gt "0" ]; do case "$PACKAGER" in apt) sudo apt update -qq if sudo apt install -qy $PACKAGES; then PACKAGES="" fi ;; setup) if /cygdrive/c/setup.exe -q -P `echo "$PACKAGES" | tr ' ' ,`; then PACKAGES="" fi ;; esac if [ ! -z "$PACKAGES" ]; then sleep 90 fi tries=$(($tries - 1)) done if [ ! -z "$PACKAGES" ]; then echo "Package installation failed." exit 1 fi if [ "${INSTALL_HARDENED_MALLOC}" = "yes" ]; then (cd ${HOME} && git clone https://github.com/GrapheneOS/hardened_malloc.git && cd ${HOME}/hardened_malloc && make -j2 && sudo cp out/libhardened_malloc.so /usr/lib/) fi if [ ! -z "${INSTALL_OPENSSL}" ]; then (cd ${HOME} && git clone https://github.com/openssl/openssl.git && cd ${HOME}/openssl && git checkout ${INSTALL_OPENSSL} && ./config no-threads shared ${SSLCONFOPTS} \ --prefix=/opt/openssl && make && sudo make install_sw) fi if [ ! -z "${INSTALL_LIBRESSL}" ]; then if [ "${INSTALL_LIBRESSL}" = "master" ]; then (mkdir -p ${HOME}/libressl && cd ${HOME}/libressl && git clone https://github.com/libressl-portable/portable.git && cd ${HOME}/libressl/portable && git checkout ${INSTALL_LIBRESSL} && sh update.sh && sh autogen.sh && ./configure --prefix=/opt/libressl && make -j2 && sudo make install) else LIBRESSL_URLBASE=https://cdn.openbsd.org/pub/OpenBSD/LibreSSL (cd ${HOME} && wget ${LIBRESSL_URLBASE}/libressl-${INSTALL_LIBRESSL}.tar.gz && tar xfz libressl-${INSTALL_LIBRESSL}.tar.gz && cd libressl-${INSTALL_LIBRESSL} && ./configure --prefix=/opt/libressl && make -j2 && sudo make install) fi fi if [ ! -z "${INSTALL_BORINGSSL}" ]; then (cd ${HOME} && git clone https://boringssl.googlesource.com/boringssl && cd ${HOME}/boringssl && mkdir build && cd build && cmake -GNinja -DCMAKE_POSITION_INDEPENDENT_CODE=ON .. && ninja && mkdir -p /opt/boringssl/lib && cp ${HOME}/boringssl/build/crypto/libcrypto.a /opt/boringssl/lib && cp -r ${HOME}/boringssl/include /opt/boringssl) fi + +if [ ! -z "${INSTALL_ZLIB}" ]; then + (cd ${HOME} && git clone https://github.com/madler/zlib.git && + cd ${HOME}/zlib && ./configure && make && + sudo make install prefix=/opt/zlib) +fi diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml index e4e2a64e05d2..be0c97f84cfd 100644 --- a/.github/workflows/c-cpp.yml +++ b/.github/workflows/c-cpp.yml @@ -1,128 +1,129 @@ name: C/C++ CI on: push: paths: [ '**.c', '**.h', '**.m4', '**.sh', '.github/**', '**/Makefile.in', 'configure.ac' ] pull_request: paths: [ '**.c', '**.h', '**.m4', '**.sh', '.github/**', '**/Makefile.in', 'configure.ac' ] jobs: ci: if: github.repository != 'openssh/openssh-portable-selfhosted' strategy: fail-fast: false matrix: # First we test all OSes in the default configuration. target: - ubuntu-20.04 - ubuntu-22.04 - macos-11 - macos-12 - macos-13 - windows-2019 - windows-2022 config: [default] # Then we include any extra configs we want to test for specific VMs. # Valgrind slows things down quite a bit, so start them first. include: - { target: windows-2019, config: cygwin-release } - { target: windows-2022, config: cygwin-release } - { target: ubuntu-20.04, config: valgrind-1 } - { target: ubuntu-20.04, config: valgrind-2 } - { target: ubuntu-20.04, config: valgrind-3 } - { target: ubuntu-20.04, config: valgrind-4 } - { target: ubuntu-20.04, config: valgrind-5 } - { target: ubuntu-20.04, config: valgrind-unit } - { target: ubuntu-20.04, config: c89 } - { target: ubuntu-20.04, config: clang-6.0 } - { target: ubuntu-20.04, config: clang-8 } - { target: ubuntu-20.04, config: clang-9 } - { target: ubuntu-20.04, config: clang-10 } - { target: ubuntu-20.04, config: clang-11 } - { target: ubuntu-20.04, config: clang-12-Werror } - { target: ubuntu-20.04, config: clang-sanitize-address } - { target: ubuntu-20.04, config: clang-sanitize-undefined } - { target: ubuntu-20.04, config: gcc-sanitize-address } - { target: ubuntu-20.04, config: gcc-sanitize-undefined } - { target: ubuntu-20.04, config: gcc-7 } - { target: ubuntu-20.04, config: gcc-8 } - { target: ubuntu-20.04, config: gcc-10 } - { target: ubuntu-20.04, config: gcc-11-Werror } - { target: ubuntu-20.04, config: pam } - { target: ubuntu-20.04, config: kitchensink } - { target: ubuntu-22.04, config: hardenedmalloc } - { target: ubuntu-20.04, config: tcmalloc } - { target: ubuntu-20.04, config: musl } - { target: ubuntu-latest, config: boringssl } - { target: ubuntu-latest, config: libressl-master } - { target: ubuntu-latest, config: libressl-3.2.6 } - { target: ubuntu-latest, config: libressl-3.3.6 } - { target: ubuntu-latest, config: libressl-3.4.3 } - { target: ubuntu-latest, config: libressl-3.5.3 } - { target: ubuntu-latest, config: libressl-3.6.1 } - { target: ubuntu-latest, config: libressl-3.7.2 } - { target: ubuntu-latest, config: openssl-master } - { target: ubuntu-latest, config: openssl-noec } - { target: ubuntu-latest, config: openssl-1.1.1 } - { target: ubuntu-latest, config: openssl-1.1.1k } - { target: ubuntu-latest, config: openssl-1.1.1n } - { target: ubuntu-latest, config: openssl-1.1.1q } - { target: ubuntu-latest, config: openssl-1.1.1t } - { target: ubuntu-latest, config: openssl-3.0.0 } - { target: ubuntu-latest, config: openssl-3.0.7 } - { target: ubuntu-latest, config: openssl-3.1.0 } - { target: ubuntu-latest, config: openssl-1.1.1_stable } - { target: ubuntu-latest, config: openssl-3.0 } # stable branch + - { target: ubuntu-latest, config: zlib-develop } - { target: ubuntu-22.04, config: pam } - { target: ubuntu-22.04, config: krb5 } - { target: ubuntu-22.04, config: heimdal } - { target: ubuntu-22.04, config: libedit } - { target: ubuntu-22.04, config: sk } - { target: ubuntu-22.04, config: selinux } - { target: ubuntu-22.04, config: kitchensink } - { target: ubuntu-22.04, config: without-openssl } - { target: macos-11, config: pam } - { target: macos-12, config: pam } - { target: macos-13, config: pam } runs-on: ${{ matrix.target }} steps: - name: set cygwin git params if: ${{ startsWith(matrix.target, 'windows') }} run: git config --global core.autocrlf input - name: install cygwin if: ${{ startsWith(matrix.target, 'windows') }} uses: cygwin/cygwin-install-action@master - uses: actions/checkout@main - name: setup CI system run: sh ./.github/setup_ci.sh ${{ matrix.config }} - name: autoreconf run: sh -c autoreconf - name: configure run: sh ./.github/configure.sh ${{ matrix.config }} - name: save config uses: actions/upload-artifact@main with: name: ${{ matrix.target }}-${{ matrix.config }}-config path: config.h - name: make clean run: make clean - name: make run: make -j2 - name: make tests run: sh ./.github/run_test.sh ${{ matrix.config }} env: TEST_SSH_UNSAFE_PERMISSIONS: 1 TEST_SSH_HOSTBASED_AUTH: yes - name: save logs if: failure() uses: actions/upload-artifact@main with: name: ${{ matrix.target }}-${{ matrix.config }}-logs path: | config.h config.log regress/*.log regress/valgrind-out/ regress/asan.log.* regress/msan.log.* regress/log/* diff --git a/.github/workflows/selfhosted.yml b/.github/workflows/selfhosted.yml index e84db699ea31..de0a4125bf08 100644 --- a/.github/workflows/selfhosted.yml +++ b/.github/workflows/selfhosted.yml @@ -1,119 +1,122 @@ name: C/C++ CI self-hosted on: push: paths: [ '**.c', '**.h', '**.m4', '**.sh', '.github/**', '**/Makefile.in', 'configure.ac' ] jobs: selfhosted: if: github.repository == 'openssh/openssh-portable-selfhosted' runs-on: ${{ matrix.host }} timeout-minutes: 600 env: HOST: ${{ matrix.host }} TARGET_HOST: ${{ matrix.target }} TARGET_CONFIG: ${{ matrix.config }} strategy: fail-fast: false # We use a matrix in two parts: firstly all of the VMs are tested with the # default config. "target" corresponds to a label associated with the # worker. The default is an ephemeral VM running under libvirt. matrix: target: - alpine - centos7 - debian-i386 - dfly30 - dfly48 - dfly60 - dfly62 - dfly64 - fbsd10 - fbsd12 - fbsd13 - minix3 - nbsd3 - nbsd4 - nbsd8 - nbsd9 - obsd51 - obsd67 - obsd69 - obsd70 + - obsd72 + - obsd73 - obsdsnap - obsdsnap-i386 - openindiana - sol10 - sol11 config: - default host: - libvirt include: # Then we include extra libvirt test configs. - { target: aix51, config: default, host: libvirt } - { target: centos7, config: pam, host: libvirt } - { target: debian-i386, config: pam, host: libvirt } - { target: dfly30, config: without-openssl, host: libvirt} - { target: dfly48, config: pam ,host: libvirt } - { target: dfly58, config: pam, host: libvirt } - { target: dfly60, config: pam, host: libvirt } - { target: dfly62, config: pam, host: libvirt } - { target: fbsd10, config: pam, host: libvirt } - { target: fbsd12, config: pam, host: libvirt } - { target: fbsd13, config: pam, host: libvirt } - { target: nbsd8, config: pam, host: libvirt } - { target: nbsd9, config: pam, host: libvirt } - { target: openindiana, config: pam, host: libvirt } - { target: sol10, config: pam, host: libvirt } - { target: sol11, config: pam-krb5, host: libvirt } - { target: sol11, config: sol64, host: libvirt } # VMs with persistent disks that have their own runner. - { target: win10, config: default, host: win10 } - { target: win10, config: cygwin-release, host: win10 } # Physical hosts, with either native runners or remote via ssh. - { target: ARM, config: default, host: ARM } - { target: ARM64, config: default, host: ARM64 } - { target: ARM64, config: pam, host: ARM64 } - { target: debian-riscv64, config: default, host: debian-riscv64 } + - { target: obsd-arm64, config: default, host: obsd-arm64 } - { target: openwrt-mips, config: default, host: openwrt-mips } - { target: openwrt-mipsel, config: default, host: openwrt-mipsel } steps: - name: shutdown VM if running run: vmshutdown working-directory: ${{ runner.temp }} - uses: actions/checkout@main - name: autoreconf run: autoreconf - name: startup VM run: vmstartup working-directory: ${{ runner.temp }} - name: configure run: vmrun ./.github/configure.sh ${{ matrix.config }} - name: save config uses: actions/upload-artifact@main with: name: ${{ matrix.target }}-${{ matrix.config }}-config path: config.h - name: make clean run: vmrun make clean - name: make run: vmrun make - name: make tests run: vmrun ./.github/run_test.sh ${{ matrix.config }} timeout-minutes: 600 - name: save logs if: failure() uses: actions/upload-artifact@main with: name: ${{ matrix.target }}-${{ matrix.config }}-logs path: | config.h config.log regress/*.log regress/log/* regress/valgrind-out/ - name: shutdown VM if: always() run: vmshutdown working-directory: ${{ runner.temp }} diff --git a/ChangeLog b/ChangeLog index 3e16fbfd346d..61725d3a136b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,10174 +1,9557 @@ -commit daa5b2d869ee5a16f3ef9035aa0ad3c70cf4028e +commit 80a2f64b8c1d27383cc83d182b73920d1e6a91f1 +Author: Damien Miller +Date: Wed Oct 4 15:34:10 2023 +1100 + + crank version numbers + +commit f65f187b105d9b5c12fd750a211397d08c17c6d4 +Author: djm@openbsd.org +Date: Wed Oct 4 04:04:09 2023 +0000 + + upstream: openssh-9.5 + + OpenBSD-Commit-ID: 5e0af680480bd3b6f5560cf840ad032d48fd6b16 + +commit ffe27e54a4bb18d5d3bbd3f4cc93a41b8d94dfd2 +Author: djm@openbsd.org +Date: Wed Oct 4 04:03:50 2023 +0000 + + upstream: add some cautionary text about % token expansion and + + shell metacharacters; based on report from vinci AT protonmail.ch + + OpenBSD-Commit-ID: aa1450a54fcee2f153ef70368d90edb1e7019113 + +commit 60ec3d54fd1ebfe2dda75893fa1e870b8dffbb0d +Author: djm@openbsd.org +Date: Tue Oct 3 23:56:10 2023 +0000 + + upstream: fix link to agent draft; spotted by Jann Horn + + OpenBSD-Commit-ID: ff5bda21a83ec013db683e282256a85201d2dc4b + +commit 12e2d4b13f6f63ce2de13cbfcc9e4d0d4b4ab231 +Author: Damien Miller +Date: Wed Oct 4 10:54:04 2023 +1100 + + use portable provider allowlist path in manpage + + spotted by Jann Horn + +commit 6c2c6ffde75df95fd838039850d3dd3d84956d87 +Author: deraadt@openbsd.org +Date: Tue Sep 19 20:37:07 2023 +0000 + + upstream: typo; from Jim Spath + + OpenBSD-Commit-ID: 2f5fba917b5d4fcf93d9e0b0756c7f63189e228e + +commit b6b49130a0089b297245ee39e769231d7c763014 +Author: djm@openbsd.org +Date: Sun Sep 10 23:12:32 2023 +0000 + + upstream: rename remote_glob() -> sftp_glob() to match other API + + OpenBSD-Commit-ID: d9dfb3708d824ec02970a84d96cf5937e0887229 + +commit 21b79af6c8d2357c822c84cef3fbdb8001ed263b +Author: djm@openbsd.org +Date: Sun Sep 10 03:51:55 2023 +0000 + + upstream: typo in comment + + OpenBSD-Commit-ID: 69285e0ce962a7c6b0ab5f17a293c60a0a360a18 + +commit 41232d25532b4d2ef6c5db62efc0cf50a79d26ca +Author: Darren Tucker +Date: Sun Sep 10 15:45:38 2023 +1000 + + Use zero-call-used-regs=used with Apple compilers. + + Apple's versions of clang have version numbers that do not match the + corresponding upstream clang versions. Unfortunately, they do still + have the clang-15 zero-call-used-regs=all bug, so for now use the value + that doesn't result in segfaults. We could allowlist future versions + that are known to work. bz#3584 (and probably also our github CI + failures). + +commit 90ccc5918ea505bf156c31148b6b59a1bf5d6dc6 +Author: djm@openbsd.org +Date: Sun Sep 10 03:25:53 2023 +0000 + + upstream: randomise keystroke obfuscation intervals and average + + interval rate. ok dtucker@ + + OpenBSD-Commit-ID: 05f61d051ab418fcfc4857ff306e420037502382 + +commit bd1b9e52f5fa94d87223c90905c5fdc1a7c32aa6 +Author: djm@openbsd.org +Date: Fri Sep 8 06:34:24 2023 +0000 + + upstream: fix sizeof(*ptr) instead sizeof(ptr) in realloc (pointer here + + is char**, so harmless); spotted in CID 416964 + + OpenBSD-Commit-ID: c61caa4a5a667ee20bb1042098861e6c72c69002 + +commit c4f966482983e18601eec70a1563115de836616f +Author: djm@openbsd.org +Date: Fri Sep 8 06:10:57 2023 +0000 + + upstream: regress test recursive remote-remote directories copies where + + the directory contains a symlink to another directory. + + also remove errant `set -x` that snuck in at some point + + OpenBSD-Regress-ID: 1c94a48bdbd633ef2285954ee257725cd7bc456f + +commit 5e1dfe5014ebc194641678303e22ab3bba15f4e5 +Author: djm@openbsd.org +Date: Fri Sep 8 06:10:02 2023 +0000 + + upstream: fix recursive remote-remote copies of directories that + + contain symlinks to other directories (similar to bz3611) + + OpenBSD-Commit-ID: 7e19d2ae09b4f941bf8eecc3955c9120171da37f + +commit 7c0ce2bf98b303b6ad91493ee3247d96c18ba1f6 +Author: djm@openbsd.org +Date: Fri Sep 8 05:50:57 2023 +0000 + + upstream: regress test for recursive copies of directories containing + + symlinks to other directories. bz3611, ok dtucker@ + + OpenBSD-Regress-ID: eaa4c29cc5cddff4e72a16bcce14aeb1ecfc94b9 + +commit 2de990142a83bf60ef694378b8598706bc654b08 +Author: djm@openbsd.org +Date: Fri Sep 8 05:56:13 2023 +0000 + + upstream: the sftp code was one of my first contributions to + + OpenSSH and it shows - the function names are terrible. + + Rename do_blah() to sftp_blah() to make them less so. + + Completely mechanical except for sftp_stat() and sftp_lstat() which + change from returning a pointer to a static variable (error-prone) to + taking a pointer to a caller-provided receiver. + + OpenBSD-Commit-ID: eb54d6a72d0bbba4d623e2175cf5cc4c75dc2ba4 + +commit 249d8bd0472b53e3a2a0e138b4c030a31e83346a +Author: djm@openbsd.org +Date: Fri Sep 8 05:50:12 2023 +0000 + + upstream: fix scp in SFTP mode recursive upload and download of + + directories that contain symlinks to other directories. In scp mode, the + links would be followed, but in SFTP mode they were not. bz3611, ok dtucker@ + + OpenBSD-Commit-ID: 9760fda668eaa94a992250d7670dfbc62a45197c + +commit 0e1f4401c466fa4fdaea81b6dadc8dd1fc4cf0af +Author: djm@openbsd.org +Date: Wed Sep 6 23:36:09 2023 +0000 + + upstream: regression test for override of subsystem in match blocks + + OpenBSD-Regress-ID: 5f8135da3bfda71067084c048d717b0e8793e87c + +commit 8a1450c62035e834d8a79a5d0d1c904236f9dcfe +Author: djm@openbsd.org +Date: Wed Sep 6 23:35:35 2023 +0000 + + upstream: allow override of Sybsystem directives in sshd Match + + blocks + + OpenBSD-Commit-ID: 3911d18a826a2d2fe7e4519075cf3e57af439722 + +commit 6e52826e2a74d077147a82ead8d4fbd5b54f4e3b +Author: djm@openbsd.org +Date: Wed Sep 6 23:26:37 2023 +0000 + + upstream: allocate the subsystems array as necessary and remove the + + fixed limit of subsystems. Saves a few kb of memory in the server and makes + it more like the other options. + + OpenBSD-Commit-ID: e683dfca6bdcbc3cc339bb6c6517c0c4736a547f + +commit e19069c9fac4c111d6496b19c7f7db43b4f07b4f +Author: djm@openbsd.org +Date: Wed Sep 6 23:23:53 2023 +0000 + + upstream: preserve quoting of Subsystem commands and arguments. + + This may change behaviour of exotic configurations, but the most common + subsystem configuration (sftp-server) is unlikely to be affected. + + OpenBSD-Commit-ID: 8ffa296aeca981de5b0945242ce75aa6dee479bf + +commit 52dfe3c72d98503d8b7c6f64fc7e19d685636c0b +Author: djm@openbsd.org +Date: Wed Sep 6 23:21:36 2023 +0000 + + upstream: downgrade duplicate Subsystem directives from being a + + fatal error to being a debug message to match behaviour with just about all + other directives. + + OpenBSD-Commit-ID: fc90ed2cc0c18d4eb8e33d2c5e98d25f282588ce + +commit 1ee0a16e07b6f0847ff463d7b5221c4bf1876e25 +Author: djm@openbsd.org +Date: Wed Sep 6 23:18:15 2023 +0000 + + upstream: handle cr+lf (instead of just cr) in sshsig signature + + files + + OpenBSD-Commit-ID: 647460a212b916540016d066568816507375fd7f + +commit e1c284d60a928bcdd60bc575c6f9604663502770 +Author: job@openbsd.org +Date: Mon Sep 4 10:29:58 2023 +0000 + + upstream: Generate Ed25519 keys when invoked without arguments + + Ed25519 public keys are very convenient due to their small size. + OpenSSH has supported Ed25519 since version 6.5 (January 2014). + + OK djm@ markus@ sthen@ deraadt@ + + OpenBSD-Commit-ID: f498beaad19c8cdcc357381a60df4a9c69858b3f + +commit 694150ad92765574ff82a18f4e86322bd3231e68 +Author: djm@openbsd.org +Date: Mon Sep 4 00:08:14 2023 +0000 + + upstream: trigger keystroke timing obfucation only if the channels + + layer enqueud some data in the last poll() cycle; this avoids triggering the + obfuscatior for non-channels data like ClientAlive probes and also fixes a + related problem were the obfucations would be triggered on fully quiescent + connections. + + Based on / tested by naddy@ + + OpenBSD-Commit-ID: d98f32dc62d7663ff4660e4556e184032a0db123 + +commit b5fd97896b59a3a46245cf438cc8b16c795d9f74 +Author: djm@openbsd.org +Date: Mon Sep 4 00:04:02 2023 +0000 + + upstream: avoid bogus "obfuscate_keystroke_timing: stopping ..." + + debug messages when keystroke timing obfuscation was never started; spotted + by naddy@ + + OpenBSD-Commit-ID: 5c270d35f7d2974db5c1646e9c64188f9393be31 + +commit ccf7d913db34e49b7a6db1b8331bd402004c840d +Author: djm@openbsd.org +Date: Mon Sep 4 00:01:46 2023 +0000 + + upstream: make channel_output_poll() return a flag indicating + + whether channel data was enqueued. Will be used to improve keystroke timing + obfuscation. Problem spotted by / tested by naddy@ + + OpenBSD-Commit-ID: f9776c7b0065ba7c3bbe50431fd3b629f44314d0 + +commit 43254b326ac6e2131dbd750f9464dc62c14bd5a7 +Author: djm@openbsd.org +Date: Sun Sep 3 23:59:32 2023 +0000 + + upstream: set interactive mode for ControlPersist sessions if they + + originally requested a tty; enables keystroke timing obfuscation for most + ControlPersist sessions. Spotted by naddy@ + + OpenBSD-Commit-ID: 72783a26254202e2f3f41a2818a19956fe49a772 + +commit ff3eda68ceb2e2bb8f48e3faceb96076c3e85c20 +Author: Darren Tucker +Date: Thu Aug 31 23:02:35 2023 +1000 + + Set LLONG_MAX for C89 test. + + If we don't have LLONG_MAX, configure will figure out that it can get it + by setting -std=gnu99, at which point we won't be testing C89 any more. + To avoid this, feed it in via CFLAGS. + +commit f98031773db361424d59e3301aa92aacf423d920 +Author: djm@openbsd.org +Date: Tue Aug 29 02:50:10 2023 +0000 + + upstream: make PerSourceMaxStartups first-match-wins; ok dtucker@ + + OpenBSD-Commit-ID: dac0c24cb709e3c595b8b4f422a0355dc5a3b4e7 + +commit cfa66857db90cd908de131e0041a50ffc17c7df8 +Author: djm@openbsd.org +Date: Mon Aug 28 09:52:09 2023 +0000 + + upstream: descriptive text shouldn't be under .Cm + + OpenBSD-Commit-ID: b1afaeb456a52bc8a58f4f9f8b2f9fa8f6bf651b + +commit 01dbf3d46651b7d6ddf5e45d233839bbfffaeaec +Author: djm@openbsd.org +Date: Mon Aug 28 09:48:11 2023 +0000 + + upstream: limit artificial login delay to a reasonable maximum (5s) + + and don't delay at all for the "none" authentication mechanism. Patch by + Dmitry Belyavskiy in bz3602 with polish/ok dtucker@ + + OpenBSD-Commit-ID: 85b364676dd84cf1de0e98fc2fbdcb1a844ce515 + +commit 528da5b9d7c5da01ed7a73ff21c722e1b5326006 +Author: jmc@openbsd.org +Date: Mon Aug 28 05:32:28 2023 +0000 + + upstream: add spacing for punctuation when macro args; + + OpenBSD-Commit-ID: e80343c16ce0420b2aec98701527cf90371bd0db + +commit 3867361ca691d0956ef7d5fb8181cf554a91d84a +Author: djm@openbsd.org +Date: Mon Aug 28 04:06:52 2023 +0000 + + upstream: explicit long long type in timing calculations (doesn't + + matter, since the range is pre-clamped) + + OpenBSD-Commit-ID: f786ed902d04a5b8ecc581d068fea1a79aa772de + +commit 7603ba71264e7fa938325c37eca993e2fa61272f +Author: djm@openbsd.org +Date: Mon Aug 28 03:31:16 2023 +0000 + + upstream: Add keystroke timing obfuscation to the client. + + This attempts to hide inter-keystroke timings by sending interactive + traffic at fixed intervals (default: every 20ms) when there is only a + small amount of data being sent. It also sends fake "chaff" keystrokes + for a random interval after the last real keystroke. These are + controlled by a new ssh_config ObscureKeystrokeTiming keyword/ + + feedback/ok markus@ + + OpenBSD-Commit-ID: 02231ddd4f442212820976068c34a36e3c1b15be + +commit dce6d80d2ed3cad2c516082682d5f6ca877ef714 +Author: djm@openbsd.org +Date: Mon Aug 28 03:28:43 2023 +0000 + + upstream: Introduce a transport-level ping facility + + This adds a pair of SSH transport protocol messages SSH2_MSG_PING/PONG + to implement a ping capability. These messages use numbers in the "local + extensions" number space and are advertised using a "ping@openssh.com" + ext-info message with a string version number of "0". + + ok markus@ + + OpenBSD-Commit-ID: b6b3c4cb2084c62f85a8dc67cf74954015eb547f + +commit d2d247938b38b928f8a6e1a47a330c5584d3a358 +Author: tobhe@openbsd.org +Date: Mon Aug 21 21:16:18 2023 +0000 + + upstream: Log errors in kex_exchange_identification() with level + + verbose instead of error to reduce preauth log spam. All of those get logged + with a more generic error message by sshpkt_fatal(). + + feedback from sthen@ + ok djm@ + + OpenBSD-Commit-ID: bd47dab4695b134a44c379f0e9a39eed33047809 + +commit 9d7193a8359639801193ad661a59d1ae4dc3d302 +Author: djm@openbsd.org +Date: Mon Aug 21 04:59:54 2023 +0000 + + upstream: correct math for ClientAliveInterval that caused the + + probes to be sent less frequently than configured; from Dawid Majchrzak + + OpenBSD-Commit-ID: 641153e7c05117436ddfc58267aa267ca8b80038 + +commit 3c6ab63b383b0b7630da175941e01de9db32a256 +Author: Darren Tucker +Date: Fri Aug 25 14:48:02 2023 +1000 + + Include Portable version in sshd version string. + + bz#3608, ok djm@ + +commit 17fa6cd10a26e193bb6f65d21264d2fe553bcd87 +Author: Darren Tucker +Date: Mon Aug 21 19:47:58 2023 +1000 + + obsd-arm64 host is real hardware... + + so put in the correct config location. + +commit 598ca75c85acaaacee5ef954251e489cc20d7be9 +Author: Darren Tucker +Date: Mon Aug 21 18:38:36 2023 +1000 + + Add OpenBSD ARM64 test host. + +commit 1acac79bfbe207e8db639e8043524962037c8feb +Author: Darren Tucker +Date: Mon Aug 21 18:05:26 2023 +1000 + + Add test for zlib development branch. + +commit 84efebf352fc700e9040c8065707c63caedd36a3 +Author: djm@openbsd.org +Date: Mon Aug 21 04:36:46 2023 +0000 + + upstream: want stdlib.h for free(3) + + OpenBSD-Commit-ID: 743af3c6e3ce5e6cecd051668f0327a01f44af29 + +commit cb4ed12ffc332d1f72d054ed92655b5f1c38f621 +Author: Darren Tucker +Date: Sat Aug 19 07:39:08 2023 +1000 + + Fix zlib version check for 1.3 and future version. + + bz#3604. + +commit 25b75e21f16bccdaa472ea1889b293c9bd51a87b +Author: Darren Tucker +Date: Mon Aug 14 11:10:08 2023 +1000 + + Add 9.4 branch to CI status page. + +commit 803e22eabd3ba75485eedd8b7b44d6ace79f2052 +Author: djm@openbsd.org +Date: Fri Aug 18 01:37:41 2023 +0000 + + upstream: fix regression in OpenSSH 9.4 (mux.c r1.99) that caused + + multiplexed sessions to ignore SIGINT under some circumstances. Reported by / + feedback naddy@, ok dtucker@ + + OpenBSD-Commit-ID: 4d5c6c894664f50149153fd4764f21f43e7d7e5a + +commit e706bca324a70f68dadfd0ec69edfdd486eed23a +Author: djm@openbsd.org +Date: Wed Aug 16 16:14:11 2023 +0000 + + upstream: defence-in-depth MaxAuthTries check in monitor; ok markus + + OpenBSD-Commit-ID: 65a4225dc708e2dae71315adf93677edace46c21 + +commit d1ab7eb90474df656d5e9935bae6df0bd000d343 +Author: djm@openbsd.org +Date: Mon Aug 14 03:37:00 2023 +0000 + + upstream: add message number of SSH2_MSG_NEWCOMPRESS defined in RFC8308 + + OpenBSD-Commit-ID: 6c984171c96ed67effd7b5092f3d3975d55d6028 + +commit fa8da52934cb7dff6f660a143276bdb28bb9bbe1 +Author: Darren Tucker +Date: Sun Aug 13 15:01:27 2023 +1000 + + Add obsd72 and obsd73 test targets. + +commit f9f18006678d2eac8b0c5a5dddf17ab7c50d1e9f +Author: djm@openbsd.org +Date: Thu Aug 10 23:05:48 2023 +0000 + + upstream: better debug logging of sessions' exit status + + OpenBSD-Commit-ID: 82237567fcd4098797cbdd17efa6ade08e1a36b0 + +commit a8c57bcb077f0cfdffcf9f23866bf73bb93e185c +Author: naddy@openbsd.org +Date: Thu Aug 10 14:37:32 2023 +0000 + + upstream: drop a wayward comma, ok jmc@ + + OpenBSD-Commit-ID: 5c11fbb9592a29b37bbf36f66df50db9d38182c6 + +commit e962f9b318a238db1becc53c2bf79dd3a49095b4 Author: Damien Miller Date: Thu Aug 10 11:10:22 2023 +1000 depend -commit 41bfb63f5101fbacde9d8d2ada863f9ee16df194 +commit 0fcb60bf83130dfa428bc4422b3a3ac20fb528af Author: Damien Miller Date: Thu Aug 10 11:05:42 2023 +1000 update versions in RPM specs -commit e598b92b1eecedac21667edf1fe92078eaf8f2b1 +commit d0cee4298491314f09afa1c4383a66d913150b26 Author: Damien Miller Date: Thu Aug 10 11:05:14 2023 +1000 update version in README -commit e797e5ffa74377c8696e3b0559a258d836479239 +commit 78b4dc6684f4d35943b46b24ee645edfdb9974f5 Author: djm@openbsd.org Date: Thu Aug 10 01:01:07 2023 +0000 upstream: openssh-9.4 OpenBSD-Commit-ID: 71fc1e01a4c4ea061b252bd399cda7be757e6e35 +commit 58ca4f0aa8c4306ac0a629c9a85fb1efaf4ff092 +Author: Darren Tucker +Date: Thu Aug 10 11:30:24 2023 +1000 + + Only include unistd.h once. + commit 3961ed02dc578517a9d2535128cff5c3a5460d28 Author: Damien Miller Date: Thu Aug 10 09:08:49 2023 +1000 wrap poll.h include in HAVE_POLL_H commit e535fbe2af893046c28adfcd787c1fdbae36a24a Author: dtucker@openbsd.org Date: Fri Aug 4 06:32:40 2023 +0000 upstream: Apply ConnectTimeout to multiplexing local socket connections. If the multiplex socket exists but the connection times out, ssh will fall back to a direct connection the same way it would if the socket did not exist at all. ok djm@ OpenBSD-Commit-ID: 2fbe1a36d4a24b98531b2d298a6557c8285dc1b4 commit 9d92e7b24848fcc605945f7c2e3460c7c31832ce Author: Darren Tucker Date: Thu Aug 3 19:35:33 2023 +1000 Fix RNG seeding for OpenSSL w/out self seeding. When sshd is built with an OpenSSL that does not self-seed, it would fail in the preauth privsep process while handling a new connection. Sanity checked by djm@ commit f70010d9b0b3e7e95de8aa0b961e1d74362cfb5d Author: djm@openbsd.org Date: Wed Aug 2 23:04:38 2023 +0000 upstream: CheckHostIP has defaulted to 'no' for a while; make the commented- out config option match. From Ed Maste OpenBSD-Commit-ID: e66e934c45a9077cb1d51fc4f8d3df4505db58d9 commit c88a8788f9865d02b986d00405b9f0be65ad0b5a Author: dtucker@openbsd.org Date: Tue Aug 1 08:15:04 2023 +0000 upstream: remove unnecessary if statement. github PR#422 from eyalasulin999, ok djm@ OpenBSD-Commit-ID: 2b6b0dde4407e039f58f86c8d2ff584a8205ea55 commit 77b8b865cd5a8c79a47605c0c5b2bacf4692c4d5 Author: jmc@openbsd.org Date: Fri Jul 28 05:42:36 2023 +0000 upstream: %C is a callable macro in mdoc(7) so, as we do for %D, escape it; OpenBSD-Commit-ID: 538cfcddbbb59dc3a8739604319491dcb8e0c0c9 commit e0f91aa9c2fbfc951e9ced7e1305455fc614d3f2 Author: djm@openbsd.org Date: Fri Jul 28 05:33:15 2023 +0000 upstream: don't need to start a command here; use ssh -N instead. Fixes failure on cygwin spotted by Darren OpenBSD-Regress-ID: ff678a8cc69160a3b862733d935ec4a383f93cfb commit f446a44f30bc680e0d026a4204844b02646c1c2d Author: djm@openbsd.org Date: Wed May 17 05:52:01 2023 +0000 upstream: add LTESTS_FROM variable to allow skipping of tests up to a specific point. e.g. "make LTESTS_FROM=t-sftp" will only run the sftp.sh test and subsequent ones. ok dtucker@ OpenBSD-Regress-ID: 07f653de731def074b29293db946042706fcead3 commit 8eb8899d612440a9b608bee7f916081d3d0b7812 Author: djm@openbsd.org Date: Fri May 12 06:37:42 2023 +0000 upstream: test ChrootDirectory in Match block OpenBSD-Regress-ID: a6150262f39065939f025e546af2a346ffe674c1 commit e43f43d3f19516222e9a143468ea0dc1b3ab67b6 Author: djm@openbsd.org Date: Fri May 12 06:36:27 2023 +0000 upstream: better error messages OpenBSD-Regress-ID: 55e4186604e80259496d841e690ea2090981bc7a commit 6958f00acf3b9e0b3730f7287e69996bcf3ceda4 Author: djm@openbsd.org Date: Thu Jul 27 22:26:49 2023 +0000 upstream: don't incorrectly truncate logged strings retrieved from PKCS#11 modules; based on GHPR406 by Jakub Jelen; ok markus OpenBSD-Commit-ID: 7ed1082f23a13b38c373008f856fd301d50012f9 commit d1ffde6b55170cd4b9a72bfd9a3f17508e6cf714 Author: djm@openbsd.org Date: Thu Jul 27 22:25:17 2023 +0000 upstream: make sshd_config AuthorizedPrincipalsCommand and AuthorizedKeysCommand accept the %D (routing domain) and a new %C (connection address/port 4-tuple) as expansion sequences; ok markus OpenBSD-Commit-ID: ee9a48bf1a74c4ace71b69de69cfdaa2a7388565 commit 999a2886ca1844a7a74b905e5f2c8c701f9838cd Author: djm@openbsd.org Date: Thu Jul 27 22:23:05 2023 +0000 upstream: increase default KDF work-factor for OpenSSH format private keys from 16 to 24; { feedback ok } x { deraadt markus } OpenBSD-Commit-ID: a3afb1383f8ff0a49613d449f02395d9e8d4a9ec commit 0fa803a1dd1c7b546c166000e23a869cf6c4ec10 Author: Darren Tucker Date: Thu Jul 27 02:25:09 2023 +1000 Prefer OpenSSL's SHA256 in sk-dummy.so Previously sk-dummy.so used libc's (or compat's) SHA256 since it may be built without OpenSSL. In many cases, however, including both libc's and OpenSSL's headers together caused conflicting definitions. We tried working around this (on OpenSSL <1.1 you could define OPENSSL_NO_SHA, NetBSD had USE_LIBC_SHA2, various #define hacks) with varying levels of success. Since OpenSSL >=1.1 removed OPENSSL_NO_SHA and including most OpenSSL headers would bring sha.h in, even if it wasn't used directly this was a constant hassle. Admit defeat and use OpenSSL's SHA256 unless we aren't using OpenSSL at all. ok djm@ commit 36cdb5dbf55c99c0faad06066f56a7c341258c1f Author: Darren Tucker Date: Thu Jul 27 10:29:44 2023 +1000 Retire dfly58 test VM. Add dfly64. commit 2d34205dab08ede9b0676efa57647fc49e6decbe Author: djm@openbsd.org Date: Wed Jul 26 23:06:00 2023 +0000 upstream: make ssh -f (fork after authentication) work properly in multiplexed cases (inc. ControlPersist). bz3589 bz3589 Based on patches by Peter Chubb; ok dtucker@ OpenBSD-Commit-ID: a7a2976a54b93e6767dc846b85647e6ec26969ac commit 076aeda86a7ee9be8fd2f0181ec7b9729a6ceb37 Author: naddy@openbsd.org Date: Sun Jul 23 20:04:45 2023 +0000 upstream: man page typos; ok jmc@ OpenBSD-Commit-ID: e6ddfef94b0eb867ad88abe07cedc8ed581c07f0 commit 135e7d5fe31f700e6dfc61ce914970c5ee7175ba Author: jmc@openbsd.org Date: Thu Jul 20 05:43:39 2023 +0000 upstream: tweak the allow-remote-pkcs11 text; OpenBSD-Commit-ID: bc965460a89edf76865b7279b45cf9cbdebd558a commit 5f83342b61d1f76c141de608ed2bd293990416bd Author: Darren Tucker Date: Tue Jul 25 13:00:22 2023 +1000 Handle a couple more OpenSSL no-ecc cases. ok djm@ commit edc2ef4e418e514c99701451fae4428ec04ce538 Author: Damien Miller Date: Thu Jul 20 12:53:44 2023 +1000 depend commit 51fda734e0d3c2df256fc03e8b060c4305be6e59 Author: Damien Miller Date: Thu Jul 20 12:53:21 2023 +1000 Bring back OPENSSL_HAS_ECC to ssh-pkcs11-client commit 099cdf59ce1e72f55d421c8445bf6321b3004755 Author: djm@openbsd.org Date: Wed Jul 19 14:03:45 2023 +0000 upstream: Separate ssh-pkcs11-helpers for each p11 module Make ssh-pkcs11-client start an independent helper for each provider, providing better isolation between modules and reliability if a single module misbehaves. This also implements reference counting of PKCS#11-hosted keys, allowing ssh-pkcs11-helper subprocesses to be automatically reaped when no remaining keys reference them. This fixes some bugs we have that make PKCS11 keys unusable after they have been deleted, e.g. https://bugzilla.mindrot.org/show_bug.cgi?id=3125 ok markus@ OpenBSD-Commit-ID: 0ce188b14fe271ab0568f4500070d96c5657244e commit 29ef8a04866ca14688d5b7fed7b8b9deab851f77 Author: djm@openbsd.org Date: Wed Jul 19 14:02:27 2023 +0000 upstream: Ensure FIDO/PKCS11 libraries contain expected symbols This checks via nlist(3) that candidate provider libraries contain one of the symbols that we will require prior to dlopen(), which can cause a number of side effects, including execution of constructors. Feedback deraadt; ok markus OpenBSD-Commit-ID: 1508a5fbd74e329e69a55b56c453c292029aefbe commit 1f2731f5d7a8f8a8385c6031667ed29072c0d92a Author: djm@openbsd.org Date: Wed Jul 19 13:56:33 2023 +0000 upstream: Disallow remote addition of FIDO/PKCS11 provider libraries to ssh-agent by default. The old behaviour of allowing remote clients from loading providers can be restored using `ssh-agent -O allow-remote-pkcs11`. Detection of local/remote clients requires a ssh(1) that supports the `session-bind@openssh.com` extension. Forwarding access to a ssh-agent socket using non-OpenSSH tools may circumvent this control. ok markus@ OpenBSD-Commit-ID: 4c2bdf79b214ae7e60cc8c39a45501344fa7bd7c commit 892506b13654301f69f9545f48213fc210e5c5cc Author: djm@openbsd.org Date: Wed Jul 19 13:55:53 2023 +0000 upstream: terminate process if requested to load a PKCS#11 provider that isn't a PKCS#11 provider; from / ok markus@ OpenBSD-Commit-ID: 39532cf18b115881bb4cfaee32084497aadfa05c commit f3f56df8ec476b2de6cbdbdfdb77a2a61087829d Author: Damien Miller Date: Wed Jul 19 12:07:18 2023 +1000 agent_fuzz doesn't want stdint.h conditionalised commit 750911fd31d307a767cc86e3bfa90bbbb77b1a25 Author: Damien Miller Date: Tue Jul 18 15:41:12 2023 +1000 conditionalise stdint.h inclusion on HAVE_STDINT_H fixes build on AIX5 at least commit ff047504fa6e008c4092f8929881816b8993bea0 Author: Damien Miller Date: Tue Jul 18 15:30:45 2023 +1000 conditionalise match localnetwork on ifaddrs.h Fixes build breakage on platforms that lack getifaddrs() commit b87b03282e466ca2927954ce93f5dbf0bfdc68f6 Author: djm@openbsd.org Date: Mon Jul 17 06:16:33 2023 +0000 upstream: missing match localnetwork negation check OpenBSD-Commit-ID: 9a08ed8dae27d3f38cf280f1b28d4e0ff41a737a commit 6d6e185ba29ef4274164b77eab4dc763907f8821 Author: jmc@openbsd.org Date: Mon Jul 17 05:41:53 2023 +0000 upstream: - add -P to usage() - sync the arg name to -J in usage() with that in ssh.1 - reformat usage() to match what "man ssh" does on 80width OpenBSD-Commit-ID: 5235dd7aa42e5bf90ae54579d519f92fc107036e commit f1a9898283a0638667b587ee4a950afd61ab51b0 Author: jmc@openbsd.org Date: Mon Jul 17 05:38:10 2023 +0000 upstream: -P before -p in SYNOPSIS; OpenBSD-Commit-ID: 535f5257c779e26c6a662a038d241b017f8cab7c commit eef4d7e873568e1c84c36bb4034e2c3378250a61 Author: jsg@openbsd.org Date: Mon Jul 17 05:36:14 2023 +0000 upstream: configuation -> configuration OpenBSD-Commit-ID: 4776ced33b780f1db0b2902faec99312f26a726b commit dc1dbe94cf6532bd546a3373ad436404f8850e5f Author: djm@openbsd.org Date: Mon Jul 17 05:26:38 2023 +0000 upstream: move other RCSIDs to before their respective license blocks too no code change OpenBSD-Commit-ID: ef5bf46b57726e4260a63b032b0b5ac3b4fe9cd4 commit ebe11044681caff78834ca6b78311ad19c1860b8 Author: djm@openbsd.org Date: Mon Jul 17 05:22:30 2023 +0000 upstream: Move RCSID to before license block and away from #includes, where it caused merge conflict in -portable for each commit :( OpenBSD-Commit-ID: 756ebac963df3245258b962e88150ebab9d5fc20 commit 05c08e5f628de3ecf6f7ea20947735bcfa3201e0 Author: djm@openbsd.org Date: Mon Jul 17 05:20:15 2023 +0000 upstream: return SSH_ERR_KRL_BAD_MAGIC when a KRL doesn't contain a valid magic number and not SSH_ERR_MESSAGE_INCOMPLETE; the former is needed to fall back to text revocation lists in some cases; fixes t-cert-hostkey. OpenBSD-Commit-ID: 5c670a6c0f027e99b7774ef29f18ba088549c7e1 commit c6fad2c3d19b74f0bd0af1ef040fc74f3a1d9ebb Author: Damien Miller Date: Mon Jul 17 14:56:14 2023 +1000 avoid AF_LINK on platforms that don't define it commit 919bc3d3b712c920de1ae6be5ac6561c98886d7e Author: djm@openbsd.org Date: Mon Jul 17 04:08:31 2023 +0000 upstream: Add support for configuration tags to ssh(1). This adds a ssh_config(5) "Tag" directive and corresponding "Match tag" predicate that may be used to select blocks of configuration similar to the pf.conf(5) keywords of the same name. ok markus OpenBSD-Commit-ID: dc08358e70e702b59ac3e591827e5a96141b06a3 commit 3071d85a47061c1bdaf11a0ac233b501ecba862c Author: djm@openbsd.org Date: Mon Jul 17 04:04:36 2023 +0000 upstream: add a "match localnetwork" predicate. This allows matching on the addresses of available network interfaces and may be used to vary the effective client configuration based on network location (e.g. to use a ProxyJump when not on a particular network). ok markus@ OpenBSD-Commit-ID: cffb6ff9a3803abfc52b5cad0aa190c5e424c139 commit beec17bb311365b75a0a5941418d4b96df7d7888 Author: djm@openbsd.org Date: Mon Jul 17 04:01:10 2023 +0000 upstream: remove vestigal support for KRL signatures When the KRL format was originally defined, it included support for signing of KRL objects. However, the code to sign KRLs and verify KRL signatues was never completed in OpenSSH. Now, some years later, we have SSHSIG support in ssh-keygen that is more general, well tested and actually works. So this removes the semi-finished KRL signing/verification support from OpenSSH and refactors the remaining code to realise the benefit - primarily, we no longer need to perform multiple parsing passes over KRL objects. ok markus@ OpenBSD-Commit-ID: 517437bab3d8180f695c775410c052340e038804 commit 449566f64c21b4578d5c0c431badd0328adc53ed Author: djm@openbsd.org Date: Mon Jul 17 03:57:21 2023 +0000 upstream: Support for KRL extensions. This defines wire formats for optional KRL extensions and implements parsing of the new submessages. No actual extensions are supported at this point. ok markus OpenBSD-Commit-ID: ae2fcde9a22a9ba7f765bd4f36b3f5901d8c3fa7 commit 18ea857770e84825a3a6238bb37f54864487b59f Author: dtucker@openbsd.org Date: Fri Jul 14 07:44:21 2023 +0000 upstream: Include stdint.h for SIZE_MAX. Fixes OPENSSL=no build. OpenBSD-Commit-ID: e7c31034a5434f2ead3579b13a7892960651e6b0 commit 20b768fcd13effe0f2d3619661b6c8592c773553 Author: Darren Tucker Date: Fri Jul 14 17:07:32 2023 +1000 Fix typo in declaration of nmesg. commit 4b94d09542e36ebde2eb9ad89bc68431609932de Author: Damien Miller Date: Fri Jul 14 15:34:47 2023 +1000 portable-specific int overflow defence-in-depth These too are unreachable, but we want the code to be safe regardless of context. Reported by Yair Mizrahi @ JFrog commit 2ee48adb9fc8692e8d6ac679dcc9f35e89ad68f0 Author: djm@openbsd.org Date: Fri Jul 14 05:31:44 2023 +0000 upstream: add defence-in-depth checks for some unreachable integer overflows reported by Yair Mizrahi @ JFrog; feedback/ok millert@ OpenBSD-Commit-ID: 52af085f4e7ef9f9d8423d8c1840a6a88bda90bd commit 4b43bc358ae6f6b19a973679246dc5172f6ac41b Author: djm@openbsd.org Date: Mon Jul 10 04:51:26 2023 +0000 upstream: misplaced debug message OpenBSD-Commit-ID: d0f12af0a5067a756aa707bc39a83fa6f58bf7e5 commit 8c7203bcee4c4f98a22487b4631fe068b992099b Author: Damien Miller Date: Wed Jul 12 11:41:19 2023 +1000 replace deprecate selinux matchpathcon function This function is apparently deprecated. Documentation on what is the supposed replacement is is non-existent, so this follows the approach glibc used https://sourceware.org/git/?p=glibc.git;a=patch;h=f278835f59 ok dtucker@ commit 7e8800f5d701efffa39ccb63ca1e095ea777c31a Author: dtucker@openbsd.org Date: Thu Jul 6 22:17:59 2023 +0000 upstream: minleft and maxsign are u_int so cast appropriately. Prompted by github PR#410, ok deraadt. OpenBSD-Commit-ID: 0514cd51db3ec60239966622a0d3495b15406ddd commit 94842bfe9b09fc93189c6ed0dc9bbebc1d44a426 Author: dlg@openbsd.org Date: Tue Jul 4 03:59:21 2023 +0000 upstream: add support for unix domain sockets to ssh -W ok djm@ dtucker@ OpenBSD-Commit-ID: 3e6d47567b895c7c28855c7bd614e106c987a6d8 commit a95fc5eed09a0238fb127b6c50e8498432b79dae Author: David Seifert Date: Fri May 12 14:06:01 2023 +0200 gss-serv.c: `MAXHOSTNAMELEN` -> `HOST_NAME_MAX` `MAXHOSTNAMELEN` is not defined in POSIX, which breaks on musl: https://pubs.opengroup.org/onlinepubs/9699919799/functions/gethostname.html Bug: https://bugs.gentoo.org/834044 commit 8a6cd08850f576e7527c52a1b086cae82fab290e Author: Darren Tucker Date: Fri Jun 23 09:49:02 2023 +1000 Update runner OS version for hardenedmalloc test. Hardenedmalloc dropped support for "legacy glibc" versions in their 64dad0a69 so use a newer Ubuntu version for the runner for that test. commit cfca6f17e64baed6822bb927ed9f372ce64d9c5b Author: Damien Miller Date: Thu Jun 22 15:04:03 2023 +1000 handle sysconf(SC_OPEN_MAX) returning > INT_MAX; bz3581; ok dtucker commit c1c2ca1365b3f7b626683690bd2c68265f6d8ffd Author: djm@openbsd.org Date: Wed Jun 21 05:10:26 2023 +0000 upstream: better validate CASignatureAlgorithms in ssh_config and sshd_config. Previously this directive would accept certificate algorithm names, but these were unusable in practice as OpenSSH does not support CA chains. part of bz3577; ok dtucker@ OpenBSD-Commit-ID: a992d410c8a78ec982701bc3f91043dbdb359912 commit 4e73cd0f4ab3e5b576c56cac9732da62c8fc0565 Author: djm@openbsd.org Date: Wed Jun 21 05:08:32 2023 +0000 upstream: make `ssh -Q CASignatureAlgorithms` only list signature algorithms that are valid for CA signing. Previous behaviour was to list all signing algorithms, including certificate algorithms (OpenSSH certificates do not support CA chains). part of bz3577; ok dtucker@ OpenBSD-Commit-ID: 99c2b072dbac0f44fd1f2269e3ff6c1b5d7d3e59 commit a69062f1695ac9c3c3dea29d3044c72aaa6af0ea Author: djm@openbsd.org Date: Wed Jun 21 05:06:04 2023 +0000 upstream: handle rlimits > INT_MAX (rlim_t is u64); ok dtucker bz3581 OpenBSD-Commit-ID: 31cf59c041becc0e5ccb0a77106f812c4cd1cd74 commit 8d33f2aa6bb895a7f85a47189913639086347b75 Author: djm@openbsd.org Date: Tue Jun 20 23:59:33 2023 +0000 upstream: prepare for support for connecting to unix domain sockets using ssh -W by explicitly decoding PORT_STREAMLOCAL (a negative number) from the u32 that's passed over the multiplexing socket; previously code would just cast, which is UB. OpenBSD-Commit-ID: e5ac5f40d354096c51e8c118a5c1b2d2b7a31384 commit b4ac435b4e67f8eb5932d8f59eb5b3cf7dc38df0 Author: djm@openbsd.org Date: Tue Jun 20 00:05:09 2023 +0000 upstream: reset comment=NULL for each key in do_fingerprint(); fixes "no comment" not showing on when running `ssh-keygen -l` on multiple keys where one has a comment and other following keys do not. Patch from Markus Kuhn via GHPR407, bz3580 OpenBSD-Commit-ID: 3cce84456fdcd67dc6b84e369f92c6686d111d9b commit b53a809a549dcd4fbde554c6aa283e597b15ea33 Author: millert@openbsd.org Date: Mon Jun 5 13:24:36 2023 +0000 upstream: Store timeouts as int, not u_int as they are limited to INT_MAX. Fixes sign compare warnings systems with 32-bit time_t due to type promotion. OK djm@ OpenBSD-Commit-ID: 48081e9ad35705c5f1705711704a4c2ff94e87b7 commit 2709809fd616a0991dc18e3a58dea10fb383c3f0 Author: Philip Hands Date: Wed May 24 19:41:14 2023 +0200 fixup! if -s & -p specified, mention 'sftp -P' on success SSH-Copy-ID-Upstream: 32686e7c65b4fa2846e474d3315102dfa0f043b0 commit 204e0bf05161b7641500d7ab266c21217412379f Author: Darren Tucker Date: Tue Aug 3 21:25:48 2021 +1000 Make ssh-copy-id(1) consistent with OpenSSH. This makes the ssh-copy-id man page more consistent with the rest of the OpenSSH man pages: - new sentence, new line - no sentences >80 - N.B. -> NB - zap unused .Pp - zap trailing whitespace Report from Debian via mindrot bz#3331, diff from jmc at openbsd.org. SSH-Copy-ID-Upstream: d8974cfb6242316460ed22a1ccc662800a50c5d3 commit 9de79df66d1430d290fab670bb4b18612875e518 Author: Philip Hands Date: Wed May 24 11:45:43 2023 +0200 if -s & -p specified, mention 'sftp -P' on success This was inspired by this: https://github.com/openssh/openssh-portable/pull/321 but I thought that it was better to not do the sed patching. BTW the reason one can get away with using $SSH_OPTS throughout, despite the lowercase -p in there, even if sftp is in use, is that the sftp call is using the already-established ssh master connection, so the port was passed to the earlier ssh. SSH-Copy-ID-Upstream: 1c124d9bfafdbe28a00b683367ebf5750ce12eb2 commit 801cda54c00e0f4e7d89345a90874c8d05dc233a Author: Philip Hands Date: Tue May 23 23:07:11 2023 +0200 drop whitespace SSH-Copy-ID-Upstream: e604fae1cdee35c18055d35dcec530cf12ef00ad commit 288482f53613f3e74544eb92deeb24f7c7f1f371 Author: Philip Hands Date: Tue May 23 20:52:13 2023 +0200 make -x also apply to the target script SSH-Copy-ID-Upstream: 3c4214704f427bd0654adf9b0fc079253db21cf4 commit b79e7b88ed44f0e4339f0ff35c96c78a92175a8d Author: Philip Hands Date: Tue May 23 16:46:42 2023 +0200 add -t option to specify the target path Allow the default target path (.ssh/authorized_files) to be over-riden This was inspired by this MR from Panagiotis Cheilaris https://gitlab.com/phil_hands/ssh-copy-id/-/merge_requests/8 SSH-Copy-ID-Upstream: a942a0e076874adb6d8b2f0fb76d6c7918190fcd commit 914f4ad138714c471ba72fb6d5496b6235320edd Author: Carlos Rodríguez Gili Date: Tue Apr 20 19:23:57 2021 +0200 Fix test error for /bin/sh on Solaris 10 and older On Solaris 10 and older targets /bin/sh is not POSIX-compliant. Test -z `...` fails with error 'sh: test: argument expected'. Using quotes around backticks fixes this and doesn't break POSIX compatibility. SSH-Copy-ID-Upstream: 98394072a3f985b2650c1e8eab2fef84e38cc065 commit bd382dca316c721aed1e45edcf4c4e0f6374afb0 Author: Jakub Jelen Date: Tue Mar 2 21:34:05 2021 +0000 Remove outdated comment The commit b068122 removed the code dropping the trailing colon, but the comment stayed leaving the code confusing for future readers SSH-Copy-ID-Upstream: 930d39f238117cd53810240ec989d0356aa1c1f6 commit bdcaf7939029433635d63aade8f9ac762aca2bbe Author: Darren Tucker Date: Wed May 10 18:50:46 2023 +1000 Special case OpenWrt instead of Dropbear. OpenWrt overrides the location of authorized_keys for root. Currently we assume that all Dropbear installations behave this way, which is not the case. Check for OpenWrt and root user before using that location instead of assuming that for all Dropbear servers. Prompted by Github PR#250. SSH-Copy-ID-Upstream: 0e1f5d443a9967483c33945793107ae3f3e4af2d commit cf84498f67abe93f813a296167b406a0db7b288e Author: Philip Hands Date: Thu May 18 18:20:55 2023 +0200 ssh-copy-id: add -x option (for debugging) This option causes the ssh-copy-id to run with set -x SSH-Copy-ID-Upstream: a0ee367ea8c0a29c8b4515245e408d2d349e7844 commit b4a1efdcb88f03394c08e7f68ed4e11676830002 Author: Philip Hands Date: Thu May 18 17:14:41 2023 +0200 update copyright notices SSH-Copy-ID-Upstream: c284ed33b361814ea48ff68cbd01ca525b2bf117 commit fcd78e31cdd45a7e69ccfe6d8a3b1037dc1de290 Author: djm@openbsd.org Date: Wed May 24 23:01:06 2023 +0000 upstream: fix AuthorizedPrincipalsCommand when AuthorizedKeysCommand appears previously in configuration. Reported by John Meyers in bz3574 ok dtucker@ OpenBSD-Commit-ID: 1c92e4517284386703936e1d3abaa36cfacf1951 commit 5ec5504f1d328d5bfa64280cd617c3efec4f78f3 Author: dtucker@openbsd.org Date: Wed May 10 10:04:20 2023 +0000 upstream: Remove unused prototypes for ssh1 RSA functions. From lengyijun via github PR#396. OpenBSD-Commit-ID: 379a5afa8b7a0f3cba0c8a9bcceb4e5e33a5c1ef commit fbf362b3891ae4b36052d1b39f37fc618b41c476 Author: Darren Tucker Date: Tue May 9 19:26:56 2023 +1000 main(void) to prevent unused variable warning. commit baf854c8bb0a6d0af5c696c801e631a48dabbaba Author: Darren Tucker Date: Tue May 9 19:25:45 2023 +1000 Remove warning pragma since clang doesn't like it. commit 5fbb7a1349fbbb48ccb1b8cafff2c1854370d87d Author: Darren Tucker Date: Tue May 9 17:13:33 2023 +1000 Suppress warning for snprintf truncation test. commit 47742c513e4e045ecc985c6483fc5c8b050acda2 Author: Darren Tucker Date: Tue May 9 17:12:50 2023 +1000 Update OpenSSL compat test for 3.x. commit 86ad25d455a2313126125540e61e0f9314283f88 Author: Darren Tucker Date: Mon May 8 20:23:08 2023 +1000 Add macos13 PAM test target. commit 77cca2c4b13bc6e5f389565583b6202b0d1bccc2 Author: Darren Tucker Date: Mon May 8 20:14:46 2023 +1000 Skip agent-peereid test on macos13. sudo -S nobody doesn't work on the github runners (probably a permission issue) so skip that test. commit b356b8e91678ea295bcf44df5248c3fbf499fdcf Author: Darren Tucker Date: Mon May 8 20:14:28 2023 +1000 Include config.guess in debug output. commit b7afd8a4ecaca8afd3179b55e9db79c0ff210237 Author: Darren Tucker Date: Mon May 8 20:12:59 2023 +1000 Handle OpenSSL >=3 ABI compatibility. Beyond OpenSSL 3.0, the ABI compatibility guarantees are wider (only major must match instead of major and minor in earlier versions). bz#3548, ok djm@ commit 0e9e2663eb2c6e9c3e10d15d70418312ae67e542 Author: dtucker@openbsd.org Date: Mon May 1 08:57:29 2023 +0000 upstream: Import regenerated moduli. OpenBSD-Commit-ID: 3d5f811cfcaed8cc4a97e1db49ac61bdf118113c commit d9687f49682e1e93383fc15ab2018850b2ef38c3 Author: Darren Tucker Date: Mon May 1 11:45:14 2023 +1000 Add macos-13 test target. Also flatten OS list for clarity. commit aacfd6767497b8fa6d41ecdd3f8e265d1e9ef1f6 Author: djm@openbsd.org Date: Sun Apr 30 22:54:22 2023 +0000 upstream: adjust ftruncate() logic to handle servers that reorder requests. sftp/scp will ftruncate the destination file after a transfer completes, to deal with the case where a longer destination file already existed. We tracked the highest contiguous block transferred to deal with this case, but our naive tracking doesn't deal with servers that reorder requests - a misfeature strictly permitted by the protocol but seldom implemented. Adjust the logic to ftruncate() at the highest absolute block received when the transfer is successful. feedback deraadt@ ok markus@ prompted by https://github.com/openssh/openssh-portable/commit/9b733#commitcomment-110679778 OpenBSD-Commit-ID: 4af7fac75958ad8507b4fea58706f3ff0cfddb1b commit c8eb3941758615c8284a48fff47872db926da63c Author: djm@openbsd.org Date: Wed Apr 26 01:36:03 2023 +0000 upstream: Check for ProxyJump=none in CanonicalizeHostname logic. Previously ssh would incorrectly refuse to canonicalise the hostname if ProxyJump was explicitly set to "none" when CanonicalizeHostname=yes bz3567; ok dtucker OpenBSD-Commit-ID: 80a58e43c3a32f97361282f756ec8d3f37989efd commit ac383f3a5c6f529a2e8a5bc44af79a08c7da294e Author: jsg@openbsd.org Date: Wed Apr 12 14:22:04 2023 +0000 upstream: remove duplicate signal.h include OpenBSD-Commit-ID: 30c0a34d74d91ddd0e6992525da70d3293392f70 commit 740dafa20f3f3d325f6f5d44e990b8c8a6d3d816 Author: jsg@openbsd.org Date: Wed Apr 12 08:53:54 2023 +0000 upstream: fix double words ok dtucker@ OpenBSD-Commit-ID: 44d3223902fbce5276422bdc8063ab72a4078489 commit 6452f89577ec4f22440c31b8e19b061d1a7c4b2a Author: Darren Tucker Date: Tue Apr 11 16:49:19 2023 +1000 Test against LibreSSL 3.7.2. commit 2138f6be595ca106fe4805a1e3ab9c4d8acc697b Author: Damien Miller Date: Thu Apr 6 14:33:10 2023 +1000 remove unused upper-case const strings in fmtfp no float format that uses upper-case is supported nor are hex floats. ok dtucker commit 484c5e6168fdb22cbcd73c4ff987cf9ca47989ca Author: djm@openbsd.org Date: Thu Apr 6 03:56:02 2023 +0000 upstream: simplify sshsig_find_principals() similar to what happened to sshsig_check_allowed_keys() in r1.31, removing some dead code OpenBSD-Commit-ID: a493e628d4d6c08f878c276d998f4313ba61702d commit 3a7b110fbc7e096423f8f7b459deffe4c65d70f4 Author: djm@openbsd.org Date: Thu Apr 6 03:21:31 2023 +0000 upstream: remove redundant ssh!=NULL check; we'd already dereferenced it OpenBSD-Commit-ID: 852bf12591ec5a9fb12dcbde9b1fd3945ad0df3c commit 2519110659a1efac6c976895a86659d1b341c91b Author: djm@openbsd.org Date: Thu Apr 6 03:19:32 2023 +0000 upstream: match_user() shouldn't be called with user==NULL unless host and ipaddr are also NULL OpenBSD-Commit-ID: fa3518346c21483e9e01a2e4b9436ae501daf8ea commit 3b9ceaad7ad63c1c03c2a89e148340ad3a62a482 Author: djm@openbsd.org Date: Thu Apr 6 03:12:32 2023 +0000 upstream: don't care about glob() return value here. OpenBSD-Commit-ID: 85bb82fea90478a482e9f65a1bec0aa24227fd66 commit 09d8da0849e2791b2500267cda333cd238f38754 Author: dtucker@openbsd.org Date: Mon Apr 3 08:10:54 2023 +0000 upstream: Move up null check and simplify process_escapes. Based on Coverity CID 291863 which points out we check the channel pointer for NULLness after dereferencing it. Move this to the start of the function, and while there simplify initialization of efc a bit. ok djm@ OpenBSD-Commit-ID: de36e5ad6fde0fe263ca134e986b9095dc59380a commit b36b162be5e6206f12b734222b7bc517c13a6bc8 Author: Damien Miller Date: Fri Mar 31 14:51:20 2023 +1100 need va_end() after va_copy(); ok dtucker spotted by Coverity commit f703757234a5c585553e72bba279b255a272750a Author: dtucker@openbsd.org Date: Fri Mar 31 05:56:36 2023 +0000 upstream: Explicitly ignore return from waitpid here too. OpenBSD-Commit-ID: eef2403df083c61028969fc679ee370373eacacb commit 6b73aa29035991d1448a1a76f63ac152a6bf931c Author: dtucker@openbsd.org Date: Fri Mar 31 04:45:08 2023 +0000 upstream: Explictly ignore return codes where we don't check them. OpenBSD-Commit-ID: 1ffb03038ba1b6b72667be50cf5e5e396b5f2740 commit 6f0308a3e717ebe68eeb3f95253612fab5dbf20e Author: dtucker@openbsd.org Date: Fri Mar 31 04:42:29 2023 +0000 upstream: Return immediately from get_sock_port if sock <0 so we don't call getsockname on a negative FD. From Coverity CID 291840, ok djm@ OpenBSD-Commit-ID: de1c1130646230c2eda559831fc6bfd1b61d9618 commit 1c1124dc901fca1ea2cb762044b8f1a5793a2bed Author: djm@openbsd.org Date: Fri Mar 31 04:23:02 2023 +0000 upstream: don't leak arg2 on parse_pubkey_algos error path; ok dtucker@ OpenBSD-Commit-ID: 7d0270ad3dd102412ca76add2b3760518abdef75 commit 8ba2d4764bb6a4701cd447d8b52604622ffe65f4 Author: djm@openbsd.org Date: Fri Mar 31 04:22:27 2023 +0000 upstream: clamp max number of GSSAPI mechanisms to 2048; ok dtucker OpenBSD-Commit-ID: ce66db603a913d3dd57063e330cb5494d70722c4 commit 1883841fc13d0eada8743cac5d3abe142ee2efa7 Author: djm@openbsd.org Date: Fri Mar 31 04:21:56 2023 +0000 upstream: don't print key if printing hostname failed; with/ok dtucker@ OpenBSD-Commit-ID: ad42971a6ee5a46feab2d79f7f656f8cf4b119f3 commit c6011129cafe4c411f6ef670a4cf271314708eb8 Author: djm@openbsd.org Date: Fri Mar 31 04:04:15 2023 +0000 upstream: remove redundant test OpenBSD-Commit-ID: 6a0b719f9b1ae9d42ad8c5b144c7962c93792f7c commit 4fb29eeafb40a2076c0dbe54e46b687c318f87aa Author: djm@openbsd.org Date: Fri Mar 31 04:00:37 2023 +0000 upstream: don't attempt to decode a ridiculous number of attributes; harmless because of bounds elsewhere, but better to be explicit OpenBSD-Commit-ID: 1a34f4b6896155b80327d15dc7ccf294b538a9f2 commit fc437c154ef724621a4af236de9bc7e51a8381ae Author: djm@openbsd.org Date: Fri Mar 31 03:22:49 2023 +0000 upstream: remove unused variable; prompted by Coverity CID 291879 OpenBSD-Commit-ID: 4c7d20ef776887b0ba1aabcfc1b14690e4ad0a40 commit 0eb8131e4a53b33a8fc9b9ab694e6b6778b87ade Author: dtucker@openbsd.org Date: Fri Mar 31 00:44:29 2023 +0000 upstream: Check fd against >=0 instead of >0 in error path. The dup could in theory return fd 0 although currently it doesn't in practice. From Dmitry Belyavskiy vi github PR#238. OpenBSD-Commit-ID: 4a95f3f7330394dffee5c749d52713cbf3b54846 commit 7174ba6f8a431ca4257767a260fc50e204068242 Author: dtucker@openbsd.org Date: Thu Mar 30 07:19:50 2023 +0000 upstream: Ignore return value from muxclient(). It normally loops without returning, but it if returns on failure we immediately exit. Coverity CID 405050. OpenBSD-Commit-ID: ab3fde6da384ea588226037c38635a6b2e015295 commit a4c1c2513e36f111eeaa1322c510067930e5e51e Author: Damien Miller Date: Fri Mar 31 14:17:22 2023 +1100 don't call connect() on negative socket Coverity CID 405037 commit 34ee842cdd981a759fe8f0d4a37521f9a1c63170 Author: djm@openbsd.org Date: Thu Mar 30 03:05:01 2023 +0000 upstream: return SSH_ERR_KEY_NOT_FOUND if the allowed_signers file is empty, not SSH_ERR_INTERNAL_ERROR. Also remove some dead code spotted by Coverity; with/ok dtucker@ OpenBSD-Commit-ID: 898a1e817cda9869554b1f586a434f67bcc3b650 commit f108e77a9dc9852e72215af1bf27731c48434557 Author: dtucker@openbsd.org Date: Thu Mar 30 00:49:37 2023 +0000 upstream: Remove dead code from inside if block. The only way the if statement can be true is if both dup()s fail, and in that case the tmp2 can never be set. Coverity CID 291805, ok djm@ OpenBSD-Commit-ID: c0d6089b3fb725015462040cd94e23237449f0c8 commit 05b8e88ebe23db690abbfb1a91111abea09cde08 Author: Darren Tucker Date: Thu Mar 30 13:53:29 2023 +1100 child_set_eng: verify both env pointer and count. If child_set env was called with a NULL env pointer and a non-zero count it would end up in a null deref, although we don't currently do this. Prompted by Coverity CID 291850, tweak & ok djm@ commit 28f1b8ef9b84b8cd2f6c9889a0c60aa4a90dadfa Author: dtucker@openbsd.org Date: Wed Mar 29 01:07:48 2023 +0000 upstream: Ignore return from sshpkt_disconnect since we set our own return value for the function. Coverity CID 291797, ok djm@ OpenBSD-Commit-ID: 710b57ba954c139240895e23feea41f203201f04 commit c3da05d95922f5550bcc7815e799474d6a160175 Author: dtucker@openbsd.org Date: Wed Mar 29 00:59:08 2023 +0000 upstream: Plug potential mem leak in process_put. It allocates abs_dst inside a loop but only frees it on exit, so free inside the loop if necessary. Coverity CID 291837, ok djm@ OpenBSD-Commit-ID: a01616503a185519b16f00dde25d34ceaf4ae1a3 commit 13ae327eae598b1043e5ec30e4b170edb3c898a5 Author: djm@openbsd.org Date: Wed Mar 29 00:18:35 2023 +0000 upstream: fix memory leak; Coverity CID 291848 with/ok dtucker@ OpenBSD-Commit-ID: 37f80cb5d075ead5a00ad1b74175684ab1156ff8 commit 9ffa76e1284c85bf459c3dcb8e995733a8967e1b Author: dtucker@openbsd.org Date: Tue Mar 28 07:44:32 2023 +0000 upstream: Plug more mem leaks in sftp by making make_absolute_pwd_glob work in the same way as make_absolute: you pass it a dynamically allocated string and it either returns it, or frees it and allocates a new one. Patch from emaste at freebsd.org and https://reviews.freebsd.org/D37253 ok djm@ OpenBSD-Commit-ID: 85f7404e9d47fd28b222fbc412678f3361d2dffc commit 82b2b8326962b1a98af279bc5bbbbbcab15b3e45 Author: dtucker@openbsd.org Date: Tue Mar 28 06:12:38 2023 +0000 upstream: Remove compat code for OpenSSL < 1.1.* since -portable no longer supports them. OpenBSD-Commit-ID: ea2893783331947cd29a67612b4e56f818f185ff commit b500afcf00ae1b6b73b2ccf171111dfbfeaef74d Author: dtucker@openbsd.org Date: Mon Mar 27 23:56:54 2023 +0000 upstream: Remove compat code for OpenSSL 1.0.* versions now that -portable has dropped support for those versions. OpenBSD-Regress-ID: 82a8eacd87aec28e4aa19f17246ddde9d5ce7fe7 commit 727560e6011efcb36d2f3ac6910444bc775abaa1 Author: Darren Tucker Date: Tue Mar 28 18:06:42 2023 +1100 Prevent conflicts between Solaris SHA2 and OpenSSL. We used to prevent conflicts between native SHA2 headers and OpenSSL's by setting OPENSSL_NO_SHA but that was removed prior to OpenSSL 1.1.0 commit 46db8e14b7f186d32173dcdecd5b785334429b8b Author: Darren Tucker Date: Tue Mar 28 12:44:03 2023 +1100 Remove HEADER_SHA_H from previous... since it causes more problems than it solves. commit 72bd68d37387aa5f81da928f6e82f1c88ed8f674 Author: Darren Tucker Date: Tue Mar 28 10:35:18 2023 +1100 Replace OPENSSL_NO_SHA with HEADER_SHA_H. Since this test doesn't use OpenSSL's SHA2 and may cause conflicts we don't want to include it, but OPENSSL_NO_SHA was removed beginning in OpenSSL's 1.1 series. commit 99668f2e6e0deb833e46cfab56db59ff0fc28c7e Author: Darren Tucker Date: Tue Mar 28 09:50:06 2023 +1100 Configure with --target instead of deprecated form. commit f751d9306c62cd1061f966e6a7483d9bab9c379b Author: Darren Tucker Date: Mon Mar 27 22:05:29 2023 +1100 Pass rpath when building 64bit Solaris. commit a64b935cd450ee8d04c26c9cd728629cf9ca5c91 Author: Darren Tucker Date: Mon Mar 27 19:21:19 2023 +1100 Explicitly disable OpenSSL on AIX test VM. commit 7ebc6f060fc2f70495a56e16d210baae6424cd96 Author: dtucker@openbsd.org Date: Mon Mar 27 03:56:50 2023 +0000 upstream: Add RevokedHostKeys to percent expansion test. OpenBSD-Regress-ID: c077fd12a38005dd53d878c5b944154dec88d2ff commit f1a17de150f8d309d0c52f9abfaebf11c51a8537 Author: dtucker@openbsd.org Date: Mon Mar 27 03:56:11 2023 +0000 upstream: Add tilde and environment variable expansion to RevokedHostKeys. bz#3552, ok djm@ OpenBSD-Commit-ID: ce5d8e0219b63cded594c17d4c2958c06918ec0d commit 009eb4cb48a9708ab9174684dcbcc0f942907abe Author: djm@openbsd.org Date: Mon Mar 27 03:31:05 2023 +0000 upstream: fix test: getnameinfo returns a non-zero value on error, not (neccessarily) -1. From GHPR#384 OpenBSD-Commit-ID: d35e2b71268f66f5543a7ea68751972b3ae22b25 commit 4f0a676486700f10a4788f7e9426e94e39c1c89e Author: djm@openbsd.org Date: Mon Mar 27 03:25:08 2023 +0000 upstream: scp: when copying local->remote, check that source file exists before opening SFTP connection to the server. Based on GHPR#370 ok dtucker, markus OpenBSD-Commit-ID: b4dd68e15bfe22ce4fac9960a1066a2b721e54fb commit 154d8baf631327163571760c2c524bc93c37567c Author: Darren Tucker Date: Mon Mar 27 12:22:30 2023 +1100 Also look for gdb error message from OpenIndiana. commit fbd3811ddb2b6ce2e6dba91fde7352c8978e5412 Author: Darren Tucker Date: Mon Mar 27 11:08:00 2023 +1100 Explicitly disable security key test on aix51 VM. We don't know how to build the shared objects required for the security key tests so skip them. commit 4922ac3be8a996780ef3dc220411da2e27c29d9c Author: Darren Tucker Date: Sun Mar 26 14:49:43 2023 +1100 Split libcrypto and other config flags. This should allow the automatic OpenSSL version selection in the tests to work better. commit 4a948b1469f185e871160a2d70e2a0fce2858f9e Author: Darren Tucker Date: Sun Mar 26 14:39:45 2023 +1100 Specify test target if we build without OpenSSL. When we decide we can't use the versions of OpenSSL available, also restrict the tests we run to avoid the ones that need OpenSSL. commit b308c636f5b5d89eecb98be00b3d56306a005a09 Author: Darren Tucker Date: Sun Mar 26 14:22:53 2023 +1100 Find suitable OpenSSL version. Check the installed OpenSSL versions for a suitable one, and if there isn't (and we don't have a specific version configured) then build without OpenSSL. commit 021ea5c2860f133f44790970968e0e73208b3a87 Author: Damien Miller Date: Fri Mar 24 15:02:52 2023 +1100 Github testing support for BoringSSL commit 9a97cd106466a2a9bda2bfaa4c48c4f1b2cc9c1b Author: Damien Miller Date: Fri Mar 24 15:34:29 2023 +1100 BoringSSL doesn't support EC_POINT_point2bn() so don't invoke it in unittest commit cc5969c033a032d126ff78e5d95cf20abbede4c7 Author: Damien Miller Date: Fri Mar 24 15:34:05 2023 +1100 another ERR_load_CRYPTO_strings() vestige commit 4974293899a068133e976f81d6693670d2b576ca Author: Damien Miller Date: Fri Mar 24 15:24:05 2023 +1100 don't use obsolete ERR_load_CRYPTO_strings() OpenSSL (and elsewhere in OpenSSH) uses ERR_load_crypto_strings() commit 3c527d55f906e6970d17c4cab6db90ae9e013235 Author: Damien Miller Date: Fri Mar 24 15:23:05 2023 +1100 Allow building with BoringSSL commit b7e27cfd7f163fc16b4c5d041cc28ee488a5eeec Author: Damien Miller Date: Fri Mar 24 15:21:18 2023 +1100 put back SSLeay_version compat in configure test Needed to detect old versions and give good "your version is bad" messages at configure time; spotted by dtucker@ commit 7280401bdd77ca54be6867a154cc01e0d72612e0 Author: Damien Miller Date: Fri Mar 24 13:56:25 2023 +1100 remove support for old libcrypto OpenSSH now requires LibreSSL 3.1.0 or greater or OpenSSL 1.1.1 or greater with/ok dtucker@ commit abda22fb48302f2142233f71d27c74040288c518 Author: Darren Tucker Date: Sun Mar 19 15:36:13 2023 +1100 Test latest OpenSSL 1.1, 3.0 and LibreSSL 3.7. commit 610ac1cb077cd5a1ebfc21612154bfa13d2ec825 Author: Darren Tucker Date: Thu Mar 16 21:38:04 2023 +1100 Show 9.3 branch instead of 9.2. commit cb30fbdbee869f1ce11f06aa97e1cb8717a0b645 Author: Damien Miller Date: Thu Mar 16 08:28:19 2023 +1100 depend commit 1dba63eb10c40b6fda9f5012ed6ae87e2d3d028e Author: Damien Miller Date: Thu Mar 16 08:27:54 2023 +1100 crank version commit ba7532d0dac9aaf0ad7270664c43837fc9f64a5f Author: djm@openbsd.org Date: Wed Mar 15 21:19:57 2023 +0000 upstream: openssh-9.3 OpenBSD-Commit-ID: 8011495f2449c1029bb316bd015eab2e00509848 commit 6fd4daafb949b66bf555f3100f715a9ec64c3390 Author: dtucker@openbsd.org Date: Tue Mar 14 07:28:47 2023 +0000 upstream: Free KRL ptr in addition to its contents. From Coverity CID 291841, ok djm@ OpenBSD-Commit-ID: f146ba08b1b43af4e0d7ad8c4dae3748b4fa31b6 commit 1d270bd303afaf6d94e9098cbbf18e5e539e2088 Author: dtucker@openbsd.org Date: Tue Mar 14 07:26:25 2023 +0000 upstream: Check pointer for NULL before deref. None of the existing callers seem to do that, but it's worth checking. From Coverity CID 291834, ok djm@ OpenBSD-Commit-ID: a0a97113f192a7cb1a2c97b932f677f573cda7a4 commit d95af508e78c0cd3dce56b83853baaa59ae295cf Author: dtucker@openbsd.org Date: Sun Mar 12 10:40:39 2023 +0000 upstream: Limit number of entries in SSH2_MSG_EXT_INFO request. This is already constrained by the maximum SSH packet size but this makes it explicit. Prompted by Coverity CID 291868, ok djm@ markus@ OpenBSD-Commit-ID: aea023819aa44a2dcb9dd0fbec10561896fc3a09 commit 8f287ba60d342b3e2f750e7332d2131e3ec7ecd0 Author: dtucker@openbsd.org Date: Sun Mar 12 09:41:18 2023 +0000 upstream: calloc can return NULL but xcalloc can't. From Coverity CID 291881, ok djm@ OpenBSD-Commit-ID: 50204b755f66b2ec7ac3cfe379d07d85ca161d2b commit 83a56a49fd50f4acf900f934279482e4ef329715 Author: dtucker@openbsd.org Date: Fri Mar 10 07:17:08 2023 +0000 upstream: Explicitly ignore return from fcntl (... FD_CLOEXEC) here too. Coverity CID 291853. OpenBSD-Commit-ID: 99d8b3da9d0be1d07ca8dd8e98800a890349e9b5 commit 0fda9d704d3bbf54a5e64ce02a6fecb11fe7f047 Author: Damien Miller Date: Fri Mar 10 15:59:46 2023 +1100 bounds checking for getrrsetbyname() replacement; Spotted by Coverity in CID 405033; ok millert@ commit 89b8df518f21677045599df0ad3e5dd0f39909b5 Author: dtucker@openbsd.org Date: Fri Mar 10 04:06:21 2023 +0000 upstream: Plug mem leak on error path. Coverity CID 405026, ok djm@. OpenBSD-Commit-ID: 8212ca05d01966fb5e72205c592b2257708a2aac commit bf4dae0ad192c3e2f03f7223834b00d88ace3d3e Author: Darren Tucker Date: Fri Mar 10 14:46:57 2023 +1100 Add prototypes for mkstemp replacements. Should prevent warnings due to our wrapper function. commit 4e04d68d6a33cdc73b831fd4b5e6124175555d3d Author: dtucker@openbsd.org Date: Fri Mar 10 03:01:51 2023 +0000 upstream: Expliticly ignore return code from fcntl(.. FD_CLOEXEC) since there's not much we can do anyway. From Coverity CID 291857, ok djm@ OpenBSD-Commit-ID: 051429dd07af8db3fec10d82cdc78d90bb051729 commit d6d38fd77cbe091c59e1bb720c3a494df4990640 Author: djm@openbsd.org Date: Fri Mar 10 02:32:04 2023 +0000 upstream: Like sshd_config, some ssh_config options are not first-match-wins. sshd_config.5 was fixed in r1.348, this is the same for this file OpenBSD-Commit-ID: 7be55b9351cde449b136afcc52d07aa4113b215e commit 7187d3f86bf8f2066cc9941f217d23b0cacae25e Author: dtucker@openbsd.org Date: Fri Mar 10 02:24:56 2023 +0000 upstream: Remove no-op (int) > INT_MAX checks since they can never be true. From Coverity CID 405031, ok djm@ OpenBSD-Commit-ID: 9df3783b181e056595e2bb9edf7ed41d61cf8e84 commit 77adde4305542ebe3005dd456122624fe2347b01 Author: Darren Tucker Date: Fri Mar 10 13:27:29 2023 +1100 Wrap mkstemp calls with umask set/restore. glibc versions 2.06 and earlier did not set a umask on files created by mkstemp created the world-writable. Wrap mkstemp to set and restore the umask. From Coverity (CIDs 291826 291886 291891), ok djm@ commit 633d3dc2a1e9e2a013d019a0576a0771c8423713 Author: jcs@openbsd.org Date: Thu Mar 9 21:06:24 2023 +0000 upstream: modify parentheses in conditionals to make it clearer what is being assigned and what is being checked ok djm dtucker OpenBSD-Commit-ID: 19c10baa46ae559474409f75a5cb3d0eade7a9b8 commit 733030840c4772f858de95d5940ec0c37663e8b0 Author: dtucker@openbsd.org Date: Thu Mar 9 07:11:05 2023 +0000 upstream: Re-split the merge of the reorder-hostkeys test. In the kex_proposal_populate_entries change I merged the the check for reordering hostkeys with the actual reordering, but kex_assemble_names mutates options.hostkeyalgorithms which renders the check ineffective. Put the check back where it was. Spotted and tested by jsg@, ok djm@ OpenBSD-Commit-ID: a7469f25a738db5567395d1881e32479a7ffc9de commit 54ac4ab2b53ce9fcb66b8250dee91c070e4167ed Author: djm@openbsd.org Date: Thu Mar 9 06:58:26 2023 +0000 upstream: include destination constraints for smartcard keys too. Spotted by Luci Stanescu; ok deraadt@ markus@ OpenBSD-Commit-ID: add879fac6903a1cb1d1e42c4309e5359c3d870f commit bfd1ad01d974a316b60622759ad17537fa2d92b4 Author: Darren Tucker Date: Thu Mar 9 18:24:54 2023 +1100 Limit the number of PAM environment variables. xcalloc has its own limits, but these are specific to PAM. From Coverity CID 405198, ok djm@ commit a231414970e01a35f45a295d5f93698fa1249b28 Author: Darren Tucker Date: Thu Mar 9 18:19:44 2023 +1100 Limit the number of PAM environment variables. From Coverity CID 405194, tweaks and ok djm@ commit 36c6c3eff5e4a669ff414b9daf85f919666e8e03 Author: dtucker@openbsd.org Date: Wed Mar 8 06:21:32 2023 +0000 upstream: Plug mem leak. Coverity CID 405196, ok djm@ OpenBSD-Commit-ID: 175f09349387c292f626da68f65f334faaa085f2 commit dfb9b736e1ccf9e6b03eea21cd961f4fd0634c98 Author: tb@openbsd.org Date: Wed Mar 8 05:33:53 2023 +0000 upstream: ssh-pkcs11: synchronize error messages with errors A handful of error messages contained incorrect function names or otherwise inaccurate descriptions. Fix them to match reality. input/ok djm OpenBSD-Commit-ID: 165a15db52f75b31e1804b043480c36af09f3411 commit 51875897b81b5c21b80c256a29597916edbde454 Author: guenther@openbsd.org Date: Wed Mar 8 04:43:12 2023 +0000 upstream: Delete obsolete /* ARGSUSED */ lint comments. ok miod@ millert@ OpenBSD-Commit-ID: 7be168a570264d59e96a7d2d22e927d45fee0e4c commit a76085bda883c2104afb33ab0334eca190927362 Author: Darren Tucker Date: Wed Mar 8 17:25:37 2023 +1100 Extra brackets to prevent warning. commit 147ae57d4dfa0508109f93b78a7d8b92819e1f83 Author: djm@openbsd.org Date: Wed Mar 8 00:05:58 2023 +0000 upstream: use RSA/SHA256 when testing usability of private key in agent; with/ok dtucker OpenBSD-Commit-ID: fe1382e2fdf23fcae631308e72342bad56066a56 commit 27fd251bc906a763e70ce0f27c8abdf8bbd1e416 Author: djm@openbsd.org Date: Wed Mar 8 00:05:37 2023 +0000 upstream: use RSA/SHA256 when testing usability of private key; based on fix in bz3546 by Dmitry Belyavskiy; with/ok dtucker OpenBSD-Commit-ID: 0ef414cc363a832f9fab92a5da0234448bce2eba commit eee9f3fc3d52ae7d2106929bb06b7f291fb0b81a Author: djm@openbsd.org Date: Tue Mar 7 21:47:42 2023 +0000 upstream: refactor to be more readable top to bottom. Prompted by Coverity CID 405048 which was a false-positive fd leak; ok dtucker@ OpenBSD-Commit-ID: fc55ec2af622a017defb9b768bf26faefc792c00 commit 42a06b29a4c99272bf690f9b3be520b08b448dc5 Author: Darren Tucker Date: Tue Mar 7 18:34:41 2023 +1100 Add header changes missed in previous. commit 4710077096edff2e6926dd5b15bf586491d317db Author: dtucker@openbsd.org Date: Tue Mar 7 06:09:14 2023 +0000 upstream: Fix mem leak in environment setup. From jjelen at redhat.com via bz#2687, ok djm@ OpenBSD-Commit-ID: 9f9e4ba3cac003e6f81da3bcebd1b9ec43e7f353 commit 03acc50d0ccb78fc91d1570de1cd0fdfea646028 Author: dtucker@openbsd.org Date: Mon Mar 6 12:15:47 2023 +0000 upstream: Unit test for kex_proposal_populate_entries. OpenBSD-Regress-ID: bdb211d80d572a08bf14b49fe2a58b9ff265c006 commit 3f9231c2e1f374ebb08016ba00ea97b47c0ed20b Author: djm@openbsd.org Date: Tue Mar 7 05:37:26 2023 +0000 upstream: fix memory leak in process_read() path; Spotted by James Robinson in GHPR363; ok markus@ OpenBSD-Commit-ID: cdc2d98e6478b7e7f3a36976845adae3820429d8 commit c5e6e890839ec520ab9301a92cba56303749dea2 Author: djm@openbsd.org Date: Tue Mar 7 01:30:52 2023 +0000 upstream: correct size for array argument when changing UMAC_OUTPUT_LEN Coverity CID 291845; ok dtucker@ OpenBSD-Commit-ID: 2eb017d10705bb623d4418691f961c930eafaec0 commit 9641753e0fd146204d57b2a4165f552a81afade4 Author: dtucker@openbsd.org Date: Mon Mar 6 12:14:48 2023 +0000 upstream: Refactor creation of KEX proposal. This adds kex_proposal_populate_entries (and corresponding free) which populates the KEX proposal array with dynamically allocated strings. This replaces the previous mix of static and dynamic that has been the source of previous leaks and bugs. Remove unused compat functions. With & ok djm@. OpenBSD-Commit-ID: f2f99da4aae2233cb18bf9c749320c5e040a9c7b commit aa59d6a489fb20973fa461d0fdb1110db412947b Author: dtucker@openbsd.org Date: Sun Mar 5 09:24:35 2023 +0000 upstream: Fix mem and FILE leaks in moduli screening. If multiple -Ocheckpoint= options are passed, the earlier ones would be overwritten and leaked. If we use an input file that wasn't stdin, close that. From Coverity CIDs 291884 and 291894. OpenBSD-Commit-ID: a4d9d15f572926f841788912e2b282485ad09e8b commit 23b8cb41767af99a1aac24589d1882d9c8c2c205 Author: dtucker@openbsd.org Date: Sun Mar 5 08:18:58 2023 +0000 upstream: Plug mem leak in moduli checkpoint option parsing. From Coverity CID 291894. OpenBSD-Commit-ID: 9b1aba2d049741ae21c8dc4560a7e29ab17310f4 commit fc7f8f2188d4a4fc8ba77eddbe863c7665666db5 Author: dtucker@openbsd.org Date: Sun Mar 5 05:34:09 2023 +0000 upstream: Remove unused compat.h includes. We've previously removed a lot of the really old compatibility code, and with it went the need to include compat.h in most of the files that have it. OpenBSD-Commit-ID: 5af8baa194be00a3092d17598e88a5b29f7ea2b4 commit 6c165c36246d8004c20e1df5cec4961a5ac422d6 Author: dtucker@openbsd.org Date: Sat Mar 4 03:22:59 2023 +0000 upstream: Use time_t for x11 timeout. Use time_t instead of u_int for remaining x11 timeout checks for 64bit time_t safety. From Coverity CIDs 405197 and 405028, ok djm@ OpenBSD-Commit-ID: 356685bfa1fc3d81bd95722d3fc47101cc1a4972 commit 4a3918f51bd2d968387e7aa87e33b32c78077fb4 Author: dtucker@openbsd.org Date: Fri Mar 3 10:23:42 2023 +0000 upstream: Ensure ms_remain is always initialized similar to what we do in ssh_packet_write_wait. bz#2687, from jjelen at redhat.com. OpenBSD-Commit-ID: a50e0541cf823f8d1c72f71ccde925d3dbe6dfac commit e44846a4487d2885ac7f2610be09b1e2bf52249b Author: dtucker@openbsd.org Date: Fri Mar 3 09:48:51 2023 +0000 upstream: Check for non-NULL before string comparison. From jjelen at redhat.com via bz#2687. OpenBSD-Commit-ID: 0d9b2e0cac88a311b5766b1aef737082583c285f commit 1842d523fae63b862ce8e60725c9b606cddb86a6 Author: djm@openbsd.org Date: Fri Mar 3 05:00:34 2023 +0000 upstream: guard against getsockname(-1, ...) from Coverity CID 291832 OpenBSD-Commit-ID: e58d5227327917d189229b7f0b37d2780f360d5f commit 78571a5fe9847d40d7f220c92b707574ae9ec4ce Author: djm@openbsd.org Date: Fri Mar 3 04:36:20 2023 +0000 upstream: some options are not first-match-wins. Mention that there are exceptions at the start of the manpage and label some of them in the option description. OpenBSD-Commit-ID: 3b74728446fa6fc8742769eeb8c3674e233e84c4 commit d1c1b3272e8895a96c4f5889bd6e07a8525bd9f1 Author: djm@openbsd.org Date: Fri Mar 3 04:34:49 2023 +0000 upstream: actually print "channeltimeout none" in config dump mode; spotted via Coverity CID 405022 OpenBSD-Commit-ID: b074b52bf138b75f08264e8da15880b29c7a630f commit 8bf61e95610b48192d4e1720cc15d9004617301d Author: Darren Tucker Date: Fri Mar 3 14:50:03 2023 +1100 Add Coverity badges. commit 93291bd723959adf462b1df958106cf07a7734dd Author: dtucker@openbsd.org Date: Fri Mar 3 03:12:24 2023 +0000 upstream: Check return values of dup2. Spotted by Coverity, ok djm@ OpenBSD-Commit-ID: 19fb1b53072826d00c67df677731d2f6c1dd602b commit e37261dff33af23f37202cfce0848d36f5c1055c Author: dtucker@openbsd.org Date: Fri Mar 3 02:37:58 2023 +0000 upstream: Use time_t for x11_refuse_time timeout. We need SSH_TIME_T_MAX for this, so move from misc.c to misc.h so it's available. Fixes a Coverity warning for 64bit time_t safety, ok djm@ OpenBSD-Commit-ID: c69c4c3152cdaab953706db4ccf4d5fd682f7d8d commit 32755a98c29114b13f4c9d47454bbb265b932ad7 Author: dtucker@openbsd.org Date: Fri Mar 3 02:34:29 2023 +0000 upstream: Check return value from fctnl and warn on failure. Spotted by Coverity, ok djm@ OpenBSD-Commit-ID: 2097c7db3cf657f1e3a6c5077041bacc63143cab commit 5fc60e8246c36b8255f72a937ebe9787b39648c6 Author: dtucker@openbsd.org Date: Thu Mar 2 11:10:27 2023 +0000 upstream: Remove SUDO in proxy command wrapper. Anything that needs sudo is already run by it, and it breaks if root isn't in sudoers. OpenBSD-Regress-ID: 6cf22fda32a89c16915f31a6ed9bbdbef2a3bac9 commit 0d514659b23a257247491179cfbb53a6dd64e164 Author: dtucker@openbsd.org Date: Thu Mar 2 08:24:41 2023 +0000 upstream: Fix breakage on dhgex test. This was due to the sshd logs being written to the wrong log file. While there, make save_debug_logs less verbose, write the name of the tarball to regress.log and use $SUDO to remove the old symlinks (which shouldn't be needed, but won't hurt). Initial problem spotted by anton@. OpenBSD-Regress-ID: 9c44fb9cd418e6ff31165e7a6c1f9f11a6d19f5b commit 860201201d4ae655702807966901682cff30a171 Author: dtucker@openbsd.org Date: Thu Mar 2 08:14:52 2023 +0000 upstream: Quote grep and log message better. OpenBSD-Regress-ID: 3823d9063127169736aa274b1784cb28e15b64d4 commit 03a03c6002525f5ad9c8fc874a5d5826a35d9858 Author: dtucker@openbsd.org Date: Thu Mar 2 06:41:56 2023 +0000 upstream: Always call fclose on checkpoints. In the case of an fprintf failure we would not call fclose which would leak the FILE pointer. While we're there, try to clean up the temp file on failure. Spotted by Coverity, ok djm@ OpenBSD-Commit-ID: 73c7ccc5d4fcc235f54c6b20767a2815408525ef commit 13fe8f9785e6d90400ce548939a0b0ddc11fcb3c Author: dtucker@openbsd.org Date: Wed Mar 1 21:54:50 2023 +0000 upstream: Remove old log symlinks before creating new ones. In -portable some platforms don't like overwriting existing symlinks. OpenBSD-Regress-ID: 7e7ddc0beb73e945e1c4c58d51c8a125b518120f commit 131fcbcaffd1e3bcf5ab766ec497b5d768955310 Author: Darren Tucker Date: Wed Mar 1 23:23:02 2023 +1100 Adjust test jobs for new log directory. commit a6f4ac8a2baf77e5361cfa017d0dc250d1409bec Author: dtucker@openbsd.org Date: Wed Mar 1 09:29:32 2023 +0000 upstream: Rework logging for the regression tests. Previously we would log to ssh.log and sshd.log, but that is insufficient for tests that have more than one concurent ssh/sshd. Instead, we'll log to separate datestamped files in a $OBJ/log/ and leave a symlink at the previous location pointing at the most recent instance with an entry in regress.log showing which files were created at each point. This should be sufficient to reconstruct what happened even for tests that use multiple instances of each program. If the test fails, tar up all of the logs for later analysis. This will let us also capture the output from some of the other tools which was previously sent to /dev/null although most of those will be in future commits. OpenBSD-Regress-ID: f802aa9e7fa51d1a01225c05fb0412d015c33e24 commit 8ead62ed5e86c7df597d8604f332f49cd1527b85 Author: dtucker@openbsd.org Date: Tue Feb 28 21:31:50 2023 +0000 upstream: fatal out if allocating banner string fails to avoid potential null deref later in sscanf. Spotted by Coverity, ok deraadt@ OpenBSD-Commit-ID: 74e8d228ac00552e96e9e968dfcccf8dd1f46ad5 commit 44ca56ba0b3f531f1d85730cc701097cd49e6868 Author: dtucker@openbsd.org Date: Tue Feb 28 08:45:24 2023 +0000 upstream: Explicitly ignore return from fchmod similar to other calls to prevent warning. OpenBSD-Commit-ID: fdc5287dcee0860b5a493186414226c655b0eb0a commit 803392933a3a6f09f834aa5f0c2aab06a3b382f4 Author: dtucker@openbsd.org Date: Mon Feb 27 22:12:40 2023 +0000 upstream: Plug mem leak on globbed ls error path. Spotted by Coverity, ok deraadt@ OpenBSD-Commit-ID: de28476025db29820a9a2e56e98b964d8a02861c commit aa33b4d396abf47a2a45f982f28d054fb1dcb5c3 Author: Darren Tucker Date: Mon Feb 27 21:04:22 2023 +1100 Cast time_t's in debug output to long long. Should fix Coverity warning about truncation of 64bit time_t. commit b0fd60a9de62a03189ad57d0c07f0ac51dc00e95 Author: Darren Tucker Date: Mon Feb 27 17:28:59 2023 +1100 Do shadow expiry calcs using "long long". Coverity flags these as potentially not 64bit time_t safe so use long long for the calculations and debug output. ok djm@ commit 01dbeb3084d714bbd001ff9d03b9de542e8cdf58 Author: Damien Miller Date: Mon Feb 27 17:07:52 2023 +1100 avoid clash between for getopt's struct option Since we don't use getopt_long() nothing outside the getopt() implementation itself uses this structure, so move it into the source to remove it from visibility and clashes with libc's ok dtucker@ commit eb88d07c43afe407094e7d609248d85a15e148ef Author: Darren Tucker Date: Sat Feb 25 14:45:41 2023 +1100 Revert explicit chmods on private keys. This should no longer be needed on Cygwin test runners due to previous commit. commit 52b75db61030a6c8baf66b73644380cf3f58e26a Author: Darren Tucker Date: Sat Feb 25 14:43:28 2023 +1100 Remove extended ACLs from working dirs. This should allow umask to work as expected and prevent tests from failing due to excessive permissions on private keys. commit 0c5d4c843df5605b043a758d69f9a611ef63c479 Author: Darren Tucker Date: Fri Feb 24 13:44:13 2023 +1100 Explicitly set permissions on user and host keys. On cygwin, the umask might not be sufficient. Should fix tests on Github runners. commit 6c9fc9d7a9f7abf82c3294d74e6d4a25735862ce Author: djm@openbsd.org Date: Wed Feb 22 03:56:43 2023 +0000 upstream: fix progressmeter corruption on wide displays; bz3534 feedback/ok dtucker@ OpenBSD-Commit-ID: f4affee067cec7c182f3e0b307d758e0472762a3 commit fe0bd3cde9665d364e5eedd2c2c2e60d4cdc3786 Author: dtucker@openbsd.org Date: Tue Feb 21 06:48:18 2023 +0000 upstream: fseek to end of known_hosts before writing to it. POSIX and ANSI C require that applications call fseek or similar between read and writing to a RW file. OpenBSD doesn't enforce this, but some (System V derived) platforms need this to prevent it from writing a spurious extra byte (in this case, a newline). ok djm@ deraadt@ OpenBSD-Commit-ID: 33e680dcd8110582a93a40a8491024e961f45137 commit 357fb8ae14c07cd025eeed66e73de91bab569849 Author: Darren Tucker Date: Tue Feb 21 17:51:09 2023 +1100 Also run unit tests on AIX VMs. In the past these tests took too long, but these days it only adds about 5 min to the run. commit 17781aaa5188ee1477f7779b280d105512e3dbed Author: Darren Tucker Date: Tue Feb 21 17:38:55 2023 +1100 Wrap stdint.h inside ifdef. commit ef798bad38505f7bf1b5fa5c0843dfc5a2b192b9 Author: Mayank Sharma Date: Mon Feb 20 17:37:15 2023 +0530 Add includes to ptimeout test. Fixes test failures on AIX due to type mismatches. commit ab69dda05d5268454209f529fa80f477e60d846a Author: Darren Tucker Date: Mon Feb 20 18:24:39 2023 +1100 Always use the openssl binary configure tells us. This fixes tests on platforms that do not have the openssl tool installed at all. commit 2a7e3449908571af601a4c2d12ab140096442e47 Author: dtucker@openbsd.org Date: Fri Feb 17 04:22:50 2023 +0000 upstream: Remove now-unused compat bit SSH_BUG_RSASIGMD5. The code to set this was removed in OpenSSH 7.7 when support for SSH implementations dating back to before RFC standardization were removed. "burn it all" djm@ OpenBSD-Commit-ID: 6330935fbe23dd00be79891505e06d1ffdac7cda commit 0833ccf2c8b7ae08b296c06f17bd53e3ab94b0b0 Author: dtucker@openbsd.org Date: Fri Feb 17 03:06:18 2023 +0000 upstream: Remove now-unused compat bit SSH_BUG_BIGENDIANAES. This was previously set for OpenSSH 2.3 (released in 2000) but this check was removed in OpenSSH 7.7 (2018). ok djm@ deraadt@ OpenBSD-Commit-ID: 326426ea328707fc9e83305291ab135c87f678af commit c81c2bea6e828d52b62b448b4ffdd3c163177975 Author: Damien Miller Date: Fri Feb 17 10:12:40 2023 +1100 whitespace fixes commit 500f90b39db5f0014e6b0c49ff1f45c994b69293 Author: Damien Miller Date: Fri Feb 17 10:02:08 2023 +1100 whitespace at EOL commit 68350152406339170721c15e97afdf827a5e4001 Author: dtucker@openbsd.org Date: Thu Feb 16 10:10:00 2023 +0000 upstream: Remove SSH_BUG_PASSWORDPAD compat bit since it's no longer used. ok markus@ OpenBSD-Commit-ID: b92c21f56fe4b7f9a54790d6a9650725c226820b commit 537cccd804eaf65f32bdce037cc31db4e0ab0f44 Author: dtucker@openbsd.org Date: Thu Feb 16 07:55:15 2023 +0000 upstream: Remove SSH_BUG_IGNOREMSG compat flag since it's only applicable to SSH1 and thus no longer used. ok markus@ "kill it with fire" djm@ OpenBSD-Commit-ID: ea13318b1937795d9db4790d3ce0a6ed01584dab commit 285cf6cd4b91a0a0ce33193c358c99085af33e43 Author: jmc@openbsd.org Date: Fri Feb 10 06:41:53 2023 +0000 upstream: space between macro and punctuation; sort usage(); OpenBSD-Commit-ID: 6141610cfca037700730e41f868d1d9124958f8c commit d39a96f70f81878c77336ed35f5c648c1804b71a Author: jmc@openbsd.org Date: Fri Feb 10 06:40:48 2023 +0000 upstream: space between macro and punctuation; OpenBSD-Commit-ID: abc95e550be9e6d9a7ff64b65c104c7be21ab19e commit 16e82bf53fc34e43e3b948d43b68d5b27a7335e6 Author: jmc@openbsd.org Date: Fri Feb 10 06:39:27 2023 +0000 upstream: sort SYNOPSIS; OpenBSD-Commit-ID: dacd9da33277d5669a51213d880632599c890c1e commit d9685121ff6d57b8797411f3cb123884a4b96e30 Author: Darren Tucker Date: Sat Feb 11 12:32:19 2023 +1100 Improve seccomp compat on older systems. Check if flags to mmap and madvise are defined before using them. Should fix problems building on older Linux systems that don't have these. bz#3537, with & ok djm@. commit 6180b0fa4f7996687678702806257e661fd5931e Author: djm@openbsd.org Date: Fri Feb 10 05:06:03 2023 +0000 upstream: test -Ohashalg=... and that the default output contains both specified hash algorithms; prompted by dtucker@ OpenBSD-Regress-ID: 26f309208c8d8b8fa9c5f419767b85f1e9b22f51 commit d651f5c9fe37e61491eee46c49ba9fa03dbc0e6a Author: djm@openbsd.org Date: Fri Feb 10 04:56:30 2023 +0000 upstream: let ssh-keygen and ssh-keyscan accept -Ohashalg=sha1|sha256 when outputting SSHFP fingerprints to allow algorithm selection. bz3493 ok dtucker@ OpenBSD-Commit-ID: e6e07fe21318a873bd877f333e189eb963a11b3d commit 18938d11a90b74d63c20b2d3c965d5bd64786ab1 Author: djm@openbsd.org Date: Fri Feb 10 04:47:19 2023 +0000 upstream: add a `sshd -G` option that parses and prints the effective configuration without attempting to load private keys and perform other checks. This allows usage of the option before keys have been generated. bz3460 feedback/ok dtucker@ OpenBSD-Commit-ID: 774504f629023fc25a559ab1d95401adb3a7fb29 commit df7d3dbf7194db8e97730ee0425d4d9d7bdb8b10 Author: djm@openbsd.org Date: Fri Feb 10 04:40:28 2023 +0000 upstream: make `ssh -Q CASignatureAlgorithms` work as the manpage says it should bz3532 OpenBSD-Commit-ID: 0ddb17b3fcbd99bfb5baea4ac5e449620cbd3adc commit d3b8d4198b6595f23b5859d43dc8fc701f97429b Author: Darren Tucker Date: Fri Feb 10 14:26:44 2023 +1100 Add CentOS 7 test targets. commit 22efb01e355bba4755b730ed417f91c081445bfc Author: dtucker@openbsd.org Date: Thu Feb 9 09:55:33 2023 +0000 upstream: Test adding terminating newline to known_hosts. OpenBSD-Regress-ID: 5fc3010ac450195b3fbdeb68e875564968800365 commit caec6da1a583ed8c32c6ad3b81bbcaab46ac8b61 Author: dtucker@openbsd.org Date: Wed Feb 8 08:06:03 2023 +0000 upstream: ssh-agent doesn't actually take -v, so the recently-added ones will result in the test not cleaning up after itself. Patch from cjwatson at debian.org vi bz#3536. OpenBSD-Regress-ID: 1fc8283568f5bf2f918517c2c1e778072cf61b1a commit 3c379c9a849a635cc7f05cbe49fe473ccf469ef9 Author: dtucker@openbsd.org Date: Thu Feb 9 09:54:11 2023 +0000 upstream: Ensure that there is a terminating newline when adding a new entry to known_hosts. bz#3529, with git+openssh at limpsquid.nl, ok deraadt@ markus@ OpenBSD-Commit-ID: fa8d90698da1886570512b96f051e266eac105e0 commit 95b6bbd2553547260b324b39d602061c88b774bc Author: Darren Tucker Date: Tue Feb 7 08:43:47 2023 +1100 Replace 9.1 with 9.2 on CI status page. commit 195313dfe10a23c82e9d56d5fdd2f59beee1bdcf Author: Damien Miller Date: Fri Feb 3 16:33:09 2023 +1100 harden Linux seccomp sandbox Linux mmap(2) and madvise(2) syscalls support quite a number of funky flags that we don't expect that sshd/libc will ever need. We can exclude this kernel attack surface by filtering the mmap(2) flags and the madvise(2) advice arguments. Similarly, the sandboxed process in sshd is a single-threaded program that does not use shared memory for synchronisation or communication. Therefore, there should be no reason for the advanced priority inheritance futex(2) operations to be necessary. These can also be excluded. Motivated by Jann Horn pointing out that there have been kernel bugs in nearby Linux kernel code, e.g. CVE-2020-29368, CVE-2020-29374 and CVE-2022-42703. Feedback Jann Horn, ok dtucker@ commit 6dfb65de949cdd0a5d198edee9a118f265924f33 Author: Damien Miller Date: Thu Feb 2 23:21:54 2023 +1100 crank versions in RPM specs commit d07cfb11a0ca574eb68a3931d8c46fbe862a2021 Author: Damien Miller Date: Thu Feb 2 23:21:45 2023 +1100 update version in README commit 9fe207565b4ab0fe5d1ac5bb85e39188d96fb214 Author: Damien Miller Date: Thu Feb 2 23:17:49 2023 +1100 adapt compat_kex_proposal() test to portable commit 903c556b938fff2d7bff8da2cc460254430963c5 Author: djm@openbsd.org Date: Thu Feb 2 12:12:52 2023 +0000 upstream: test compat_kex_proposal(); by dtucker@ OpenBSD-Regress-ID: 0e404ee264db546f9fdbf53390689ab5f8d38bf2 commit 405fba71962dec8409c0c962408e09049e5624b5 Author: dtucker@openbsd.org Date: Thu Jan 19 07:53:45 2023 +0000 upstream: Check if we can copy sshd or need to use sudo to do so during reexec test. Skip test if neither can work. Patch from anton@, tweaks from me. OpenBSD-Regress-ID: 731b96ae74d02d5744e1f1a8e51d09877ffd9b6d commit b2a2a8f69fd7737ea17dc044353c514f2f962f35 Author: djm@openbsd.org Date: Thu Feb 2 12:10:22 2023 +0000 upstream: openssh-9.2 OpenBSD-Commit-ID: f7389f32413c74d6e2055f05cf65e7082de03923 commit 12da7823336434a403f25c7cc0c2c6aed0737a35 Author: djm@openbsd.org Date: Thu Feb 2 12:10:05 2023 +0000 upstream: fix double-free caused by compat_kex_proposal(); bz3522 by dtucker@, ok me OpenBSD-Commit-ID: 2bfc37cd2d41f67dad64c17a64cf2cd3806a5c80 commit 79efd95ab5ff99f4cb3a955e2d713b3f54fb807e Author: Darren Tucker Date: Wed Feb 1 17:17:26 2023 +1100 Skip connection-timeout test on minix3. Minix 3's Unix domain sockets don't seem to work the way we expect, so skip connection-timeout test on that platform. While there, group together all similarly skipped tests and explicitly comment. commit 6b508c4e039619842bcf5a16f8a6b08dd6bec44a Author: Damien Miller Date: Wed Feb 1 12:12:05 2023 +1100 fix libfido2 detection without pkg-config Place libfido2 before additional libraries (that it may depend upon) and not after. bz3530 from James Zhang; ok dtucker@ commit 358e300fed5e6def233a2c06326e51e20ebed621 Author: deraadt@openbsd.org Date: Wed Jan 18 20:56:36 2023 +0000 upstream: delete useless dependency OpenBSD-Commit-ID: e1dc11143f83082e3154d6094f9136d0dc2637ad commit a4cb9be1b021b511e281ee55c356f964487d9e82 Author: deraadt@openbsd.org Date: Wed Jan 18 20:43:15 2023 +0000 upstream: Create and install sshd random relink kit. ../Makefile.inc and Makfile are concatenated for reuse, which hopefully won't be too fragile, we'll see if we need a different approach. The resulting sshd binary is tested with the new sshd -V option before installation. As the binary layout is now semi-unknown (meaning relative, fixed, and gadget offsets are not precisely known), change the filesystem permissions to 511 to prevent what I call "logged in BROP". I have ideas for improving this further but this is a first step ok djm OpenBSD-Commit-ID: 1e0a2692b7e20b126dda60bf04999d1d30d959d8 commit bc7de6f91a9a0ae2f148a9d31a4027d441a51999 Author: jmc@openbsd.org Date: Wed Jan 18 06:55:32 2023 +0000 upstream: tweak previous; ok djm OpenBSD-Commit-ID: df71ce4180c58202dfdc1d92626cfe900b91b7c3 commit a20b7e999773e6333c8aa9b0a7fa41966e63b037 Author: Darren Tucker Date: Tue Jan 31 19:35:44 2023 +1100 Skip connection-timeout test under Valgrind. Valgrind slows things down so much that the timeout test fails. Skip this test until we figure out if we can make it work. commit c3ffb54b4fc5e608206037921db6ccbc2f5ab25f Author: Darren Tucker Date: Wed Jan 25 21:58:40 2023 +1100 Skip connection-timeout when missing FD passing. This tests uses multiplexing which uses file descriptor passing, so skip it if we don't have that. Fixes test failures on Cygwin. commit 35253af01d8c0ab444c8377402121816e71c71f5 Author: djm@openbsd.org Date: Wed Jan 18 02:00:10 2023 +0000 upstream: when restoring non-blocking mode to stdio fds, restore exactly the flags that ssh started with and don't just clobber them with zero, as this could also remove the append flag from the set; bz3523; ok dtucker@ OpenBSD-Commit-ID: 1336b03e881db7564a4b66014eb24c5230e9a0c0 commit 7d17ea151c0b2519f023bd9cc7f141128833ac47 Author: millert@openbsd.org Date: Wed Jan 18 01:50:21 2023 +0000 upstream: Add a -V (version) option to sshd like the ssh client has. OK markus@ deraadt@ OpenBSD-Commit-ID: abe990ec3e636fb040132aab8cbbede98f0c413e commit 62360feb7f08f2a4c6fc36f3b3449309203c42c9 Author: millert@openbsd.org Date: Tue Jan 17 18:52:44 2023 +0000 upstream: For "ssh -V" always exit 0, there is no need to check opt again. This was missed when the fallthrough in the switch case above it was removed. OK deraadt@ OpenBSD-Commit-ID: 5583e5d8f6d62a8a4215cfa95a69932f344c8120 commit 12492c0abf1eb415d08a897cc1d8b9e789888230 Author: djm@openbsd.org Date: Tue Jan 17 10:15:10 2023 +0000 upstream: also check that an active session inhibits UnusedConnectionTimeout idea markus@ OpenBSD-Regress-ID: 55c0fb61f3bf9e092b0a53f9041d3d2012f14003 commit cef2593c33ac46a58238ff998818754eabdf64ff Author: djm@openbsd.org Date: Tue Jan 17 10:02:34 2023 +0000 upstream: regression test for UnusedConnectionTimeout OpenBSD-Regress-ID: 7f29001374a68e71e5e078f69e4520cf4bcca084 commit aff9493a89c71d6a080419b49ac64eead9730491 Author: djm@openbsd.org Date: Mon Jan 16 04:11:29 2023 +0000 upstream: unbreak test: cannot access shell positional parameters past $9 without wrapping the position in braces (i.e. need ${10}, etc.) OpenBSD-Regress-ID: 3750ec98d5d409ce6a93406fedde6f220d2ea2ac commit 0293c19807f83141cdf33b443154459f9ee471f6 Author: djm@openbsd.org Date: Tue Jan 17 09:44:48 2023 +0000 upstream: Add a sshd_config UnusedConnectionTimeout option to terminate client connections that have no open channels for some length of time. This complements the recently-added ChannelTimeout option that terminates inactive channels after a timeout. ok markus@ OpenBSD-Commit-ID: ca983be74c0350364c11f8ba3bd692f6f24f5da9 commit 8ec2e3123802d2beeca06c1644b0b647f6d36dab Author: djm@openbsd.org Date: Sun Jan 15 23:35:10 2023 +0000 upstream: adapt to ed25519 changes in src/usr.bin/ssh OpenBSD-Regress-ID: 4b3e7ba7ee486ae8a0b4790f8112eded2bb7dcd5 commit 9fbbfeca1ce4c7ec0001c827bbf4189a3ba0964b Author: djm@openbsd.org Date: Sun Jan 15 23:05:32 2023 +0000 upstream: update OpenSSH's Ed25519 code to the last version of SUPERCOP (20221122) and change the import approach to the same one we use for Streamlined NTRUPrime: use a shell script to extract the bits we need from SUPERCOP, make some minor adjustments and squish them all into a single file. ok tb@ tobhe@ OpenBSD-Commit-ID: 1bc0fd624cb6af440905b8ba74ac7c03311b8e3b commit 6283f4bd83eee714d0f5fc55802eff836b06fea8 Author: Darren Tucker Date: Sat Jan 14 22:02:44 2023 +1100 Allow writev is seccomp sandbox. This seems to be used by recent glibcs at least in some configurations. From bz#3512, ok djm@ commit 923c3f437f439cfca238fba37e97a7041782f615 Author: dtucker@openbsd.org Date: Sat Jan 14 10:05:54 2023 +0000 upstream: Shell syntax fix. From ren mingshuai vi github PR#369. OpenBSD-Regress-ID: 6696b2eeefe128099fc3d7ea9f23252cc35156f9 commit 4d87a00f704e0365e11c3c38b170c1275ec461fc Author: dtucker@openbsd.org Date: Sat Jan 14 09:57:08 2023 +0000 upstream: Instead of skipping the all-tokens test if we don't have OpenSSL (since we use it to compute the hash), put the hash at the end and just omit it if we don't have it. Prompted by bz#3521. OpenBSD-Regress-ID: c79ecba64250ed3b6417294b6c965e6b12ca5eea commit b05406d6f93b8c8ec11ec8b27e7c76cc7a5a55fb Author: jmc@openbsd.org Date: Fri Jan 13 07:13:40 2023 +0000 upstream: fix double phrase in previous; OpenBSD-Commit-ID: 671e6c8dc5e9230518b2bbfa143daaa88adc66c2 commit 40564812b659c530eb1f4b62d09e85612aef3107 Author: dtucker@openbsd.org Date: Fri Jan 13 03:16:29 2023 +0000 upstream: Document "UserKnownHostsFile none". ok djm@ OpenBSD-Commit-ID: f695742d39e34ecdcc3c861c3739a84648a4bce5 commit d03e245e034019a37388f6f5f893ce848ab6d2e2 Author: Darren Tucker Date: Fri Jan 13 23:02:34 2023 +1100 Retry package installation 3 times. When setting up the CI environment, retry package installation 3 times before going up. Should help prevent spurious failures during infrastructure issues. commit 625f6bc39840167dafb3bf5b6a3e18503ac986e8 Author: dtucker@openbsd.org Date: Fri Jan 13 04:47:34 2023 +0000 upstream: Move scp path setting to a helper function. The previous commit to add scp to the test sshd's path causes the t-envpass test to fail when the test scp is given using a fully qualified path. Put this in a helper function and only call it from the scp tests. OpenBSD-Regress-ID: 7533dc1c4265c1de716abb062957994195b36df4 commit 6e6f88647042b3cde54a628545c2f5fb656a9327 Author: dtucker@openbsd.org Date: Fri Jan 13 04:23:00 2023 +0000 upstream: Add scp's path to test sshd's PATH. If the scp we're testing is fully qualified (eg it's not in the system PATH) then add its path to the under-test sshd's PATH so we can find it. Prompted by bz#3518. OpenBSD-Regress-ID: 7df4f5a0be3aa135495b7e5a6719d3cbc26cc4c0 commit 8a5e99a70fcf9b022a8aa175ebf6a71f58511da3 Author: Darren Tucker Date: Fri Jan 13 15:49:48 2023 +1100 Remove skipping test when scp not in path. An upcoming change renders this obsolete by adding scp's path to the test sshd's PATH, and removing this first will make the subsequent sync easier. commit 41f36dd896c8fb8337d403fcf476762986976e9d Author: dtucker@openbsd.org Date: Fri Jan 13 02:58:20 2023 +0000 upstream: Add a "Host" line to the output of ssh -G showing the original host arg. Inspired by patch from vincent at bernat.ch via bz#3343, ok djm@ OpenBSD-Commit-ID: 59c0f60a222113a44d0650cd394376e3beecc883 commit f673b49f3be3eb51074fbb8a405beb6cd0f7d93e Author: djm@openbsd.org Date: Fri Jan 13 02:44:02 2023 +0000 upstream: avoid printf("%s", NULL) if using ssh -oUserKnownHostsFile=none and a hostkey in one of the system known hosts file changes; ok dtucker@ OpenBSD-Commit-ID: 7ca87614bfc6da491315536a7f2301434a9fe614 commit 93fc7c576563e3d88a1dc019dd213f65607784cc Author: djm@openbsd.org Date: Wed Jan 11 05:39:38 2023 +0000 upstream: clamp the minimum buffer lengths and number of inflight requests too OpenBSD-Commit-ID: c4965f62fa0ba850940fd66ae3f60cf516bbcd56 commit 48bf234322e639d279c5a28435eae50155e9b514 Author: djm@openbsd.org Date: Wed Jan 11 05:36:50 2023 +0000 upstream: ignore bogus upload/download buffer lengths in the limits extension OpenBSD-Commit-ID: c5b023e0954693ba9a5376e4280c739b5db575f8 commit 36b00d31833ca74cb0f7c7d8eda1bde55700f929 Author: djm@openbsd.org Date: Wed Jan 11 02:13:52 2023 +0000 upstream: remove whitespace at EOL from code extracted from SUPERCOP OpenBSD-Commit-ID: 1ec524ff2fbb9387d731601437c82008f35a60f4 commit d888de06c5e4d7dbf2f2b85f2b5bf028c570cf78 Author: djm@openbsd.org Date: Wed Jan 11 00:51:27 2023 +0000 upstream: rewrite this test to use a multiplexed ssh session so we can control its lifecycle without risk of race conditions; fixes some of the Github integration tests for openssh-portable OpenBSD-Regress-ID: 5451cad59ba0d43ae9eeda48ec80f54405fee969 commit 4bcc737a35fdd9cc4af7423d6c23dfd0c7ef4786 Author: Damien Miller Date: Wed Jan 11 11:45:17 2023 +1100 remove buffer len workaround for NetBSD 4.x Switching to from pipes to a socketpair for communicating with the ssh process avoids the (kernel bug?) problem. commit f5154d2aac3e6a32a1b13dec23a701a087850cdc Author: Damien Miller Date: Wed Jan 11 11:44:19 2023 +1100 add back use of pipes in scp.c under USE_PIPES This matches sftp.c which prefers socketpair but uses pipes on some older platforms. commit eec737b59cf13841de46134967a206607000acd4 Author: millert@openbsd.org Date: Tue Jan 10 23:22:15 2023 +0000 upstream: Switch scp from using pipes to a socketpair for communication with it's ssh sub-processes. We no longer need to reserve two descriptors to ensure that we don't end up using fd 0-2 unexpectedly, that is handled by sanitise_stdfd() in main(). Based on an original diff from djm@. OK deraadt@ djm@ OpenBSD-Commit-ID: b80c372faac462471e955ddeab9480d668a2e48d commit d213d126a4a343abd3a1eb13687d39c1891fe5c8 Author: jmc@openbsd.org Date: Fri Jan 6 08:44:11 2023 +0000 upstream: tweak previous; ok djm OpenBSD-Commit-ID: 229c493452766d70a78b0f02f6ff9894f9028858 commit 4a5590a5ee47b7dfd49773e9fdba48ad3089fe64 Author: Damien Miller Date: Mon Jan 9 16:33:56 2023 +1100 try to improve logging for dynamic-forward test previously the logs from the ssh used to exercise the forwarding channel would clobber the logs from the ssh actually doing the forwarding commit 715bc25dcfccf9fb2bee820155fe071d01a618db Author: Darren Tucker Date: Sat Jan 7 23:24:50 2023 +1100 Skip dynamic-forward test on minix3. This test relies on loopback addresses which minix does not have. Previously the test would not run at all since it also doesn't have netcat, but now we use our own netcat it tries and fails. commit dd1249bd5c45128a908395c61b26996a70f82205 Author: Damien Miller Date: Sun Jan 8 12:08:59 2023 +1100 don't test IPv6 addresses if platform lacks support commit d77fc611a62f2dfee0b654c31a50a814b13310dd Author: dtucker@openbsd.org Date: Fri Jan 6 12:33:33 2023 +0000 upstream: When OpenSSL is not available, skip parts of percent test that require it. Based on github pr#368 from ren mingshuai. OpenBSD-Regress-ID: 49a375b2cf61ccb95b52e75e2e025cd10988ebb2 commit 1cd2aac312af9172f1b5cb06c2e1cd090abb83cf Author: Darren Tucker Date: Sat Jan 7 23:01:11 2023 +1100 Use our own netcat for dynamic-forward test. That way we can be surer about its behaviour rather than trying to second-guess the behaviour of various netcat implementations. commit 26cab41c05d7b0859d2a1ea5b6ed253d91848a80 Author: Darren Tucker Date: Sat Jan 7 14:30:43 2023 +1100 Use autoconf to find openssl binary. It's possible to install an OpenSSL in a path not in the system's default library search path. OpenSSH can still use this (eg if you specify an rpath) but the openssl binary there may not work. If one is available on the system path just use that. commit 5532e010a0eeb6aa264396514f9aed7948471538 Author: Darren Tucker Date: Sat Jan 7 10:34:18 2023 +1100 Check openssl_bin path is executable before using. commit 5d7b16cff48598d5908db970bfdc9ff9326142c8 Author: Darren Tucker Date: Fri Jan 6 23:19:07 2023 +1100 Set OPENSSL_BIN from OpenSSL directory. commit 344a0e8240eaf08da5d46a5e3a9ecad6e4f64c35 Author: dtucker@openbsd.org Date: Fri Jan 6 08:50:33 2023 +0000 upstream: Save debug logs from ssh for debugging purposes. OpenBSD-Regress-ID: 109e40b06de1c006a3b8e0d8745b790b2c5870a0 commit e1ef172646f7f49c80807eea90225ef5e0be55a8 Author: djm@openbsd.org Date: Fri Jan 6 08:07:39 2023 +0000 upstream: regression test for ChannelTimeout OpenBSD-Regress-ID: 280bfbefcfa415428ad744e43f69a8dede8ad685 commit 2393ea8daf25853459eb07a528d7577688847777 Author: djm@openbsd.org Date: Fri Jan 6 07:18:18 2023 +0000 upstream: fix typo in verbose logging OpenBSD-Regress-ID: 0497cdb66e003b2f50ed77291a9104fba2e017e9 commit 161a5378a3cc2e7aa3f9674cb7f4686ae6ce9586 Author: djm@openbsd.org Date: Fri Jan 6 02:59:50 2023 +0000 upstream: unit tests for misc.c:ptimeout_* API OpenBSD-Regress-ID: 01f8fb12d08e5aaadd4bd4e71f456b6588be9a94 commit 018d671d78145f03d6f07ae9d64d51321da70325 Author: tb@openbsd.org Date: Wed Jan 4 22:48:57 2023 +0000 upstream: Copy bytes from the_banana[] rather than banana() Fixes test failure due to segfault seen on arm64 with xonly snap. ok djm OpenBSD-Regress-ID: 86e2aa4bbd1dff1bc4ebb2969c0d6474485be046 commit ab6bb69e251faa8b24f81b25c72ec0120f20cad4 Author: Damien Miller Date: Fri Jan 6 19:13:36 2023 +1100 unbreak scp on NetBSD 4.x e555d5cad5 effectively increased the default copy buffer size for SFTP transfers. This caused NetBSD 4.x to hang during the "copy local file to remote file in place" scp.sh regression test. This puts back the original 32KB copy buffer size until we can properly figure out why. lots of debugging assistance from dtucker@ commit 2d1ff2b9431393ad99ef496d5e3b9dd0d4f5ac8c Author: djm@openbsd.org Date: Fri Jan 6 02:47:18 2023 +0000 upstream: Implement channel inactivity timeouts This adds a sshd_config ChannelTimeouts directive that allows channels that have not seen traffic in a configurable interval to be automatically closed. Different timeouts may be applied to session, X11, agent and TCP forwarding channels. Note: this only affects channels over an opened SSH connection and not the connection itself. Most clients close the connection when their channels go away, with a notable exception being ssh(1) in multiplexing mode. ok markus dtucker OpenBSD-Commit-ID: ae8bba3ed9d9f95ff2e2dc8dcadfa36b48e6c0b8 commit 0e34348d0bc0b1522f75d6212a53d6d1d1367980 Author: djm@openbsd.org Date: Fri Jan 6 02:42:34 2023 +0000 upstream: Add channel_set_xtype() This sets an "extended" channel type after channel creation (e.g. "session:subsystem:sftp") that will be used for setting channel inactivity timeouts. ok markus dtucker OpenBSD-Commit-ID: 42564aa92345045b4a74300528f960416a15d4ca commit ceedf09b2977f3a756c759a6e7eb8f8e9db86a18 Author: djm@openbsd.org Date: Fri Jan 6 02:41:49 2023 +0000 upstream: tweak channel ctype names These are now used by sshd_config:ChannelTimeouts to specify timeouts by channel type, so force them all to use a similar format without whitespace. ok dtucker markus OpenBSD-Commit-ID: 66834765bb4ae14f96d2bb981ac98a7dae361b65 commit c60438158ad4b2f83d8504257aba1be7d0b0bb4b Author: djm@openbsd.org Date: Fri Jan 6 02:39:59 2023 +0000 upstream: Add channel_force_close() This will forcibly close an open channel by simulating read/write errors, draining the IO buffers and calling the detach function. Previously the detach function was only ever called during channel garbage collection, but there was no way to signal the user of a channel (e.g. session.c) that its channel was being closed deliberately (vs. by the usual state-machine logic). So this adds an extra "force" argument to the channel cleanup callback to indicate this condition. ok markus dtucker OpenBSD-Commit-ID: 23052707a42bdc62fda2508636e624afd466324b commit d478cdc7ad6edd4b1bcd1e86fb2f23194ff33d5a Author: djm@openbsd.org Date: Fri Jan 6 02:38:23 2023 +0000 upstream: replace manual poll/ppoll timeout math with ptimeout API feedback markus / ok markus dtucker OpenBSD-Commit-ID: c5ec4f2d52684cdb788cd9cbc1bcf89464014be2 commit 4adf3817a24efe99b06e62630577d683c7cd8065 Author: djm@openbsd.org Date: Fri Jan 6 02:37:04 2023 +0000 upstream: add ptimeout API for keeping track of poll/ppoll timeouts; ok dtucker markus OpenBSD-Commit-ID: 3335268ca135b3ec15a947547d7cfbb8ff929ead commit 8c7c69d32375d2f3ce9da0109c9bffc560842316 Author: djm@openbsd.org Date: Thu Jan 5 05:49:13 2023 +0000 upstream: suppress "Connection closed" message when in quiet mode OpenBSD-Commit-ID: 8a3ab7176764da55f60bfacfeae9b82d84e3908f commit 845ceecea2ac311b0c267f9ecbd34862e1876fc6 Author: djm@openbsd.org Date: Mon Jan 2 07:03:57 2023 +0000 upstream: regression test for PermitRemoteOpen OpenBSD-Regress-ID: 8271aafbf5c21950cd5bf966f08e585cebfe630c commit b3daa8dc582348d6ab8150bc1e571b7aa08c5388 Author: djm@openbsd.org Date: Mon Jan 2 07:03:30 2023 +0000 upstream: fix bug in PermitRemoteOpen which caused it to ignore its first argument unless it was one of the special keywords "any" or "none". Reported by Georges Chaudy in bz3515; ok dtucker@ OpenBSD-Commit-ID: c5678a39f1ff79993d5ae3cfac5746a4ae148ea5 commit 0872663a7be0301bcc3d49acdbc9b740a3d972d4 Author: jmc@openbsd.org Date: Mon Dec 26 19:16:03 2022 +0000 upstream: spelling fixes; from paul tagliamonte amendments to his diff are noted on tech OpenBSD-Commit-ID: d776dd03d0b882ca9c83b84f6b384f6f9bd7de4a commit 797da2812a71785b34890bb6eb44767a7d09cd34 Author: djm@openbsd.org Date: Fri Dec 16 07:13:22 2022 +0000 upstream: Mention that scp uses the SFTP protocol and remove reference to legacy flag. Spotted by, feedback and ok jmc@ OpenBSD-Commit-ID: 9dfe04966f52e941966b46c7a2972147f95281b3 commit 93f2ce8c050a7a2a628646c00b40b9b53fef93ef Author: djm@openbsd.org Date: Fri Dec 16 06:56:47 2022 +0000 upstream: Clear signal mask early in main(); sshd may have been started with one or more signals masked (sigprocmask(2) is not cleared on fork/exec) and this could interfere with various things, e.g. the login grace timer. Execution environments that fail to clear the signal mask before running sshd are clearly broken, but apparently they do exist. Reported by Sreedhar Balasubramanian; ok dtucker@ OpenBSD-Commit-ID: 77078c0b1c53c780269fc0c416f121d05e3010ae commit 4acfaabfae41badb9d334a2ee88c5c6ad041c0d5 Author: jmc@openbsd.org Date: Fri Dec 16 06:52:48 2022 +0000 upstream: add -X to usage(); OpenBSD-Commit-ID: 1bdc3df7de11d766587b0428318336dbffe4a9d0 commit e555d5cad5afae7d5ef2bbc02ca591178fe16fed Author: djm@openbsd.org Date: Fri Dec 16 03:40:03 2022 +0000 upstream: add a -X option to both scp(1) and sftp(1) to allow control over some SFTP protocol knobs: the copy buffer length and the number of inflight requests, both of which are used during upload/download. Previously these could be controlled in sftp(1) using the -b/-R options. This makes them available in both SFTP protocol clients using the same option character sequence. ok dtucker@ OpenBSD-Commit-ID: 27502bffc589776f5da1f31df8cb51abe9a15f1c commit 5a7a7acab2f466dc1d7467b5d05d35268c3137aa Author: deraadt@openbsd.org Date: Thu Dec 15 18:20:39 2022 +0000 upstream: The idiomatic way of coping with signed char vs unsigned char (which did not come from stdio read functions) in the presence of ctype macros, is to always cast to (unsigned char). casting to (int) for a "macro" which is documented to take int, is weird. And sadly wrong, because of the sing extension risk.. same diff from florian OpenBSD-Commit-ID: 65b9a49a68e22ff3a0ebd593f363e9f22dd73fea commit b0b58222c7cc62efd8212c4fb65a545f58ebb22d Author: Darren Tucker Date: Mon Dec 19 18:49:51 2022 +1100 Simply handling of SSH_CONNECTION PAM env var. Prompted by bz#3508: there's no need to cache the value of sshpam_conninfo so remove the global. While there, add check of return value from pam_putenv. ok djm@ commit ed8444572ae684fdb892f97bae342c6cb6456f04 Author: Darren Tucker Date: Mon Dec 19 18:42:34 2022 +1100 Add tests for LibreSSL 3.7.0 and OpenSSL 1.1.1s. commit abb9a8aaddfcacbd12641f6e4f203da0fa85a287 Author: Darren Tucker Date: Sun Dec 18 21:36:25 2022 +1100 Use sudo when resetting perms on directories. commit 2f5664c5908d84697cbe91302d5d5c4d83cb2121 Author: Darren Tucker Date: Sun Dec 18 21:19:33 2022 +1100 Set group perms on regress dir. This ensures that the tests don't fail due to StrictMode checks. commit 137196300fc1540affadde880210f02ba6cb4abf Author: Darren Tucker Date: Sun Dec 18 21:13:42 2022 +1100 Fetch regress logs from obj dir. commit 5f93c4836527d9fda05de8944a1c7b4a205080c7 Author: Darren Tucker Date: Tue Dec 13 20:59:54 2022 +1100 obsdsnap test VMs runs-on libvirt too. commit 8386886fb1ab7fda73069fb0db1dbe0e5a52f758 Author: Darren Tucker Date: Tue Dec 13 20:55:37 2022 +1100 Run upstream obsdsnap tests on ephemeral runners. commit b6e01459b55ece85d7f296b2bc719d1841e1009e Author: Darren Tucker Date: Tue Dec 13 20:48:56 2022 +1100 Move obsdsnap test VMs to ephemeral runners. commit ea6fdf9a1aa71a411f7db218a986392c4fb55693 Author: Damien Miller Date: Fri Dec 9 18:00:21 2022 +1100 use calloc for allocating arc4random structs ok dtucker commit 4403b62f5548e91389cb3339d26a9d0c4bb07b34 Author: dtucker@openbsd.org Date: Fri Dec 9 00:22:29 2022 +0000 upstream: Warn if no host keys for hostbased auth can be loaded. OpenBSD-Commit-ID: 2a0a13132000cf8d3593133c1b49768aa3c95977 commit a6183e25e3f1842e21999fe88bc40bb99b121dc3 Author: dtucker@openbsd.org Date: Fri Dec 9 00:17:40 2022 +0000 upstream: Add server debugging for hostbased auth. auth_debug_add queues messages about the auth process which is sent to the client after successful authentication. This also sends those to the server debug log to aid in debugging. From bz#3507, ok djm@ OpenBSD-Commit-ID: 46ff67518cccf9caf47e06393e2a121ee5aa258a commit b85c3581c16aaf6e83b9a797c80705a56b1f312e Author: cheloha@openbsd.org Date: Sun Dec 4 23:50:49 2022 +0000 upstream: remove '?' from getopt(3) loops userspace: remove vestigial '?' cases from top-level getopt(3) loops getopt(3) returns '?' when it encounters a flag not present in the in the optstring or if a flag is missing its option argument. We can handle this case with the "default" failure case with no loss of legibility. Hence, remove all the redundant "case '?':" lines. Prompted by dlg@. With help from dlg@ and millert@. Link: https://marc.info/?l=openbsd-tech&m=167011979726449&w=2 ok naddy@ millert@ dlg@ OpenBSD-Commit-ID: b2f89346538ce4f5b33ab8011a23e0626a67e66e commit 9a067e8d28a2249fd73f004961e30c113ee85e5d Author: dtucker@openbsd.org Date: Wed Dec 7 11:45:43 2022 +0000 upstream: Fix comment typo. OpenBSD-Regress-ID: 3b04faced6511bb5e74648c6a4ef4bf2c4decf03 commit ce3c3e78ce45d68a82c7c8dc89895f297a67f225 Author: Darren Tucker Date: Wed Dec 7 18:58:25 2022 +1100 Add SANDBOX_DEBUG to the kitchensink test build. commit bc234605fa3eb10f56bf0d74c8ecb0d91ada9d05 Author: Damien Miller Date: Wed Dec 7 18:38:25 2022 +1100 disable SANDBOX_SECCOMP_FILTER_DEBUG It was mistakenly enabled in 2580916e4872 Reported by Peter sec-openssh-com.22.fichtner AT 0sg.net commit b087c5cfa011b27992e01589314fec830266f99d Author: Rose <83477269+AtariDreams@users.noreply.github.com> Date: Tue Nov 29 15:12:54 2022 -0500 Update autotools Regenerate config files using latest autotools commit d63f5494978a185c7421d492b9c2f6f05bb54138 Author: Darren Tucker Date: Tue Dec 6 12:22:36 2022 +1100 Fix typo in comment. Spotted by tim@ commit 73dcca12115aa12ed0d123b914d473c384e52651 Author: dtucker@openbsd.org Date: Sun Dec 4 11:03:11 2022 +0000 upstream: Remove duplicate includes. Patch from AtariDreams via github PR#364. OpenBSD-Commit-ID: b9186638a05cb8b56ef7c0de521922b6723644ea commit 3cec15543010bc8d6997d896b1717a650afb7e92 Author: djm@openbsd.org Date: Fri Dec 2 04:40:27 2022 +0000 upstream: make struct sshbuf private and remove an unused field; ok dtucker OpenBSD-Commit-ID: c7a3d77c0b8c153d463398606a8d57569186a0c3 commit 5796bf8ca9535f9fa7d01829a540d2550e05c860 Author: Darren Tucker Date: Fri Dec 2 11:43:36 2022 +1100 Restore ssh-agent permissions on exit. ...enough that subsequent builds can overwrite ssh-agent if necessary. commit ccf5a13868cbb4659107458cac1e017c98abcbda Author: dtucker@openbsd.org Date: Thu Dec 1 02:22:13 2022 +0000 upstream: Clean up ssh-add and ssh-agent logs. OpenBSD-Regress-ID: 9eda8e4c3714d7f943ab2e73ed58a233bd29cd2c commit 7a8b40cf6a5eda80173140cc6750a6db8412fa87 Author: dtucker@openbsd.org Date: Thu Dec 1 02:19:29 2022 +0000 upstream: Log output of ssh-agent and ssh-add This should make debugging easier. OpenBSD-Regress-ID: 5974b02651f428d7e1079b41304c498ca7e306c8 commit 4a1805d532616233dd6072e5cd273b96dd3062e6 Author: dtucker@openbsd.org Date: Tue Nov 29 22:41:14 2022 +0000 upstream: Add void to client_repledge args to fix compiler warning. ok djm@ OpenBSD-Commit-ID: 7e964a641ce4a0a0a11f047953b29929d7a4b866 commit 815c4704930aa449edf6e812e99d69e9ffd31f01 Author: djm@openbsd.org Date: Mon Nov 28 01:38:22 2022 +0000 upstream: tighten pledge(2) after session establishment feedback, ok & testing in snaps deraadt@ OpenBSD-Commit-ID: aecf4d49d28586dfbcc74328d9333398fef9eb58 commit f7cebbbf407d772ed71403d314343766782fe540 Author: djm@openbsd.org Date: Mon Nov 28 01:37:36 2022 +0000 upstream: New EnableEscapeCommandline ssh_config(5) option This option (default "no") controls whether the ~C escape is available. Turning it off by default means we will soon be able to use a stricter default pledge(2) in the client. feedback deraadt@ dtucker@; tested in snaps for a while OpenBSD-Commit-ID: 7e277595d60acb8263118dcb66554472257b387a commit d323f7ecf52e3d4ec1f4939bf31693e02f891dca Author: mbuhl@openbsd.org Date: Fri Nov 18 19:47:40 2022 +0000 upstream: In channel_request_remote_forwarding the parameters for permission_set_add are leaked as they are also duplicated in the call. Found by CodeChecker. ok djm OpenBSD-Commit-ID: 4aef50fa9be7c0b138188814c8fe3dccc196f61e commit 62cc33e6eed847aafdc29e34aa69e9bd82a0ee16 Author: Darren Tucker Date: Wed Nov 30 11:23:11 2022 +1100 Use -fzero-call-used-regs=used on clang 15. clang 15 seems to have a problem with -fzero-call-used-reg=all which causes spurious "incorrect signature" failures with ED25519. On those versions, use -fzero-call-used-regs=used instead. (We may add exceptions later if specific versions prove to be OK). Also move the GCC version check to match. Initial investigation by Daniel Pouzzner (douzzer at mega nu), workaround suggested by Bill Wendling (morbo at google com). bz#3475, ok djm@ commit f84b9cffd52c9c5c359a54a1929f9948e803ab1d Author: Darren Tucker Date: Mon Nov 28 21:09:28 2022 +1100 Skip unit tests on slow riscv64 hardware. commit 9f2747e0bed3faca92679eae69aef10c95dc82f5 Author: Darren Tucker Date: Sun Nov 27 15:26:22 2022 +1100 Rework how selfhosted tests interact with runners. Previously there was one runner per test target (mostly VMs). This had a few limitations: - multiple tests that ran on the same target (eg multiple build configs) were serialized on availability or that runner. - it needed manual balancing of VMs over host machines. To address this, make VMs that use ephemeral disks (ie most of them) all use a pool of runners with the "libvirt" label. This requires that we distinguish between "host" and "target" for those. Native runners and VMs with persistent disks (eg the constantly-updated snapshot ones) specify the same host and target. This should improve test throughput. commit d664ddaec87bdc7385be8ef7f1337793e1679d48 Author: Darren Tucker Date: Sun Nov 27 12:19:37 2022 +1100 Run vmstartup from temp dir. This will allow us to create ephemeral disk images per-runner. commit 0fa16e952b1fc1c4cf65e3dd138b0e87003e2e45 Author: Darren Tucker Date: Sun Nov 27 12:14:00 2022 +1100 Make "config" in matrix singular and pass in env. This will allow the startup scripts to adapt their behaviour based on the type and config. commit e8857043af54809187be1e8b06749db61112899f Author: Darren Tucker Date: Sun Nov 27 11:42:22 2022 +1100 Add "libvirt" label to dfly30. commit 9775473d84902dc37753686cd10ae71fbe67efda Author: Darren Tucker Date: Sun Nov 27 09:28:20 2022 +1100 Rename "os" in matrix to "target". This is in preparation to distinguish this from the host that the runner runs on in case where they are separate (eg VMs). commit 04fd00ceff39f4544ced6f5342060abe584835d0 Author: Darren Tucker Date: Sun Nov 27 09:23:04 2022 +1100 Remove unused self-hosted test targets. commit c9d9fcad2a11c1cd1550a541f44091d65f0b5584 Author: Darren Tucker Date: Sun Nov 27 09:16:15 2022 +1100 Remove explicit "default" test config argument. Not specifying the test config implicitly selects default args. commit 15a01cf15f396f87c6d221c5a6af98331c818962 Author: Darren Tucker Date: Wed Nov 23 13:18:54 2022 +1100 Add fallback for old platforms w/out MAP_ANON. commit 6b9bbbfe8b26db6e9a30a7e08c223e85421aed98 Author: Darren Tucker Date: Wed Nov 23 13:09:11 2022 +1100 If we haven't found it yet, recheck for sys/stat.h. On some very old platforms, sys/stat.h needs sys/types.h, however autoconf 2.71's AC_CHECK_INCLUDES_DEFAULT checks for them in the opposite order, which in combination with modern autoconf's "present but cannot be compiled" behaviour causes it to not be detected. commit 8926956f22639132a9f2433fcd25224e01b900f5 Author: Darren Tucker Date: Fri Nov 11 11:25:37 2022 +1100 Add dfly62 test target. commit 650de7ecd3567b5a5dbf16dd1eb598bd8c20bca8 Author: dtucker@openbsd.org Date: Thu Nov 10 23:03:10 2022 +0000 upstream: Handle dynamic remote port forwarding in escape commandline's -R processing. bz#3499, ok djm@ OpenBSD-Commit-ID: 194ee4cfe7ed0e2b8ad0727f493c798a50454208 commit 5372db7e7985ba2c00f20fdff8942145ca99e033 Author: Darren Tucker Date: Thu Nov 10 12:44:51 2022 +1100 Remove seed passing over reexec. This was added for the benefit of platforms using ssh-rand-helper to prevent a delay on each connection as sshd reseeded itself. ssh-random-helper is long gone, and since the re-exec happens before the chroot the re-execed sshd can reseed itself normally. ok djm@ commit ca98d3f8c64cfc51af81e1b01c36a919d5947ec2 Author: Darren Tucker Date: Wed Nov 9 20:59:20 2022 +1100 Skip reexec test on OpenSSL 1.1.1 specifically. OpenSSL 1.1.1 has a bug in its RNG that breaks reexec fallback, so skip that test. See bz#3483 for details. commit 5ec4ebc2548e5f7f1b55b2a5cef5b67bdca8146f Author: dtucker@openbsd.org Date: Wed Nov 9 09:04:12 2022 +0000 upstream: Fix typo in fatal error message. Patch from vapier at chromium.org. OpenBSD-Commit-ID: 8a0c164a6a25eef0eedfc30df95bfa27644e35cf commit e6abafe9a6d809422d3432b95b3f9747b0acaa71 Author: dtucker@openbsd.org Date: Wed Nov 9 09:01:52 2022 +0000 upstream: Remove errant colon and simplify format string in error messages. Patch from vapier at chromium.org. OpenBSD-Commit-ID: fc28466ebc7b74e0072331947a89bdd239c160d3 commit db2027a687516f87c3fb141e87154bb3d8a7807c Author: djm@openbsd.org Date: Wed Nov 9 01:37:44 2022 +0000 upstream: rename client_global_hostkeys_private_confirm() to client_global_hostkeys_prove_confirm(), as it handles the "hostkeys-prove00@openssh.com" message; no functional change OpenBSD-Commit-ID: 31e09bd3cca6eed26855b88fb8beed18e9bd026d commit 1c2be7c2004cf1abcd172fee9fe3eab57cd4c426 Author: djm@openbsd.org Date: Wed Nov 9 00:15:59 2022 +0000 upstream: typo in comment OpenBSD-Commit-ID: 39c58f41e0f32d1ff31731fa6f5bbbc3ad25084a commit cf1a9852d7fc93e4abc4168aed09529a57427cdc Author: Darren Tucker Date: Wed Nov 9 09:23:47 2022 +1100 Defer seed_rng until after closefrom call. seed_rng will initialize OpenSSL, and some engine providers (eg Intel's QAT) will open descriptors for their own use. bz#3483, patch from joel.d.schuetze at intel.com, ok djm@ commit dffa64480163fbf76af7e4fb62c26bb0dd6642aa Author: Darren Tucker Date: Wed Nov 9 08:27:47 2022 +1100 Fix comment text. From emaste at freebsd.org. commit d9df5689c29823ab830ec4f54c83c6cc3c0077ad Author: Pierre Ossman Date: Wed Jul 6 13:52:10 2022 +0200 Avoid assuming layout of fd_set POSIX doesn't specify the internal layout of the fd_set object, so let's not assume it is just a bit mask. This increases compatibility with systems that have a different layout. The assumption is also worthless as we already refuse to use file descriptors over FD_SETSIZE anyway. Meaning that the default size of fd_set is quite sufficient. commit 419aa8a312e8d8f491933ca3d5933e602cb05aae Author: Darren Tucker Date: Tue Nov 8 12:42:52 2022 +1100 Shutdown any VM before trying to check out repo. In the case where the previous run did not clean up, the checkout will fail as it'll leave a stale mount. commit a32c07cbb78f65d8527642b96474a83b413f8108 Author: Darren Tucker Date: Tue Nov 8 11:33:25 2022 +1100 Run vm startup and shutdown from runner temp dir. Should work even if the github workspace dir is on a stale sshfs mount. commit 2b40a7dfcdb8e616155b9504145aa52b271455aa Author: Darren Tucker Date: Tue Nov 8 11:03:31 2022 +1100 Add valrind-5 test here too. commit 2ea03d1f6d0a05ee2b63ed2dc0f2d54f1e4655a1 Author: Darren Tucker Date: Tue Nov 8 09:21:10 2022 +1100 Update checkout and upload actions. Update actions/checkout and actions/upload-artifact to main branch for compatibility with node.js v16. commit 4e316ff0f18a118232bb9ac6512ee62773a9e8ea Author: Darren Tucker Date: Tue Nov 8 09:17:04 2022 +1100 Split out rekey test since it runs the longest. commit 21625a6424258a92a96a3bb73ae6aabc5ed8a6b4 Author: dtucker@openbsd.org Date: Mon Nov 7 10:09:28 2022 +0000 upstream: The IdentityFile option in ssh_config can also be used to specify a public key file, as documented in ssh.1 for the -i option. Document this also for IdentityFile in ssh_config.5, for documentation completeness. From laalsaas at systemli.org via portable github PR#352, ok jmc@ djm@ OpenBSD-Commit-ID: 2f943be9f96e60ef81a9a4faa25b009999f9883b commit 747691604d3325ed2b62bad85b6fd8563ad32f6c Author: dtucker@openbsd.org Date: Mon Nov 7 10:05:38 2022 +0000 upstream: Remove some set but otherwise unused variables, spotted in -portable by clang 16's -Wunused-but-set-variable. ok djm@ OpenBSD-Commit-ID: 3d943ddf2369b38fbf89f5f19728e7dc1daf3982 commit 1d78d25653805aefc7a8dd9d86cd7359ada3823c Author: dtucker@openbsd.org Date: Mon Nov 7 10:02:59 2022 +0000 upstream: Check for and disallow MaxStartups values less than or equal to zero during config parsing, rather than faling later at runtime. bz#3489, ok djm@ OpenBSD-Commit-ID: d79c2b7a8601eb9be493629a91245d761154308b commit a00f59a645072e5f5a8d207af15916a7b23e2642 Author: djm@openbsd.org Date: Mon Nov 7 04:04:40 2022 +0000 upstream: fix parsing of hex cert expiry time; was checking whether the start time began with "0x", not the expiry time. from Ed Maste OpenBSD-Commit-ID: 6269242c3e1a130b47c92cfca4d661df15f05739 commit f58acaf8c7315483f4ac87d46a1aa2142a713cd8 Author: Darren Tucker Date: Mon Nov 7 15:10:59 2022 +1100 Fix merge conflict. commit 162e5741020a8d996c0c12b988b118e71ed728e6 Author: Darren Tucker Date: Mon Nov 7 15:04:33 2022 +1100 Branch-specific links for master status badges. commit e4b7c12ab24579312aa3ed38ce7041a439ec2d56 Author: Darren Tucker Date: Mon Nov 7 14:46:38 2022 +1100 Add CIFuzz status badge. commit b496b9f831acd1e5bcd875e26e797488beef494a Author: Darren Tucker Date: Mon Nov 7 14:45:16 2022 +1100 Do not run CIFuzz on selfhosted tree. We already run it on the regular tree, no need to double up. commit 2138b1c4ddb300129a41a5104627b0d561184c7b Author: Darren Tucker Date: Mon Nov 7 14:41:58 2022 +1100 Whitespace change to trigger CIFuzz workflow. commit 4670b97ef87c7b0f21283c9b07c7191be88dda05 Author: Darren Tucker Date: Mon Nov 7 14:34:04 2022 +1100 Run cifuzz workflow on the actions as regular CI. commit 79391e66ce851ace1baf3c6a35e83a23f08ec2ba Author: David Korczynski Date: Tue Nov 30 11:45:20 2021 +0000 Add CIFuzz integration commit c1893364a0be243270014d7d34362a8101d55112 Author: dtucker@openbsd.org Date: Mon Nov 7 02:21:22 2022 +0000 upstream: Import regenerated moduli. OpenBSD-Commit-ID: b0e54ee4d703bd6929bbc624068666a7a42ecb1f commit 5c3f18fb994ef27e685b205ee2351851b80fdbd1 Author: dtucker@openbsd.org Date: Mon Nov 7 01:53:01 2022 +0000 upstream: Fix typo. From pablomh via -portable github PR#344. OpenBSD-Commit-ID: d056ee2e73691dc3ecdb44a6de68e6b88cd93827 commit e1c6fcc142066417c9832e634463faa3dd5d116c Author: Darren Tucker Date: Mon Nov 7 12:46:58 2022 +1100 Link to branch-specific queries for V_9_1 status. commit 4f4a5fad6d8892c3f8ee9cd81ec7de6458210c9f Author: Darren Tucker Date: Sun Nov 6 10:55:59 2022 +1100 Use "prohibit-password" in -portable comments. "without-password" is the deprecated alias for "prohibit-password", so we should reference the latter. From emaste at freebsd.org. commit 0f7e1eba55259ec037f515000b4c4afbf446230a Author: Darren Tucker Date: Sun Nov 6 10:50:01 2022 +1100 Fix tracing disable on FreeBSD. Some versions of FreeBSD do not support using id 0 to refer to the current pid for procctl, so pass getpid() explicitly. From emaste at freebsd.org. commit 32fddb982fd61b11a2f218a115975a87ab126d43 Author: Darren Tucker Date: Mon Nov 7 10:39:01 2022 +1100 Fix setres*id checks to work with clang-16. glibc has the prototypes for setresuid and setresgid behind _GNU_SOURCE, and clang 16 will error out on implicit function definitions, so add _GNU_SOURCE and the required headers to the configure checks. From sam at @gentoo.org via bz#3497. commit 12af712d116f42164bcfa56db901d06e4fa27199 Author: Sam James Date: Sun Nov 6 04:52:38 2022 +0000 configure.ac: Fix -Wstrict-prototypes Clang 16 now warns on this and it'll be removed in C23, so let's just be future proof. It also reduces noise when doing general Clang 16 porting work (which is a big job as it is). github PR#355. Signed-off-by: Sam James commit 40b0a5eb6e3edfa2886b60c09c7803353b0cc7f5 Author: Sam James Date: Sun Nov 6 04:47:35 2022 +0000 configure.ac: Add include for openpty Another Clang 16ish fix (which makes -Wimplicit-function-declaration an error by default). github PR#355. See: 2efd71da49b9cfeab7987058cf5919e473ff466b See: be197635329feb839865fdc738e34e24afd1fca8 commit 6b17e128879ec6cc32ca2c28b5d894b4aa72e32d Author: Rochdi Nassah Date: Fri Oct 28 01:26:31 2022 +0100 Fix broken zlib link. commit 99500df246ccb736ddbdd04160dcc82165d81a77 Author: Darren Tucker Date: Fri Nov 4 16:59:26 2022 +1100 Don't run openbsd-compat tests on Cygwin. Add "compat-tests" to the default TEST_TARGET so we can override as necessary. Override TEST_TARGET for Cygwin as the tests don't currently compile there. commit 3cae9f92a31897409666aa1e6f696f779759332b Author: djm@openbsd.org Date: Thu Nov 3 21:59:20 2022 +0000 upstream: replace recently-added valid_domain() check for hostnames going to known_hosts with a more relaxed check for bad characters; previous commit broke address literals. Reported by/feedback from florian@ OpenBSD-Commit-ID: 10b86dc6a4b206adaa0c11b58b6d5933898d43e0 commit 9655217231c9056200bea7ae2dffcc9c0c3eb265 Author: Darren Tucker Date: Thu Nov 3 23:07:50 2022 +1100 Rerun tests on changes to Makefile.in in any dir. commit 3500f0405a3ab16b59a26f3508c4257a3fc3bce6 Author: Darren Tucker Date: Thu Nov 3 23:04:08 2022 +1100 Link libssh into compat tests. The cygwin compat code uses xmalloc, so add libssh.a so pick up that. commit ec59effcf65b8a4c85d47ff5a271123259dd0ab8 Author: Darren Tucker Date: Thu Nov 3 21:44:23 2022 +1100 Fix compat regress to work with non-GNU make. commit 73550a218e7dfbbd599534cbf856309bc924f6fd Author: Darren Tucker Date: Thu Nov 3 13:41:16 2022 +1100 Increase selfhosted job timeout. The default job timeout of 360 (6h) is not enough to complete the regress tests for some of the slow VMs depending on the load on the host. Increase to 600 (10h). commit db97d8d0b90c6ce52b94b153d6f8f5f7d3b11777 Author: Darren Tucker Date: Thu Nov 3 10:00:43 2022 +1100 Only run opensslver tests if built with OpenSSL. commit ba053709638dff2f6603df0c1f340352261d63ea Author: Darren Tucker Date: Wed Nov 2 14:16:04 2022 +1100 Add tests for OpenSSL 3.0.7 and LibreSSL 3.6.1. commit edd24101c7e17d1a8f6576e1aaf62233b47ad6f5 Author: Darren Tucker Date: Thu Nov 3 08:17:39 2022 +1100 Run compat regress tests too. commit fe88d67e7599b0bc73f6e4524add28d743e7f977 Author: Darren Tucker Date: Thu Nov 3 08:14:05 2022 +1100 Compat tests need libcrypto. This was moved to CHANNELLIBS during the libs refactor. Spotted by rapier at psc.edu. commit 96b519726b7944eee3c23a54eee3d5c031ba1533 Author: Darren Tucker Date: Thu Nov 3 04:24:39 2022 +1100 Include time.h when defining timegm. Fixes build on some platforms eg recent AIX. commit da6038bd5cd55eb212eb2aec1fc8ae79bbf76156 Author: Darren Tucker Date: Tue Nov 1 19:10:30 2022 +1100 Always use compat getentropy. Have it call native getentropy and fall back as required. Should fix issues of platforms where libc has getentropy but it is not implemented in the kernel. Based on github PR#354 from simsergey. commit 5ebe18cab6be3247b44c807ac145164010465b82 Author: Darren Tucker Date: Wed Nov 2 10:51:48 2022 +1100 Check for sockaddr_in.sin_len. If found, set SOCK_HAS_LEN which is used in addr.c. Should fix keyscan tests on platforms with this (eg old NetBSD). commit a1febadf426536612c2734168d409147c392e7cf Author: dtucker@openbsd.org Date: Sun Oct 30 18:42:07 2022 +0000 upstream: Use variable for diff options instead of unconditionally specifying "-rN". This will make life easier in -portable where not all diff's understand -N. OpenBSD-Regress-ID: 8b8a407115546be1c6d72d350b1e4f1f960d3cd3 commit f6d3ed9a8a9280cbb68d6a499850cfe810e92bd0 Author: Darren Tucker Date: Mon Oct 31 05:13:02 2022 +1100 OpenSSL dev branch is 302 not 320. While there, also accept 301 which it shat it was previously. commit 25c8a2bbcc10c493d27faea57c42a6bf13fa51f2 Author: djm@openbsd.org Date: Fri Oct 28 02:47:04 2022 +0000 upstream: put sshkey_check_rsa_length() back in sshkey.c to unbreak OPENSSL=no builds OpenBSD-Commit-ID: 99eec58abe382ecd14b14043b195ee1babb9cf6e commit 1192588546c29ceec10775125f396555ea71850f Author: djm@openbsd.org Date: Fri Oct 28 02:29:34 2022 +0000 upstream: allow ssh-keyscan(1) to accept CIDR address ranges, e.g. ssh-keyscan 192.168.0.0/24 If a CIDR range is passed, then it will be expanded to all possible addresses in the range including the all-0s and all-1s addresses. bz#976 feedback/ok markus@ OpenBSD-Commit-ID: ce6c5211f936ac0053fd4a2ddb415277931e6c4b commit 64af4209309461c79c39eda2d13f9d77816c6398 Author: Damien Miller Date: Fri Oct 28 12:54:35 2022 +1100 fix merge botch commit 27267642699342412964aa785b98afd69d952c88 Author: djm@openbsd.org Date: Fri Oct 28 00:44:44 2022 +0000 upstream: refactor sshkey_private_deserialize feedback/ok markus@ OpenBSD-Commit-ID: f5ca6932fdaf840a5e8250becb38315a29b5fc9f commit 2519a7077a9332f70935e5242ba91ee670ed6b87 Author: djm@openbsd.org Date: Fri Oct 28 00:44:17 2022 +0000 upstream: refactor sshkey_private_serialize_opt() feedback/ok markus@ OpenBSD-Commit-ID: 61e0fe989897901294efe7c3b6d670cefaf44cbd commit 11a768adf98371fe4e43f3b06014024c033385d5 Author: djm@openbsd.org Date: Fri Oct 28 00:43:30 2022 +0000 upstream: refactor certify feedback/ok markus@ OpenBSD-Commit-ID: 35d742992e223eaca3537e6fb3d3002c08eed4f6 commit 3fbc58bb249d967cc43ebdc554f6781bb73d4a58 Author: djm@openbsd.org Date: Fri Oct 28 00:43:08 2022 +0000 upstream: refactor sshkey_sign() and sshkey_verify() feedback/ok markus@ OpenBSD-Commit-ID: 368e662c128c99d05cc043b1308d2b6c71a4d3cc commit a1deb6cdbbe6afaab74ecb08fcb62db5739267be Author: djm@openbsd.org Date: Fri Oct 28 00:41:52 2022 +0000 upstream: refactor sshkey_from_blob_internal() feedback/ok markus@ OpenBSD-Commit-ID: 1f46c0cbb8060ee9666a02749594ad6658c8e283 commit 7d00799c935271ce89300494c5677190779f6453 Author: djm@openbsd.org Date: Fri Oct 28 00:41:17 2022 +0000 upstream: refactor sshkey_from_private() feedback/ok markus@ OpenBSD-Commit-ID: e5dbe7a3545930c50f70ee75c867a1e08b382b53 commit 262647c2e920492ca57f1b9320d74f4a0f6e482b Author: djm@openbsd.org Date: Fri Oct 28 00:39:29 2022 +0000 upstream: factor out key generation feedback/ok markus@ OpenBSD-Commit-ID: 5b4211bff4de8d9adb84bc72857a8c42c44e7ceb commit 401c74e7dc15eab60540653d2f94d9306a927bab Author: djm@openbsd.org Date: Fri Oct 28 00:38:58 2022 +0000 upstream: refactor and simplify sshkey_read() feedback/ok markus@ OpenBSD-Commit-ID: 0d93b7a56e31cd06a8bb0d2191d084ce254b0971 commit 591fed94e66a016acf87f4b7cd416ce812f2abe8 Author: djm@openbsd.org Date: Fri Oct 28 00:37:24 2022 +0000 upstream: factor out public key serialization feedback/ok markus@ OpenBSD-Commit-ID: a3570c4b97290c5662890aea7328d87f55939033 commit 1e78844ae2b2dc01ba735d5ae740904c57e13685 Author: djm@openbsd.org Date: Fri Oct 28 00:36:31 2022 +0000 upstream: factor out sshkey_equal_public() feedback/ok markus@ OpenBSD-Commit-ID: 1368ba114cb37732fe6ec3d89c7e6d27ea6fdc94 commit 25de1c01a8b9a2c8ab9b1da22444a03e89c982de Author: djm@openbsd.org Date: Fri Oct 28 00:35:40 2022 +0000 upstream: begin big refactor of sshkey Move keytype data and some of the type-specific code (allocation, cleanup, etc) out into each key type's implementation. Subsequent commits will move more, with the goal of having each key-*.c file owning as much of its keytype's implementation as possible. lots of feedback + ok markus@ OpenBSD-Commit-ID: 0f2b4334f73914344e9e5b3d33522d41762a57ec commit 445363433ba20b8a3e655b113858c836da46a1cb Author: djm@openbsd.org Date: Mon Oct 24 22:43:36 2022 +0000 upstream: Be more paranoid with host/domain names coming from the never write a name with bad characters to a known_hosts file. reported by David Leadbeater, ok deraadt@ OpenBSD-Commit-ID: ba9b25fa8b5490b49398471e0c9657b0cbc7a5ad commit 7190154de2c9fe135f0cc1ad349cb2fa45152b89 Author: djm@openbsd.org Date: Mon Oct 24 21:52:50 2022 +0000 upstream: regress test for unmatched glob characters; fails before previous commit but passes now. bz3488; prodded by dtucker@ OpenBSD-Regress-ID: 0cc5cc9ea4a6fd170dc61b9212f15badaafb3bbd commit a4821a592456c3add3cd325db433110cdaaa3e5c Author: djm@openbsd.org Date: Mon Oct 24 21:51:55 2022 +0000 upstream: when scp(1) is using the SFTP protocol for transport (the default), better match scp/rcp's handling of globs that don't match the globbed characters but do match literally (e.g. trying to transfer "foo.[1]"). Previously scp(1) in SFTP mode would not match these pathnames but legacy scp/rcp mode would. Reported by Michael Yagliyan in bz3488; ok dtucker@ OpenBSD-Commit-ID: d8a3773f53015ba811fddba7473769a2fd343e11 commit 18376847b8043ba967eabbe23692ef74c9a3fddc Author: jsg@openbsd.org Date: Thu Oct 13 09:09:28 2022 +0000 upstream: use correct type with sizeof ok djm@ OpenBSD-Commit-ID: d6c882c2e8a42ff831a5b3cbc2c961ecb2dd6143 commit 4a4883664d6b4e9e4e459a8cdc16bd8d4b735de9 Author: jmc@openbsd.org Date: Fri Oct 7 06:00:58 2022 +0000 upstream: ssh-agent.1: - use Nm not Xr for self-ref - while here, wrap a long line ssh-agent.c: - add -O to usage() OpenBSD-Commit-ID: 855dac4695cef22e96d69c53436496bc408ca389 commit 9fd2441113fce2a83fc7470968c3b27809cc7f10 Author: djm@openbsd.org Date: Fri Oct 7 04:06:26 2022 +0000 upstream: document "-O no-restrict-websafe"; spotted by Ross L Richardson OpenBSD-Commit-ID: fe9eaa50237693a14ebe5b5614bf32a02145fe8b commit 614252b05d70f798a0929b1cd3d213030ad4d007 Author: Darren Tucker Date: Tue Oct 18 06:29:16 2022 +1100 OpenSSL dev branch now identifies as 3.2.0. commit 195e5a65fd793a738ea8451ebfdd1919db5aff3e Author: Damien Miller Date: Mon Oct 17 09:41:47 2022 +1100 revert c64b62338b4 and guard POLL* defines instead c64b62338b4 broke OSX builds, which do have poll.h but lack ppoll(2) Spotted by dtucker commit bc2e480d99613bd59720edae244d1764636544c4 Author: Damien Miller Date: Fri Oct 14 14:52:22 2022 +1100 undef _get{short,long} before redefining commit 5eb796a369c64f18d55a6ae9b1fa9b35eea237fb Author: Harmen Stoppels Date: Thu Oct 13 16:08:46 2022 +0200 Fix snprintf configure test for clang 15 Clang 15 -Wimplicit-int defaults to an error in C99 mode and above. A handful of tests have "main(..." and not "int main(..." which caused the tests to produce incorrect results. commit c64b62338b46ffa08839f05f21ad69fa6234dc17 Author: Damien Miller Date: Mon Oct 10 12:32:43 2022 +1100 skip bsd-poll.h if poll.h found; ok dtucker commit 5ee2b8ccfcf4b606f450eb0ff2305e311f68b0be Author: djm@openbsd.org Date: Thu Oct 6 22:42:37 2022 +0000 upstream: honour user's umask if it is more restrictive then the ssh default (022); based on patch from Alex Henrie, ok dtucker@ deraadt@ OpenBSD-Commit-ID: fe1b9e15fc9a4f49fc338e848ce14d8727abe82d commit a75cffc2700cebd3e2dd9093f7f7388d2be95cb7 Author: Darren Tucker Date: Fri Oct 7 03:54:56 2022 +1100 Add LibreSSL 3.6.0 to test suite. While there, bump OpenSSL to latest 1.1.1q release. commit fcc0f0c0e96a30076683fea9a7c9eedc72931742 Author: Darren Tucker Date: Thu Oct 6 21:18:16 2022 +1100 Add 9.1 branch to CI status page. commit ef211eee63821d894a8bf81f22bfba9f6899d0fe Author: Darren Tucker Date: Tue Oct 4 23:20:23 2022 +1100 Test commits to all branches of portable. Only test OpenBSD upstream on commits to master since that's what it tracks. commit fe646de03cafb6593ff4e4954bca9ec4b4b753a8 Author: Damien Miller Date: Wed Oct 5 03:47:26 2022 +1100 whitespace at EOL commit a6e1852d10c63a830196e82168dadd957aaf28ec Author: Damien Miller Date: Wed Oct 5 03:40:01 2022 +1100 mention libfido2 autodetection commit 7360c2c206f33d309edbaf64036c96fadf74d640 Author: Damien Miller Date: Wed Oct 5 03:37:36 2022 +1100 remove mention of --with-security-key-builtin it is enabled by default when libfido2 is installed commit 0ffb46f2ee2ffcc4daf45ee679e484da8fcf338c Author: Damien Miller Date: Tue Oct 4 01:51:42 2022 +1100 update .depend commit 657e676ff696c7bb787bffb0e249ea1be3b474e1 Author: Damien Miller Date: Tue Oct 4 01:45:52 2022 +1100 update release notes URL commit f059da2b29840c0f048448809c317ce2ae014da7 Author: Damien Miller Date: Tue Oct 4 01:45:41 2022 +1100 crank versions in RPM spec files commit b51f3f172d87cbdb80ca4eb7b2149e56a7647557 Author: djm@openbsd.org Date: Mon Sep 26 22:18:40 2022 +0000 upstream: openssh-9.1 OpenBSD-Commit-ID: 5a467b2ee81da01a86adf1ad93b62b1728494e56 commit 4cf8d0c0f3030f594a238bab21a0695735515487 Author: dtucker@openbsd.org Date: Wed Sep 21 22:26:50 2022 +0000 upstream: Fix typo. From AlexanderStohr via github PR#343. OpenBSD-Commit-ID: a134c9b4039e48803fc6a87f955b0f4a03181497 commit 8179fed3264d5919899900ed8881d5f9bb57ca33 Author: djm@openbsd.org Date: Mon Sep 19 21:39:16 2022 +0000 upstream: add RequiredRSASize to the list of keywords accepted by -o; spotted by jmc@ OpenBSD-Commit-ID: fe871408cf6f9d3699afeda876f8adbac86a035e commit 5f954929e9f173dd1e279e07d0e8b14fa845814d Author: Damien Miller Date: Mon Sep 19 20:59:34 2022 +1000 no need for glob.h here it also causes portability problems commit 03d94a47207d58b3db37eba4f87eb6ae5a63168a Author: Damien Miller Date: Mon Sep 19 20:59:04 2022 +1000 avoid Wuninitialized false positive in gcc-12ish commit 9d952529113831fb3071ab6e408d2726fd72e771 Author: djm@openbsd.org Date: Mon Sep 19 10:46:00 2022 +0000 upstream: use users-groups-by-id@openssh.com sftp-server extension (when available) to fill in user/group names for directory listings. Implement a client-side cache of see uid/gid=>user/group names. ok markus@ OpenBSD-Commit-ID: f239aeeadfa925a37ceee36ee8b256b8ccf4466e commit 8ff680368b0bccf88ae85d4c99de69387fbad7a6 Author: djm@openbsd.org Date: Mon Sep 19 10:43:12 2022 +0000 upstream: sftp client library support for users-groups-by-id@openssh.com; ok markus@ OpenBSD-Commit-ID: ddb2f33a2da6349a9a89a8b5bcb9ca7c999394de commit 488f6e1c582212c2374a4bf8cd1b703d2e70fb8b Author: djm@openbsd.org Date: Mon Sep 19 10:41:58 2022 +0000 upstream: extend sftp-common.c:extend ls_file() to support supplied user/group names; ok markus@ OpenBSD-Commit-ID: c70c70498b1fdcf158531117e405b6245863bfb0 commit 74b77f7497dba3a58315c8f308883de448078057 Author: djm@openbsd.org Date: Mon Sep 19 10:40:52 2022 +0000 upstream: sftp-server(8): add a "users-groups-by-id@openssh.com" extension request that allows the client to obtain user/group names that correspond to a set of uids/gids. Will be used to make directory listings more useful and consistent in sftp(1). ok markus@ OpenBSD-Commit-ID: 7ebabde0bcb95ef949c4840fe89e697e30df47d3 commit 231a346c0c67cc7ca098360f9a554fa7d4f1eddb Author: djm@openbsd.org Date: Mon Sep 19 08:49:50 2022 +0000 upstream: better debugging for connect_next() OpenBSD-Commit-ID: d16a307a0711499c971807f324484ed3a6036640 commit 1875042c52a3b950ae5963c9ca3774a4cc7f0380 Author: djm@openbsd.org Date: Sat Sep 17 10:34:29 2022 +0000 upstream: Add RequiredRSASize for sshd(8); RSA keys that fall beneath this limit will be ignored for user and host-based authentication. Feedback deraadt@ ok markus@ OpenBSD-Commit-ID: 187931dfc19d51873df5930a04f2d972adf1f7f1 commit 54b333d12e55e6560b328c737d514ff3511f1afd Author: djm@openbsd.org Date: Sat Sep 17 10:33:18 2022 +0000 upstream: add a RequiredRSASize for checking RSA key length in ssh(1). User authentication keys that fall beneath this limit will be ignored. If a host presents a host key beneath this limit then the connection will be terminated (unfortunately there are no fallbacks in the protocol for host authentication). feedback deraadt, Dmitry Belyavskiy; ok markus@ OpenBSD-Commit-ID: 430e339b2a79fa9ecc63f2837b06fdd88a7da13a commit 07d8771bacfefbcfb37fa8a6dc6103bcc097e0ab Author: djm@openbsd.org Date: Sat Sep 17 10:30:45 2022 +0000 upstream: Add a sshkey_check_rsa_length() call for checking the length of an RSA key; ok markus@ OpenBSD-Commit-ID: de77cd5b11594297eda82edc594b0d32b8535134 commit 3991a0cf947cf3ae0f0373bcec5a90e86a7152f5 Author: djm@openbsd.org Date: Sat Sep 17 10:11:29 2022 +0000 upstream: actually hook up restrict_websafe; the command-line flag was never actually used. Spotted by Matthew Garrett OpenBSD-Commit-ID: 0b363518ac4c2819dbaa3dfad4028633ab9cdff1 commit 30b2a7e4291fb9e357f80a237931ff008d686d3b Author: djm@openbsd.org Date: Fri Sep 16 06:55:37 2022 +0000 upstream: correct error value OpenBSD-Commit-ID: 780efcbad76281f11f14b2a5ff04eb6db3dfdad4 commit ac1ec9545947d9f9657259f55d04cb49d3a94c8a Author: djm@openbsd.org Date: Fri Sep 16 03:33:14 2022 +0000 upstream: sftp: Be a bit more clever about completions There are commands (e.g. "get" or "put") that accept two arguments, a local path and a remote path. However, the way current completion is written doesn't take this distinction into account and always completes remote or local paths. By expanding CMD struct and "cmds" array this distinction can be reflected and with small adjustment to completer code the correct path can be completed. By Michal Privoznik, ok dtucker@ OpenBSD-Commit-ID: 1396d921c4eb1befd531f5c4a8ab47e7a74b610b commit 590db83384f9d99fc51c84505792d26d1ef60df9 Author: djm@openbsd.org Date: Fri Sep 16 03:13:34 2022 +0000 upstream: sftp: Don't attempt to complete arguments for non-existent commands If user entered a non-existent command (e.g. because they made a typo) there is no point in trying to complete its arguments. Skip calling complete_match() if that's the case. From Michal Privoznik OpenBSD-Commit-ID: cf39c811a68cde2aeb98fc85addea4000ef6b07a commit ff9809fdfd1d9a91067bb14a77d176002edb153c Author: djm@openbsd.org Date: Wed Sep 14 00:14:37 2022 +0000 upstream: sk_enroll: never drop SSH_SK_USER_VERIFICATION_REQD flag from response Now that all FIDO signing calls attempt first without PIN and then fall back to trying PIN only if that attempt fails, we can remove the hack^wtrick that removed the UV flag from the keys returned during enroll. By Corinna Vinschen OpenBSD-Commit-ID: 684517608c8491503bf80cd175425f0178d91d7f commit 940dc10729cb5a95b7ee82c10184e2b9621c8a1d Author: djm@openbsd.org Date: Wed Sep 14 00:13:13 2022 +0000 upstream: a little extra debugging OpenBSD-Commit-ID: edf1601c1d0905f6da4c713f4d9cecc7d1c0295a commit 4b5f91cb959358141181b934156513fcb8a6c1e3 Author: djm@openbsd.org Date: Wed Sep 14 00:02:03 2022 +0000 upstream: ssh-agent: attempt FIDO key signing without PIN and use the error to determine whether a PIN is required and prompt only if necessary. from Corinna Vinschen OpenBSD-Commit-ID: dd6be6a0b7148608e834ee737c3479b3270b00dd commit 113523bf0bc33600b07ebb083572c8c346b6fdf4 Author: jmc@openbsd.org Date: Sun Sep 11 06:38:11 2022 +0000 upstream: .Li -> .Vt where appropriate; from josiah frentsos, tweaked by schwarze ok schwarze OpenBSD-Commit-ID: 565046e3ce68b46c2f440a93d67c2a92726de8ed commit 86af013b56cecb5ee58ae0bd9d495cd586fc5918 Author: jsg@openbsd.org Date: Sat Sep 10 08:50:53 2022 +0000 upstream: fix repeated words ok miod@ jmc@ OpenBSD-Commit-ID: 6765daefe26a6b648cc15cadbbe337596af709b7 commit 0ba39b93b326a7d5dfab776cc9b9d326161a9b16 Author: djm@openbsd.org Date: Fri Sep 9 03:31:42 2022 +0000 upstream: notifier_complete(NULL, ...) is a noop, so no need to test that ctx!=NULL; from Corinna Vinschen OpenBSD-Commit-ID: ade2f2e9cc519d01a586800c25621d910bce384a commit be197635329feb839865fdc738e34e24afd1fca8 Author: Sam James Date: Thu Sep 8 02:49:29 2022 +0100 openbsd-compat/bsd-asprintf: add include for vsnprintf Fixes the following build failure with Clang 15 on musl: ``` bsd-asprintf.c:51:8: error: call to undeclared library function 'vsnprintf' with type 'int (char *, unsigned long, const char *, struct __va_list_tag *)'; ISO C99 and laterclang -O2 -pipe -fdiagnostics-color=always -frecord-gcc-switches -pipe -Wunknown-warning-option -Qunused-arguments -Wall -Wpointer-arith -Wuninitialized -Wsign-compare -Wformat-security -Wsizeof-pointer-memaccess -Wno-pointer-sign -Wno-unused-result -Wmisleading-indentation -Wbitwise-instead-of-logical -fno-strict-aliasing -mretpoline -ftrapv -fzero-call-used-regs=all -fno-builtin-memset -fstack-protector-strong -fPIE -I. -I. -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_DEFAULT_SOURCE -DSSHDIR=\"/etc/ssh\" -D_PATH_SSH_PROGRAM=\"/usr/bin/ssh\" -D_PATH_SSH_ASKPASS_DEFAULT=\"/usr/lib/misc/ssh-askpass\" -D_PATH_SFTP_SERVER=\"/usr/lib/misc/sftp-server\" -D_PATH_SSH_KEY_SIGN=\"/usr/lib/misc/ssh-keysign\" -D_PATH_SSH_PKCS11_HELPER=\"/usr/lib/misc/ssh-pkcs11-helper\" -D_PATH_SSH_SK_HELPER=\"/usr/lib/misc/ssh-sk-helper\" -D_PATH_SSH_PIDDIR=\"/run\" -D_PATH_PRIVSEP_CHROOT_DIR=\"/var/empty\" -DHAVE_CONFIG_H -c cipher-aes.c -o cipher-aes.o do not support implicit function declarations [-Wimplicit-function-declaration] ret = vsnprintf(string, INIT_SZ, fmt, ap2); ^ bsd-asprintf.c:51:8: note: include the header or explicitly provide a declaration for 'vsnprintf' 1 error generated. ``` commit 6cb6f660bb35f77a0456dd2581ddf39c29398a5e Author: Darren Tucker Date: Fri Sep 2 16:43:27 2022 +1000 Remove DEF_WEAK, it's already in defines.h. commit ce39e7d8b70c4726defde5d3bc4cb7d40d131153 Author: Darren Tucker Date: Fri Sep 2 14:28:14 2022 +1000 Resync arc4random with OpenBSD. This brings us up to current, including djm's random-reseeding change, as prompted by logan at cyberstorm.mu in bz#3467. It brings the platform-specific hooks from LibreSSL Portable, simplified to match our use case. ok djm@. commit beaddde26f30e2195b8aa4f3193970e140e17305 Author: Darren Tucker Date: Fri Sep 2 14:20:04 2022 +1000 Move OPENBSD ORIGINAL marker. Putting this after the copyright statement (which doesn't change) instead of before the version identifier (which does) prevents merge conflicts when resyncing changes. commit c83e467ead67a8cb48ef4bec8085d6fb880a2ff4 Author: Darren Tucker Date: Fri Sep 2 14:17:28 2022 +1000 Remove arc4random_uniform from arc4random.c This was previously moved into its own file (matching OpenBSD) which prematurely committed in commit 73541f2. commit 5f45c2395c60865e59fa44152ff1d003a128c5bc Author: djm@openbsd.org Date: Fri Sep 2 04:20:02 2022 +0000 upstream: sk-usbhid: fix key_lookup() on tokens with built-in UV explicitly test whether the token performs built-in UV (e.g. biometric tokens) and enable UV in that case. From Pedro Martelletto via GHPR#388 OpenBSD-Commit-ID: 007eb7e387d27cf3029ab06b88224e03eca62ccd commit 03277a4aa49b80af541a3e691f264c0c0d8f9cec Author: Darren Tucker Date: Wed Aug 31 20:26:30 2022 +1000 Move sftp from valgrind-2 to 3 to rebalance. commit fcf5365da69c516817321ba89c3a91df98d098df Author: djm@openbsd.org Date: Wed Aug 31 02:56:40 2022 +0000 upstream: whitespace OpenBSD-Commit-ID: c2bcbf93610d3d62ed206cdf9bf9ff98c6aaf232 commit e60136a3d7a223dd8e84ba8a6895bc3142360993 Author: Damien Miller Date: Mon Aug 29 13:27:45 2022 +1000 additional keys commit 2b02dcb505288c462d1b5dd1ac04e603d01340eb Author: Damien Miller Date: Mon Aug 29 13:23:43 2022 +1000 cross-sign allowed_signers with PGP key Provides continuity of trust from legacy PGP release key to the SSHSIG signing keys that we will use henceforth for git signing. commit 51b345f177ae981b8755f6bdf8358b1cc5e83d67 Author: Darren Tucker Date: Sat Aug 27 21:49:27 2022 +1000 Add libcrypt-devel to cygwin-release deps. Based on feedback from vinschen at redhat.com. commit 9f81736cf16dd8dda1c8942f1973a5f80b8cd78c Author: Darren Tucker Date: Sat Aug 27 09:37:40 2022 +1000 Add Windows 2022 test targets. commit 85e1a69243f12be8520438ad6a3cfdc0b7fcbb2d Author: Darren Tucker Date: Fri Aug 26 16:26:06 2022 +1000 Add cygwin-release test target. This also moves the cygwin package install from the workflow file to setup_ci.sh so that we can install different sets of Cygwin packages for different test configs. commit 92382dbe8bf9ea1225b16858f9b9b208c15c7e8d Author: djm@openbsd.org Date: Fri Aug 26 08:16:27 2022 +0000 upstream: whitespace OpenBSD-Commit-ID: a5d015efbfd228dc598ffdef612d2da3a579e5d8 commit 70a5de0a50e84d7250eb4e4537f765599f64c4af Author: djm@openbsd.org Date: Fri Aug 26 08:12:56 2022 +0000 upstream: whitespace OpenBSD-Commit-ID: d297e4387935d4aef091c5e9432578c2e513f538 commit 3a683a19fd116ea15ebf8aa13d02646cceb302a9 Author: Damien Miller Date: Fri Aug 26 14:23:55 2022 +1000 initial list of allowed signers commit 6851f4b8c3fc1b3e1114c56106e4dc31369c8513 Author: Darren Tucker Date: Fri Aug 19 17:22:18 2022 +1000 Install Cygwin packages based on OS not config. commit f96480906893ed93665df8cdf9065865c51c1475 Author: djm@openbsd.org Date: Fri Aug 19 06:07:47 2022 +0000 upstream: attemp FIDO key signing without PIN and use the error code returned to fall back only if necessary. Avoids PIN prompts for FIDO tokens that don't require them; part of GHPR#302 OpenBSD-Commit-ID: 4f752aaf9f2e7c28bcaaf3d4f8fc290131bd038e commit 5453333b5d28e313284cb9aae82899704103f98d Author: djm@openbsd.org Date: Fri Aug 19 05:53:28 2022 +0000 upstream: remove incorrect check that can break enrolling a resident key (introduced in r1.40) OpenBSD-Commit-ID: 4cab364d518470e29e624af3d3f9ffa9c92b6f01 commit ff89b1bed80721295555bd083b173247a9c0484e Author: dtucker@openbsd.org Date: Fri Aug 19 04:02:46 2022 +0000 upstream: Strictly enforce the maximum allowed SSH2 banner size in ssh-keyscan and prevent a one-byte buffer overflow. Patch from Qualys, ok djm@ OpenBSD-Commit-ID: 6ae664f9f4db6e8a0589425f74cd0bbf3aeef4e4 commit 1b470b9036639cef4f32fb303bb35ea0b711178d Author: Darren Tucker Date: Fri Aug 19 15:18:09 2022 +1000 Fix cygwin conditional steps. commit fd6ee741ab16714b7035d60aca924123ba28135a Author: Darren Tucker Date: Fri Aug 19 15:12:57 2022 +1000 Add a bit more debug output. commit a9305c4c739f4d91a3d3a92c0b6d4949404a36c5 Author: Darren Tucker Date: Fri Aug 12 15:08:47 2022 +1000 Add Cygwin (on windows-2019) test target. In addition to installing the requisite Cygwin packages, we also need to explicitly invoke "sh" for steps that run other scripts since the runner environment doesn't understand #! paths. commit 5062ad48814b06162511c4f5924a33d97b6b2566 Author: djm@openbsd.org Date: Fri Aug 19 03:06:30 2022 +0000 upstream: double free() in error path; from Eusgor via GHPR333 OpenBSD-Commit-ID: 39f35e16ba878c8d02b4d01d8826d9b321be26d4 commit 5a5c580b48fc6006bdfa731fc2f6d4945c2c0e4e Author: Darren Tucker Date: Thu Aug 18 21:36:39 2022 +1000 Check for perms to run agent-getpeereid test. Ubuntu 22.04 defaults to private home dirs which prevents "nobody" running ssh-add during the agent-getpeereid test. Check for this and add the necessary permissions. commit cd06a76b7ccc706e2bb4f1cc4aa9e9796a28a812 Author: Damien Miller Date: Wed Aug 17 16:04:16 2022 +1000 on Cygwin, prefer WinHello FIDO device If no FIDO device was explictly specified, then prefer the windows://hello FIDO device. An exception to this is when probing resident FIDO keys, in which case hardware FIDO devices are preferred. commit 47f72f534ac5cc2cd3027675a3df7b00a8f77575 Author: djm@openbsd.org Date: Wed Aug 17 06:01:57 2022 +0000 upstream: add an extra flag to sk_probe() to indicate whether we're probing for a FIDO resident key or not. Unused here, but will make like easier for portable OpenBSD-Commit-ID: 432c8ff70e270378df9dbceb9bdeaa5b43b5a832 commit edb0bcb3c79b16031dc87a8e57aecc3c4a3414f0 Author: jmc@openbsd.org Date: Tue Aug 16 20:24:08 2022 +0000 upstream: use .Cm for "sign"; from josiah frentsos OpenBSD-Commit-ID: 7f80a53d54857ac6ae49ea6ad93c5bd12231d1e4 commit cccb011e130cbbac538b1689d10e4a067298df8b Author: Corinna Vinschen Date: Thu Aug 11 20:19:35 2022 +0200 Revert "check_sk_options: add temporary WinHello workaround" Cygwin now comes with libfido2 1.11.0, so this workaround isn't required anymore. This reverts commit 242c044ab111a37aad3b0775727c36a4c5f0102c. Signed-off-by: Corinna Vinschen commit 9468cd7cf9d989dfa2ac20e2a0268ba6e93bfa5a Author: Corinna Vinschen Date: Thu Aug 11 20:18:17 2022 +0200 fido_dev_is_winhello: return 0, not "false" "false" is not used anywhere in OpenSSH, so return 0 like everywhere else. Signed-off-by: Corinna Vinschen commit 730a80609472ee0451c99482d75c9c41f3ebc42d Author: djm@openbsd.org Date: Fri Aug 12 05:20:28 2022 +0000 upstream: sftp-server: support home-directory request Add support to the sftp-server for the home-directory extension defined in draft-ietf-secsh-filexfer-extensions-00. This overlaps a bit with the existing expand-path@openssh.com, but uses a more official protocol name, and so is a bit more likely to be implemented by non-OpenSSH clients. From Mike Frysinger, ok dtucker@ OpenBSD-Commit-ID: bfc580d05cc0c817831ae7ecbac4a481c23566ab commit 5e820bf79ce3ce99ef7e98b0ab642b0a0a4f396c Author: Darren Tucker Date: Fri Aug 12 14:56:55 2022 +1000 Replace deprecated ubuntu-18.04 runners with 22.04 commit 87b0d9c1b789d3ff958ec45df2ac912e24461bae Author: Darren Tucker Date: Thu Aug 11 22:48:23 2022 +1000 Add a timegm implementation from Heimdal via Samba. Fixes build on (at least Solaris 10). commit d0c4fa58594577994921b593f10037c5282597ca Author: Darren Tucker Date: Thu Aug 11 14:23:58 2022 +1000 Rerun tests if any .github config file changes. commit 113fe6c77ab43769fc61e953d07cb619fd7ea54b Author: Darren Tucker Date: Thu Aug 11 13:33:51 2022 +1000 Skip hostbased during Valgrind tests. Valgrind doesn't let ssh exec ssh-keysign (because it's setuid) so skip it during the Valgrind based tests. See https://bugs.kde.org/show_bug.cgi?id=119404 for a discussion of this (ironically there the problematic binary was ssh(1) back when it could still be setuid). commit b98a42afb69d60891eb0488935990df6ee571c4d Author: djm@openbsd.org Date: Thu Aug 11 01:57:50 2022 +0000 upstream: add some tests for parse_absolute_time(), including cases where it is forced to the UTC timezone. bz3468 ok dtucker OpenBSD-Regress-ID: ea07ca31c2f3847a38df028ca632763ae44e8759 commit ec1ddb72a146fd66d18df9cd423517453a5d8044 Author: djm@openbsd.org Date: Thu Aug 11 01:56:51 2022 +0000 upstream: allow certificate validity intervals, sshsig verification times and authorized_keys expiry-time options to accept dates in the UTC time zone in addition to the default of interpreting them in the system time zone. YYYYMMDD and YYMMDDHHMM[SS] dates/times will be interpreted as UTC if suffixed with a 'Z' character. Also allow certificate validity intervals to be specified in raw seconds-since-epoch as hex value, e.g. -V 0x1234:0x4567890. This is intended for use by regress tests and other tools that call ssh-keygen as part of a CA workflow. bz3468 ok dtucker OpenBSD-Commit-ID: 454db1cdffa9fa346aea5211223a2ce0588dfe13 commit 4df246ec75751da7eb925e1880498300d8bda187 Author: Darren Tucker Date: Thu Aug 11 10:23:55 2022 +1000 Fix conditional for running hostbased tests. commit 2580916e48721802220c61ce9e0df1297c00bc07 Author: Damien Miller Date: Thu Aug 11 08:58:28 2022 +1000 fix SANDBOX_SECCOMP_FILTER_DEBUG commit fdbd5bf507fc271ff813714fab8a72ff2c6cb5ca Author: Darren Tucker Date: Wed Aug 10 17:35:52 2022 +1000 Test hostbased auth on github runners. commit 7e2f51940ba48a1c0fae1107801ea643fa83c971 Author: Darren Tucker Date: Wed Aug 10 17:25:24 2022 +1000 Rename our getentropy to prevent possible loops. Since arc4random seeds from getentropy, and we use OpenSSL for that if enabled, there's the possibility that if we build on a system that does not have getentropy then run on a system that does have it, then OpenSSL could end up calling our getentropy and getting stuck in a loop. Pointed out by deraadt@, ok djm@ commit 7a01f61be8d0aca0e975e7417f26371495fe7674 Author: Darren Tucker Date: Mon Aug 8 12:17:04 2022 +1000 Actually put HAVE_STDINT_H around the stdint.h. commit 73541f29f0b50480da6c20dceb7a7191bd8ea7d3 Author: Darren Tucker Date: Mon Aug 8 10:30:34 2022 +1000 Give unused param a name. Fixes builds on platforms that do have fido2 but don't have fido_dev_is_winhello. commit 2a108c0ea960381bd9b14ee0d84e818a23df4482 Author: djm@openbsd.org Date: Fri Aug 5 05:01:40 2022 +0000 upstream: don't prompt for FIDO passphrase before attempting to enroll the credential, just let the enroll operating fail and we'll attempt to get a PIN anyway. Might avoid some unneccessary PIN prompts. Part of GHPR#302 from Corinna Vinschen; ok dtucker@ OpenBSD-Commit-ID: bd5342ffc353ee37d39617906867c305564d1ce2 commit 2886975c0ad9244e60dc5e4be34fde3aa573a4b5 Author: Corinna Vinschen Date: Fri Feb 11 14:33:41 2022 +0100 sk_sign: set FIDO2 uv attribute explicitely for WinHello WinHello via libfido2 performs user verification by default. However, if we stick to that, there's no way to differentiate between keys created with or without "-O verify-required". Set FIDO2 uv attribute explicitely to FIDO_OPT_FALSE, then check if user verification has been requested. Signed-off-by: Corinna Vinschen commit 242c044ab111a37aad3b0775727c36a4c5f0102c Author: Corinna Vinschen Date: Tue Feb 15 11:28:08 2022 +0100 check_sk_options: add temporary WinHello workaround Up to libfido 1.10.0, WinHello advertises "clientPin" rather than "uv" capability. This is fixed in 1.11.0. For the time being, workaround it here. Signed-off-by: Corinna Vinschen commit 78774c08cc4b4997382975b0f414a86e06b6780c Author: Corinna Vinschen Date: Thu Feb 10 18:19:29 2022 +0100 compat code for fido_dev_is_winhello() Signed-off-by: Corinna Vinschen commit 3d3a932a019aedfb891e0779bb4990cd5008a390 Author: Darren Tucker Date: Fri Aug 5 13:12:27 2022 +1000 Factor out getrnd() and rename to getentropy(). Factor out the arc4random seeding into its own file and change the interface to match getentropy. Use native getentropy if available. This will make it easier to resync OpenBSD changes to arc4random. Prompted by bz#3467, ok djm@. commit 9385d277b787403be9dfcb229cf372202496d2f3 Author: Darren Tucker Date: Thu Aug 4 18:55:48 2022 +1000 Include CHANNEL and FIDO2 libs in configure output commit 141535b904b6fba01724444f38193a8599201f82 Author: djm@openbsd.org Date: Mon Aug 1 11:09:26 2022 +0000 upstream: avoid double-free in error path introduced in r1.70; report and fix based on GHPR#332 by v-rzh ok dtucker@ OpenBSD-Commit-ID: 3d21aa127b1f37cfc5bdc21461db369a663a951f commit dba7099ffcba3ca07b3946f017ba6a4c3158d9b1 Author: Darren Tucker Date: Wed Jul 27 18:40:12 2022 +1000 Remove deprecated MacOS 10.15 runners. commit 722a56439aa5972c830e4a9a724cf52aff4a950a Author: Darren Tucker Date: Wed Jul 27 18:31:14 2022 +1000 Move stale-configure check as early as possible. We added a check in Makefile to catch the case where configure needs to be rebuilt, however this did not happen until a build was attempted in which case all of the work done by configure was wasted. Move this check to the start of configure to catch it as early as possible. ok djm@ commit 099d6b56288b421ba38531d26dc1bd6bb685e311 Author: Darren Tucker Date: Fri Jul 22 10:47:19 2022 +1000 Move libcrypto into CHANNELLIBS. This will result in sftp, sftp-server and scp no longer being linked against libcrypto. ok djm@ commit 1bdf86725b77733bb5f17c54888b88a10b2f6538 Author: Darren Tucker Date: Fri Jul 22 10:45:47 2022 +1000 Remove seed_rng calls from scp, sftp, sftp-server. These binaries don't use OpenSSL's random functions. The next step will be to stop linking them against libcrypto. ok djm@ commit d73f77b8cb9b422f1ac4facee7890aa10ff2bc21 Author: Darren Tucker Date: Fri Jul 22 09:51:51 2022 +1000 Group libcrypto and PRNGD checks together. They're related more than the libcrypt or libiaf checks which are currently between them. ok djm@ commit f117e372b3f42f2fbdb0a578d063b2609ab58e1f Author: Darren Tucker Date: Fri Jul 22 09:24:45 2022 +1000 Do not link scp, sftp and sftp-server w/ zlib. Some of our binaries (eg sftp, sftp-server, scp) do not interact with the channels code and thus do use libraries such as zlib and libcrypto although they are linked with them. This adds a CHANNELLIBS and starts by moving zlib into it, which means the aformentioned binaries are no longer linked against zlib. ok djm@ commit 800c2483e68db38bd1566ff69677124be974aceb Author: Darren Tucker Date: Mon Jul 25 21:49:04 2022 +1000 Remove workarounds for OpenSSL missing AES-CTR. We have some compatibility hacks that were added to support OpenSSL versions that do not support AES CTR mode. Since that time, however, the minimum OpenSSL version that we support has moved to 1.0.1 which *does* have CTR, so this is no longer needed. ok djm@ commit b7c56b65c12f51fe0dbae798d19c8f58224a5d95 Author: Darren Tucker Date: Mon Jul 25 21:43:00 2022 +1000 Remove workarounds for OpenSSL missing AES-GCM. We have some compatibility hacks that were added to support OpenSSL versions that do not support AES GCM mode. Since that time, however, the minimum OpenSSL version that we support has moved to 1.0.1 which *does* have GCM, so this is no longer needed. ok djm@ commit 5a4a9f7a968fbf92cc1eac519c65638e79ae9f1f Author: dtucker@openbsd.org Date: Mon Jul 25 07:12:45 2022 +0000 upstream: Restore missing "!" in TEST_SSH_ELAPSED_TIMES test. OpenBSD-Regress-ID: 38783f9676ec348c5a792caecee9a16e354b37b0 commit 0ff886be132299386cc29d87c2aa16ff68a1aa08 Author: dtucker@openbsd.org Date: Sun Jul 24 23:29:10 2022 +0000 upstream: Test TEST_SSH_ELAPSED_TIMES for empty string not executable. No-op on most platforms but should prevent warnings in -portable on systems that don't have 'date %s'. OpenBSD-Regress-ID: e39d79867b8065e33d0c5926fa1a31f85659d2a4 commit f69319ad8ad1dd50f90bbcf5912e11cc8ed3e037 Author: Darren Tucker Date: Sat Jul 23 14:38:22 2022 +1000 Convert "have_prog" function into "which". "which" and its behaviour is not standardized, so convert the existing have_prog function into "which" so we can rely on it being available and what its semantics are. Add a have_prog wrapper that maintains the existing behaviour. commit ea7ecc2c3ae39fdf5c6ad97b7bc0b47a98847f43 Author: Darren Tucker Date: Sat Jul 23 14:36:38 2022 +1000 Skip scp3 test if there's no scp on remote path. scp -3 ends up using the scp that's in the remote path and will fail if one is not available. Based on a patch from rapier at psc.edu. commit c46f6fed419167c1671e4227459e108036c760f8 Author: Damien Miller Date: Wed Jul 20 13:39:14 2022 +1000 crank SSH_SK_VERSION_MAJOR in sk-dummy.so commit f208e3b9ffb5ee76cf9c95df7ff967adc7f51c7d Author: djm@openbsd.org Date: Wed Jul 20 03:33:22 2022 +0000 upstream: ssh-keygen: fix touch prompt, pin retries; part of GHPR329 from Pedro Martelletto OpenBSD-Commit-ID: 75d1005bd2ef8f29fa834c90d2684e73556fffe8 commit 8638a2ce7e90c8a51d9af3143404282126c524f8 Author: djm@openbsd.org Date: Wed Jul 20 03:31:42 2022 +0000 upstream: sk-usbhid: preserve error code returned by key_lookup() it conveys useful information, such as the supplied pin being wrong. Part of GHPR329 from Pedro Martelletto OpenBSD-Commit-ID: c0647eb9290f793add363d81378439b273756c1b commit 9ab929ca2d820520327b41929372bcb9e261534c Author: djm@openbsd.org Date: Wed Jul 20 03:29:14 2022 +0000 upstream: when enrolling a resident key on a security token, check if a credential with matching application and user ID strings already exists. if so, prompt the user for confirmation before overwriting the credential. patch from Pedro Martelletto via GHPR329 NB. cranks SSH_SK_VERSION_MAJOR, so any third-party FIDO middleware implementations will need to adjust OpenBSD-Commit-ID: e45e9f1bf2b2f32d9850669e7a8dbd64acc5fca4 commit 5bcfc788b38d5b64e4c347bdc04bd9a01bbc36da Author: djm@openbsd.org Date: Wed Jul 20 03:13:04 2022 +0000 upstream: pull passphrase reading and confirmation into a separate function so it can be used for FIDO2 PINs; no functional change OpenBSD-Commit-ID: bf34f76b8283cc1d3f54633e0d4f13613d87bb2f commit eb679e2959bdb15454eb94751930eb4c9110da94 Author: Darren Tucker Date: Fri Jul 15 21:31:48 2022 +1000 Move vmshutdown to first step. If a previous run on a physical runner has failed to clean up, the next run will fail because it'll try to check out the code to a broken directory mount. Make cleanup the first step. commit 46b91b70ff3cb9c147e2875ef5dc609fd64c0c96 Author: Darren Tucker Date: Fri Jul 15 20:25:27 2022 +1000 Rename bbone test target to ARM. commit 751d22cdeffed9fe921db78eedc32a29f9e80510 Author: Darren Tucker Date: Fri Jul 15 13:37:29 2022 +1000 Add AUDIT_ARCH_PPC to supported seccomp arches. Patch from dries.deschout at dodeco.eu. commit a061792a6e8d235fc40a9b5d4c22a1762bb75a7b Author: Darren Tucker Date: Thu Jul 14 19:20:24 2022 +1000 Remove unintended changes. I inadvertently included a couple of local changes with the OpenSSL 3.0.4 change. Revert, anything that should be there will be committed separately. commit 527cb43fa1b4e55df661feabbac51b8e608b6519 Author: Darren Tucker Date: Thu Jul 14 11:22:08 2022 +1000 Return ERANGE from getcwd() if buffer size is 1. If getcwd() is supplied a buffer size of exactly 1 and a path of "/", it could result in a nul byte being written out of array bounds. POSIX says it should return ERANGE if the path will not fit in the available buffer (with terminating nul). 1 byte cannot fit any possible path with its nul, so immediately return ERANGE in that case. OpenSSH never uses getcwd() with this buffer size, and all current (and even quite old) platforms that we are currently known to work on have a native getcwd() so this code is not used on those anyway. Reported by Qualys, ok djm@ commit 36857fefd8849c4b0e877cfd9d1eb22f79b76650 Author: Darren Tucker Date: Thu Jul 14 10:02:35 2022 +1000 Split README.platform into its own line. README.platform has general platform-specific information, having it following text about FIDO2 on the same line could imply that it only has information about FIDO2. commit 00a496c6c14f2d41f2a9365714d494dd5f3aac9f Author: Darren Tucker Date: Thu Jul 14 09:56:01 2022 +1000 Clarify README.md text. Clarify the text about the implications of building without OpenSSL, and prefix the "configure --help" example command with a "./" so it's likely to work as-is in more shells. From bz#3461. commit f40b52f21fbc52eb513279168a49d3285c65256c Author: Darren Tucker Date: Tue Jul 12 19:48:44 2022 +1000 Remove special casing of crypt(). Configure goes to some lengths to pick crypt() from either libcrypt or OpenSSL's libcrypto because they can more or less featureful (eg supporting md5-style passwords). OpenSSL removed its crypt() interface in 2002: https://github.com/openssl/openssl/commit/69deec58 so these hijinks should no longer be necessary. This also only links sshd with libcrypt which is the only thing that needs it. ok djm@ commit 76f4e48631d7b09fb243b47d7b393d100d3741b7 Author: Darren Tucker Date: Wed Jul 13 13:17:47 2022 +1000 Only refuse to use OpenSSL 3.0.4 on x86_64. The potential RCE only impacts x86_64, so only refuse to use it if we're targetting a potentially impacted architecture. ok djm@ commit e75bbc1d88491fa85e61b2cc8783d4bbd00cd131 Author: Darren Tucker Date: Tue Jul 12 14:37:15 2022 +1000 Capture stderr output from configure. commit d9eaea4bea6271bcee6a2b9428f1271faf2d033b Author: Darren Tucker Date: Tue Jul 12 12:54:49 2022 +1000 Refuse to use OpenSSL 3.0.4 due to potential RCE. OpenSSL has a potential RCE in its RSA implementation (CVE-2022-2274) so refuse to use that specific version. commit fb2f3a61bf3d28fff285524535f7ffcd177c9235 Author: Darren Tucker Date: Tue Jul 12 12:54:24 2022 +1000 Move unset to before we set anything. commit c483a5c0fb8e8b8915fad85c5f6113386a4341ca Author: Darren Tucker Date: Wed Jul 6 11:52:54 2022 +1000 Test against openssl-3.0.5. commit 669a56bcfe73f8b985f2bba476ba834d55253acf Author: Darren Tucker Date: Tue Jul 5 18:35:53 2022 +1000 Update sanitizer test targets: - remove clang-sanitize-memory for now. It takes so long that the test times out. - add gcc sanitize-address and sanitize-undefined test targets. commit 48cc68b69118b3ce8d07fd4f82e00d58667d5379 Author: Darren Tucker Date: Tue Jul 5 16:23:28 2022 +1000 Add GCC address sanitizer build/test. commit 55c60bdd39b82457e92efa77da8d16cfa6a49391 Author: Darren Tucker Date: Tue Jul 5 12:02:33 2022 +1000 Move sanitizer logs into regress for collection. commit 35ef2b3b6ef198f8574904a45780487ec2f17858 Author: dtucker@openbsd.org Date: Mon Jul 4 09:10:31 2022 +0000 upstream: Add TEST_REGRESS_CACHE_DIR. If set, it is used to cache regress test names that have succeeded and skip those on a re-run. OpenBSD-Regress-ID: a7570dd29a58df59f2cca647c3c2ec989b49f247 commit 7394ed80c4de8b228a43c8956cf2fa1b9c6b2622 Author: Darren Tucker Date: Sun Jul 3 21:46:44 2022 +1000 Add clang sanitizer tests. commit bfce0e66b6017a9bfab450b9dc7d4b16f90de817 Author: Darren Tucker Date: Sun Jul 3 18:14:09 2022 +1000 Skip all rlimit tests when sandboxing disabled. The rlimit tests can hang when being run with some compiler sanitizers so skip all of them if sandbox=no. commit 6208d611520f9ea94d5369f9da404b709930029d Author: Darren Tucker Date: Sun Jul 3 17:54:49 2022 +1000 Move checks for pollfd.fd and nfds_t. Move the checks for struct pollfd.fd and nfds_t to before the sandboxing checks. This groups all the sandbox checks together so we can skip them all when sandboxing is disabled. commit 322964f8f2e9c321e77ebae1e4d2cd0ccc5c5a0b Author: dtucker@openbsd.org Date: Fri Jul 1 05:08:23 2022 +0000 upstream: Remove leftover line. Remove extra line leftover from merge conflict. ok djm@ OpenBSD-Commit-ID: 460e2290875d7ae64971a7e669c244b1d1c0ae2e commit 7ec81daad0e03a64e8d91c5590960c48c1a899a3 Author: djm@openbsd.org Date: Fri Jul 1 04:45:50 2022 +0000 upstream: use consistent field names (s/char/byte) in format description OpenBSD-Commit-ID: 3de33572733ee7fcfd7db33d37db23d2280254f0 commit 32e82a392d9f263485effdd606ff5862d289a4a0 Author: Darren Tucker Date: Fri Jul 1 13:55:19 2022 +1000 Skip select+rlimit check if sandboxing is disabled It's not needed in that case, and the test can fail when being built with some compiler memory sanitizer flags. bz#3441 commit 4be7184ebe2a2ccef175983517a35ee06766e1b4 Author: djm@openbsd.org Date: Fri Jul 1 03:52:57 2022 +0000 upstream: bump up loglevel from debug to info when unable to open authorized keys/principals file for errno != ENOENT; bz2042 ok dtucker OpenBSD-Commit-ID: e79aa550d91ade6a80f081bda689da24c086d66b commit 6c31ba10e97b6953c4f325f526f3e846dfea647a Author: dtucker@openbsd.org Date: Fri Jul 1 03:39:44 2022 +0000 upstream: Don't leak the strings allocated by order_hostkeyalgs() and list_hostkey_types() that are passed to compat_pkalg_proposal(). Part of github PR#324 from ZoltanFridrich, ok djm@ This is a roll-forward of the previous rollback now that the required changes in compat.c have been done. OpenBSD-Commit-ID: c7cd93730b3b9f53cdad3ae32462922834ef73eb commit 486c4dc3b83b4b67d663fb0fa62bc24138ec3946 Author: dtucker@openbsd.org Date: Fri Jul 1 03:35:45 2022 +0000 upstream: Always return allocated strings from the kex filtering so that we can free them later. Fix one leak in compat_kex_proposal. Based on github PR#324 from ZoltanFridrich with some simplications by me. ok djm@ OpenBSD-Commit-ID: 9171616da3307612d0ede086fd511142f91246e4 commit 96faa0de6c673a2ce84736eba37fc9fb723d9e5c Author: djm@openbsd.org Date: Fri Jul 1 00:36:30 2022 +0000 upstream: ignore SIGPIPE earlier in main(), specifically before muxclient() which performs operations that could cause one; Reported by Noam Lewis via bz3454, ok dtucker@ OpenBSD-Commit-ID: 63d8e13276869eebac6d7a05d5a96307f9026e47 commit 33efac790f6b09d54894ba6c3e17dfb08b6fc7e1 Author: jmc@openbsd.org Date: Tue Jun 28 06:09:14 2022 +0000 upstream: reflect the update to -D arg name in usage(); OpenBSD-Commit-ID: abdcde4f92b1ef094ae44210ee99d3b0155aad9c commit c71a1442d02f0a3586109dfe2cb366de36dee08e Author: Darren Tucker Date: Wed Jun 29 18:28:47 2022 +1000 Update OpenSSL tests to the most recent releases. commit 2a822f29300b2de7335fbff65f0b187a0c582304 Author: djm@openbsd.org Date: Mon Jun 27 21:41:55 2022 +0000 upstream: allow arguments to sftp -D option, e.g. sftp -D "/usr/libexec/sftp-server -el debug3" ok markus@ OpenBSD-Commit-ID: 5a002b9f3a7aef2731fc0ffa9c921cf15f38ecce commit 2369a2810187e08f2af5d58b343956062fb96ee8 Author: dtucker@openbsd.org Date: Fri Jun 24 10:45:06 2022 +0000 upstream: Roll back previous KEX changes as they aren't safe until compat_pkalg_proposal and friends always allocate their returned strings. Reported by Qualys. OpenBSD-Commit-ID: 1c7a88a0d5033f42f88ab9bec58ef1cf72c81ad0 commit 646686136c34c2dbf6a01296dfaa9ebee029386d Author: dtucker@openbsd.org Date: Fri Jun 24 04:37:00 2022 +0000 upstream: Don't leak the strings allocated by order_hostkeyalgs() and list_hostkey_types() that are passed to compat_pkalg_proposal(). Part of github PR#324 from ZoltanFridrich, ok djm@ OpenBSD-Commit-ID: b2f6e5f60f2bba293b831654328a8a0035ef4a1b commit 193c6d8d905dde836b628fc07a7b9cf2d347e2a3 Author: Darren Tucker Date: Sat Jun 25 12:16:15 2022 +1000 Zero out LIBFIDO2 when SK support not usable. Prevents us from trying to link them into ssh-sk-helper and failing to build. commit 40f5d849d25c60b4ae21261e78484d435f5cfd51 Author: Darren Tucker Date: Sat Jun 25 11:47:28 2022 +1000 Disable SK support if FIDO libs not found. commit 5fd922ade1b25880fe8a8249f5c0385e413108f9 Author: Damien Miller Date: Fri Jun 24 14:43:54 2022 +1000 fix broken case statement in previous commit f51423bdaf0008d46b6af082bcfd7a22a87375f0 Author: Damien Miller Date: Fri Jun 24 14:40:42 2022 +1000 request 1.1x API compatibility for OpenSSL >=3.x idea/patch from Pedro Martelletto via GHPR#322; ok dtucker@ commit 455cee8d6c2e4c48c5af9faead3599c49948411e Author: djm@openbsd.org Date: Fri Jun 24 04:27:14 2022 +0000 upstream: make it clear that RekeyLimit applies to both transmitted and received data. GHPR#328 from Jan Pazdziora OpenBSD-Commit-ID: d180a905fec9ff418a75c07bb96ea41c9308c3f9 commit 17904f05802988d0bb9ed3c8d1d37411e8f459c3 Author: tobhe@openbsd.org Date: Tue Jun 21 14:52:13 2022 +0000 upstream: Make sure not to fclose() the same fd twice in case of an error. ok dtucker@ OpenBSD-Commit-ID: e384c4e05d5521e7866b3d53ca59acd2a86eef99 commit f29d6cf98c25bf044079032d22c1a57c63ab9d8e Author: dtucker@openbsd.org Date: Sat Jun 18 02:17:16 2022 +0000 upstream: Don't attempt to fprintf a null identity comment. From Martin Vahlensieck via tech@. OpenBSD-Commit-ID: 4c54d20a8e8e4e9912c38a7b4ef5bfc5ca2e05c2 commit ad1762173bb38716a106e8979806149fd0f2753e Author: dtucker@openbsd.org Date: Fri Jun 17 01:00:03 2022 +0000 upstream: Log an error if pipe() fails while accepting a connection. bz#3447, from vincent-openssh at vinc17 net, ok djm@ OpenBSD-Commit-ID: 9d59f19872b94900a5c79da2d57850241ac5df94 commit 9c59e7486cc8691401228b43b96a3edbb06e0412 Author: Damien Miller Date: Fri Jun 24 14:20:43 2022 +1000 automatically enable built-in FIDO support If libfido2 is found and usable, then enable the built-in security key support unless --without-security-key-builtin was requested. ok dtucker@ commit 7d25b37fb2a5ff4dadabcbdac6087a97479434f5 Author: Damien Miller Date: Fri Jun 24 13:46:39 2022 +1000 fix possible NULL deref when built without FIDO Analysis/fix from kircher in bz3443; ok dtucker@ commit f5ba85daddfc2da6a8dab6038269e02c0695be44 Author: djm@openbsd.org Date: Wed Jun 15 16:08:25 2022 +0000 upstream: make sure that UseDNS hostname lookup happens in the monitor and not in the pledge(2)'d unprivileged process; fixes regression caused by recent refactoring spotted by henning@ OpenBSD-Commit-ID: a089870b95101cd8881a2dff65b2f1627d13e88d commit acb2059febaddd71ee06c2ebf63dcf211d9ab9f2 Author: djm@openbsd.org Date: Fri Jun 3 04:47:21 2022 +0000 upstream: move auth_openprincipals() and auth_openkeyfile() over to auth2-pubkeyfile.c too; they make more sense there. OpenBSD-Commit-ID: 9970d99f900e1117fdaab13e9e910a621b7c60ee commit 3d9b0845f34510111cc693bb99a667662ca50cd8 Author: djm@openbsd.org Date: Fri Jun 3 04:31:54 2022 +0000 upstream: test setenv in both client and server, test first-match-wins too OpenBSD-Regress-ID: 4c8804f9db38a02db480b9923317457b377fe34b commit 22e1a3a71ad6d108ff0c5f07f93c3fcbd30f8b40 Author: djm@openbsd.org Date: Fri Jun 3 04:30:46 2022 +0000 upstream: Make SetEnv directives first-match-wins in both sshd_config and sshd_config; previously if the same name was reused then the last would win (which is the opposite to how the config is supposed to work). While there, make the ssh_config parsing more like sshd_config. bz3438, ok dtucker OpenBSD-Commit-ID: 797909c1e0262c0d00e09280459d7ab00f18273b commit 38ed6c57e9e592c08e020fa6e82b45b4e1040970 Author: dtucker@openbsd.org Date: Fri Jun 3 04:00:15 2022 +0000 upstream: Add missing *-sk types to ssh-keyscan manpage. From skazi0 via github PR#294. OpenBSD-Commit-ID: fda2c869cdb871f3c90a89fb3f985370bb5d25c0 commit ea97ec98c41ec2b755dfab459347db674ff9a5de Author: dtucker@openbsd.org Date: Fri Jun 3 03:21:09 2022 +0000 upstream: Add period at end of "not known by any other names" message. github PR#320 from jschauma, ok djm@ OpenBSD-Commit-ID: bd60809803c4bfd3ebb7c5c4d918b10e275266f2 commit 88e376fcd67478ad1660d94bc73ab348ac9f4527 Author: dtucker@openbsd.org Date: Fri Jun 3 03:17:42 2022 +0000 upstream: ssh-keygen -A: do not generate DSA keys by default. Based on github PR#303 from jsegitz with man page text from jmc@, ok markus@ djm@ OpenBSD-Commit-ID: 5c4c57bdd7063ff03381cfb6696659dd3f9f5b9f commit 6b3fb624675082a1e5aa615d1b8479873d8b5731 Author: naddy@openbsd.org Date: Tue May 31 14:05:12 2022 +0000 upstream: ssh-keygen: implement "verify-required" certificate option. This was already documented when support for user-verified FIDO keys was added, but the ssh-keygen(1) code was missing. ok djm@ OpenBSD-Commit-ID: f660f973391b593fea4b7b25913c9a15c3eb8a06 commit b7f86ffc301be105bba9a3e0618b6fab3ae379bd Author: jmc@openbsd.org Date: Sat May 28 05:57:56 2022 +0000 upstream: keywords ref ssh_config.5; from caspar schutijser OpenBSD-Commit-ID: f146a19d7d5c9374c3b9c520da43b2732d7d1a4e commit dc7bc52372f2744fa39191577be5306ee57aacd4 Author: Damien Miller Date: Mon May 30 09:29:09 2022 +1000 fix some bugs in the fuzzer commit 1781f507c113667613351c19898efaf1e311a865 Author: Darren Tucker Date: Fri May 27 18:19:48 2022 +1000 Test against OpenSSL 1.1.1o and 3.0.3. commit c53906e0c59e569691b4095d3e8db79cf78fa058 Author: Darren Tucker Date: Fri May 27 18:18:31 2022 +1000 Test against LibreSSL 3.5.3. commit 9b3ad432ad2f19319bcc089370e356c6315d682f Author: Damien Miller Date: Fri May 27 17:00:43 2022 +1000 fuzzer for authorized_keys parsing mostly redundant to authopt_fuzz, but it's sensitive code so IMO it makes sense to test this layer too commit c83d8c4d6f3ccceef84d46de107f6b71cda06359 Author: djm@openbsd.org Date: Fri May 27 05:02:46 2022 +0000 upstream: split the low-level file handling functions out from auth2-pubkey.c Put them in a new auth2-pubkeyfile.c to make it easier to refer to them (e.g. in unit/fuzz tests) without having to refer to everything else pubkey auth brings in. ok dtucker@ OpenBSD-Commit-ID: 3fdca2c61ad97dc1b8d4a7346816f83dc4ce2217 commit 3b0b142d2a0767d8cd838e2f3aefde8a0aaa41e1 Author: djm@openbsd.org Date: Fri May 27 05:01:25 2022 +0000 upstream: refactor authorized_keys/principals handling remove "struct ssh *" from arguments - this was only used to pass the remote host/address. These can be passed in instead and the resulting code is less tightly coupled to ssh_api.[ch] ok dtucker@ OpenBSD-Commit-ID: 9d4373d013edc4cc4b5c21a599e1837ac31dda0d commit 2c334fd36f80cb91cc42e4b978b10aa35e0df236 Author: dtucker@openbsd.org Date: Fri May 27 04:29:40 2022 +0000 upstream: f sshpkt functions fail, then password is not cleared with freezero. Unconditionally call freezero to guarantee that password is removed from RAM. From tobias@ and c3h2_ctf via github PR#286, ok djm@ OpenBSD-Commit-ID: 6b093619c9515328e25b0f8093779c52402c89cd commit 5d3a77f4c5ae774c6796387266503f52c7cdc7c2 Author: dtucker@openbsd.org Date: Fri May 27 04:27:49 2022 +0000 upstream: Avoid kill with -1 argument. The out_ctx label can be reached before fork has been called. If this happens, then kill -1 would be called, sending SIGTERM to all processes reachable by the current process. From tobias@ and c3h2_ctf via github PR#286, ok djm@ OpenBSD-Commit-ID: 6277af1207d81202f5daffdccfeeaed4c763b1a8 commit 533b31cd08e4b97f455466f91c36915e2924c15a Author: dtucker@openbsd.org Date: Fri May 27 04:13:24 2022 +0000 upstream: Note that ProxyJump also accepts the same tokens as ProxyCommand. From pallxk via github PR#305. OpenBSD-Commit-ID: 7115ac351b129205f1f1ffa6bbfd62abd76be7c5 commit 9d8c80f8a304babe61ca28f2e3fb5eb6dc9c39bf Author: djm@openbsd.org Date: Wed May 25 06:03:44 2022 +0000 upstream: revert previous; it was broken (spotted by Theo) OpenBSD-Commit-ID: 457c79afaca2f89ec2606405c1059b98b30d8b0d commit 9e0d02ef7ce88b67643bfb1c2272c9f5f04cc680 Author: djm@openbsd.org Date: Wed May 25 00:31:13 2022 +0000 upstream: make SSHBUF_DBG/SSHBUF_TELL (off by default and only enabled via #define) dump to stderr rather than stdout OpenBSD-Commit-ID: 10298513ee32db8390aecb0397d782d68cb14318 commit 2487163630f28be28b7e2396b4bd6511b98f1d3e Author: Tim Rice Date: Tue May 24 10:21:25 2022 -0700 configure.ac: Add missing AC_DEFINE for caph_cache_tzdata test causing HAVE_CAPH_CACHE_TZDATA to be missing from config.h.in. Spotted by Bryan Drewery commit bedb93415b60db3dfd704a3d525e82adb14a2481 Author: djm@openbsd.org Date: Sun May 15 23:48:07 2022 +0000 upstream: regress test for in-place transfers and clobbering larger files with smaller ones; would have caught last regression in scp(1) OpenBSD-Regress-ID: 19de4e88dd3a4f7e5c1618c9be3c32415bd93bc2 commit b4f0d719c2548cb74da509fb65f384dada4ebd37 Author: anton@openbsd.org Date: Fri Apr 22 05:08:43 2022 +0000 upstream: Only run agent-ptrace.sh if gdb is available as all architectures do not ship with gdb. OpenBSD-Regress-ID: ec53e928803e6b87f9ac142d38888ca79a45348d commit 9b73345f80255a7f3048026462f2c0c6a241eeac Author: djm@openbsd.org Date: Sun May 15 23:47:21 2022 +0000 upstream: fix in-place copies; r1.163 incorrectly skipped truncation in all cases, not just at the start of a transfer. This could cause overwrites of larger files to leave junk at the end. Spotted by tb@ OpenBSD-Commit-ID: b189f19cd68119548c8e24e39c79f61e115bf92c commit 56a0697fe079ff3e1ba30a2d5c26b5e45f7b71f8 Author: djm@openbsd.org Date: Fri May 13 06:31:50 2022 +0000 upstream: arrange for scp, when in sftp mode, to not ftruncate(3) files early previous behavious of unconditionally truncating the destination file would cause "scp ~/foo localhost:" and "scp localhost:foo ~/" to delete all the contents of their destination. spotted by solene@ sthen@, also bz3431; ok dtucker@ OpenBSD-Commit-ID: ca39fdd39e0ec1466b9666f15cbcfddea6aaa179 commit fbcef70c2832712f027bccea1aa9bc4b4103da93 Author: dtucker@openbsd.org Date: Mon May 9 08:25:27 2022 +0000 upstream: Remove errant apostrophe. From haruyama at queen-ml org. OpenBSD-Commit-ID: dc6b294567cb84b384ad6ced9ca469f2bbf0bd10 commit 0086a286ea6bbd11ca9b664ac3bb12b27443d6eb Author: djm@openbsd.org Date: Mon May 9 03:09:53 2022 +0000 upstream: Allow existing -U (use agent) flag to work with "-Y sign" operations, where it will be interpreted to require that the private keys is hosted in an agent; bz3429, suggested by Adam Szkoda; ok dtucker@ OpenBSD-Commit-ID: a7bc69873b99c32c42c7628ed9ea91565ba08c2f commit cb010744cc98f651b1029bb09efa986eb54e4ccf Author: djm@openbsd.org Date: Sun May 8 22:58:35 2022 +0000 upstream: improve error message when 'ssh-keygen -Y sign' is unable to load a private key; bz3429, reported by Adam Szkoda ok dtucker@ OpenBSD-Commit-ID: bb57b285e67bea536ef81b1055467be2fc380e74 commit aa61fc82c63d309a90c22ca74fb1da6c6f4372fd Author: Tobias Heider Date: Mon May 9 02:00:01 2022 +0200 Remove duplicate bcrypt_pbkdf.o from Makefile bcrypt_pbkdf.o is duplicated in the openbsd-compat Makefile's object file list. commit deb506d00da8d11fb04c1e7b9b1e1cc379c1705c Author: djm@openbsd.org Date: Sun May 8 22:32:36 2022 +0000 upstream: When performing operations that glob(3) a remote path, ensure that the implicit working directory used to construct that path escapes glob(3) characters. This prevents glob characters from being processed in places they shouldn't, e.g. "cd /tmp/a*/", "get *.txt" should have the get operation treat the path "/tmp/a*" literally and not attempt to expand it. Reported by Lusia Kundel; ok markus@ OpenBSD-Commit-ID: 4f647f58482cbad3d58b1eab7f6a1691433deeef commit f38cf74f20b5da113cfa823afd5bfb5c6ba65f3d Author: Darren Tucker Date: Fri May 6 14:50:18 2022 +1000 Also retest OpenBSD upstream on .yml changes. commit f87a132800ba3710ab130d703448a31ef1128d77 Author: Darren Tucker Date: Fri May 6 14:46:09 2022 +1000 Note that, for now, we need variadic macros. commit 217b518e0f7c52c4b909e935141a55344c61e644 Author: Darren Tucker Date: Fri May 6 14:39:34 2022 +1000 Add ubsan minimal testcase on OpenBSD. As suggested by djm@. commit 457dce2cfef6a48f5442591cd8b21c7e8cba13f8 Author: djm@openbsd.org Date: Thu May 5 01:04:14 2022 +0000 upstream: sshkey_unshield_private() contains a exact duplicate of the code in private2_check_padding(). Pull private2_check_padding() up so the code can be reused. From Martin Vahlensieck, ok deraadt@ OpenBSD-Commit-ID: 876884c3f0e62e8fd8d1594bab06900f971c9c85 commit 0e44db4d9cb313e68a59a44d27884af66c02356e Author: djm@openbsd.org Date: Thu May 5 00:56:58 2022 +0000 upstream: channel_new no longer frees remote_name. So update the comment accordingly. As remote_name is not modified, it can be const as well. From Martin Vahlensieck OpenBSD-Commit-ID: e4e10dc8dc9f40c166ea5a8e991942bedc75a76a commit 37b62fd5caf19c85a48241535277cefff65adace Author: djm@openbsd.org Date: Thu May 5 00:55:11 2022 +0000 upstream: mux.c: mark argument as const; from Martin Vahlensieck OpenBSD-Commit-ID: 69a1a93a55986c7c2ad9f733c093b46a47184341 commit f4e67c0ad259b4cf10177277a5827fa5545bac53 Author: markus@openbsd.org Date: Wed May 4 07:31:22 2022 +0000 upstream: make sure stdout is non-blocking; ok djm@ OpenBSD-Commit-ID: 64940fffbd1b882eda2d7c8c7a43c79368309c0d commit e5c036d2092c00bef395e9161dc5ce42d4be9565 Author: florian@openbsd.org Date: Tue May 3 07:42:27 2022 +0000 upstream: Add FIDO AUTHENTICATOR section and explain a bit how FIDO works. The wording came mostly from the 8.2 OpenSSH release notes, addapted to fit the man page. Then move the -O bits into the new section as is already done for CERTIFICATES and MODULI GENERATION. Finally we can explain the trade-offs of resident keys. While here, consistently refer to the FIDO thingies as "FIDO authenticators", not "FIDO tokens". input & OK jmc, naddy OpenBSD-Commit-ID: dd98748d7644df048f78dcf793b3b63db9ab1d25 commit 575771bf79bef7127be6aaccddc46031ea15529e Author: jmc@openbsd.org Date: Mon May 2 05:40:37 2022 +0000 upstream: remove an obsolete rsa1 format example from an example; from megan batty ok djm OpenBSD-Commit-ID: db2c89879c29bf083df996bd830abfb1e70d62bf commit 0bc6b4c8f04e292577bdb44d5dc6b630d3448087 Author: djm@openbsd.org Date: Sun May 1 23:20:30 2022 +0000 upstream: fix some integer overflows in sieve_large() that show up when trying to generate modp groups > 16k bits. Reported via GHPR#306 by Bertram Felgenhauer, but fixed in a different way. feedback/ok tb@ OpenBSD-Commit-ID: 81cbc6dd3a21c57bd6fadea10e44afe37bca558e commit a45615cb172bc827e21ec76750de39dfb30ecc05 Author: djm@openbsd.org Date: Fri Apr 29 04:55:07 2022 +0000 upstream: be stricter in which characters will be accepted in specifying a mask length; allow only 0-9. From khaleesicodes via GHPR#278; ok dtucker@ OpenBSD-Commit-ID: e267746c047ea86665cdeccef795a8a56082eeb2 commit 4835544d2dd31de6ffc7dba59f92093aea98155b Author: Darren Tucker Date: Sat Apr 30 10:56:41 2022 +1000 Add Mac OS X 12 test target. commit 97a6a8b8c1f2da09712d0e72d0ef800e4edd34cd Author: Darren Tucker Date: Fri Apr 29 18:27:34 2022 +1000 Only run tests when source files change. Also run tests on changes to V_9_0 branch. commit 6d0392b9ff4b50a56ac5685d1b9392e2cd432ca3 Author: Darren Tucker Date: Fri Apr 29 18:22:34 2022 +1000 Remove now-empty int32_minmax.inc. commit af59463553b5ad52d3b42c4455ee3c5600158bb7 Author: djm@openbsd.org Date: Fri Apr 29 03:24:30 2022 +0000 upstream: mention that the helpers are used by ssh(1), ssh-agent(1) and ssh-keygen(1). Previously only ssh(1) was mentioned. From Pedro Martelletto OpenBSD-Commit-ID: 30f880f989d4b329589c1c404315685960a5f153 commit 3e26b3a6eebcee27be177207cc0846fb844b7a56 Author: dtucker@openbsd.org Date: Fri Apr 29 03:16:48 2022 +0000 upstream: Don't leak SK device. Patch from Pedro Martelletto via github PR#316. ok djm@ OpenBSD-Commit-ID: 17d11327545022e727d95fd08b213171c5a4585d commit 247082b5013f0d4fcae8f97453f2a2f01bcda811 Author: djm@openbsd.org Date: Fri Apr 29 03:13:32 2022 +0000 upstream: fix memleak on session-bind path; from Pedro Martelletto, ok dtucker@ OpenBSD-Commit-ID: e85899a26ba402b4c0717b531317e8fc258f0a7e commit e05522008092ceb86a87bdd4ad7878424315db89 Author: djm@openbsd.org Date: Thu Apr 28 02:53:31 2022 +0000 upstream: avoid printing hash algorithm twice; from lucas AT sexy.is OpenBSD-Commit-ID: 9d24671e10a84141b7c504396cabad600e47a941 commit 0979e29356915261d69a9517a1e0aaade7c9fc75 Author: dtucker@openbsd.org Date: Wed Apr 27 11:08:55 2022 +0000 upstream: Add authfd path to debug output. ok markus@ OpenBSD-Commit-ID: f735a17d1a6f2bee63bfc609d76ef8db8c090890 commit 67b7c784769c74fd4d6b147d91e17e1ac1a8a96d Author: dtucker@openbsd.org Date: Tue Apr 26 07:41:44 2022 +0000 upstream: Check sshauthopt_new() for NULL. bz#3425, from tessgauthier at microsoft.com. ok djm@ OpenBSD-Commit-ID: af0315bc3e44aa406daa7e0ae7c2d719a974483f commit d571314d14b919fbd7c84a61f9bf2065fc0a6841 Author: millert@openbsd.org Date: Wed Apr 20 16:00:25 2022 +0000 upstream: Remove unnecessary includes: openssl/hmac.h and openssl/evp.h. From Martin Vahlensieck. OpenBSD-Commit-ID: a6debb5fb0c8a44e43e8d5ca7cc70ad2f3ea31c3 commit da8dddf8cc1f2516ff894b8183e83a7c5ba3ef80 Author: millert@openbsd.org Date: Wed Apr 20 15:59:18 2022 +0000 upstream: Add missing includes of stdlib.h and stdint.h. We need stdlib.h for malloc(3) and stdint.h for SIZE_MAX. Unlike the other xmss files, ssh-xmss.c does not include xmss_commons.h so ssh-xmss.c must include those headers itself. From Martin Vahlensieck OpenBSD-Commit-ID: 70e28a9818cee3da1be2ef6503d4b396dd421e6b commit fe9d87a6800a7a33be08f4d5ab662a758055ced2 Author: millert@openbsd.org Date: Wed Apr 20 15:56:49 2022 +0000 upstream: Avoid an unnecessary xstrdup in rm_env() when matching patterns. Since match_pattern() doesn't modify its arguments (they are const), there is no need to make an extra copy of the strings in options->send_env. From Martin Vahlensieck OpenBSD-Commit-ID: 2c9db31e3f4d3403b49642c64ee048b2a0a39351 commit 7bf2eb958fbb551e7d61e75c176bb3200383285d Author: Darren Tucker Date: Tue Apr 26 23:30:59 2022 +1000 Add debian-riscv64 test target. commit 3913c935523902482974c4c503bcff20bd850a6a Author: Darren Tucker Date: Mon Apr 25 17:20:06 2022 +1000 Update OpenSSL and LibreSSL versions in tests. commit dcd8dca29bcdb193ff6be35b96fc55e6e30d37d9 Author: Darren Tucker Date: Sat Apr 23 20:40:28 2022 +1000 Include stdlib.h for free() prototype. ... which is used inside the CUSTOM_SYS_AUTH_GET_LASTLOGIN_MSG block. commit 4cc05de568e1c3edd7834ff3bd9d8214eb34861b Author: Darren Tucker Date: Sat Apr 23 20:17:26 2022 +1000 Cache timezone data in capsicum sandbox. From emaste at freebsd.org, originally part of FreeBSD commit r339216 / fc3c19a9 with autoconf bits added by me. commit c31404426d212e2964ff9e5e58e1d0fce3d83f27 Author: dtucker@openbsd.org Date: Thu Apr 21 01:36:46 2022 +0000 upstream: It looks like we can't completely avoid waiting for processes to exit so retrieve the pid via controlmaster and use that. OpenBSD-Regress-ID: 8246f00f22b14e49d2ff1744c94897ead33d457b commit d19b21afab5c8e2f3df6bd8aee9766bdad3d8c58 Author: dtucker@openbsd.org Date: Wed Apr 20 13:25:55 2022 +0000 upstream: Use ssh -f and ControlPersist .. to start up test forwards and ssh -O stop to shut them down intead of sleep loops. This speeds up the test by an order of magnitude. OpenBSD-Regress-ID: eb3db5f805100919b092a3b2579c611fba3e83e7 commit 5f76286a126721fa005de6edf3d1c7a265555f19 Author: dtucker@openbsd.org Date: Wed Apr 20 05:24:13 2022 +0000 upstream: Simplify forward-control test. Since we no longer need to support SSH1 we don't need to run shell commands on the other end of the connection and can use ssh -N instead. This also makes the test less racy. OpenBSD-Regress-ID: 32e94ce272820cc398f30b848b2b0f080d10302c commit 687bbf23572d8bdf25cbbcdf8ac583514e1ba710 Author: djm@openbsd.org Date: Thu Mar 31 03:07:33 2022 +0000 upstream: regression test for sftp cp command OpenBSD-Regress-ID: c96bea9edde3a384b254785e7f9b2b24a81cdf82 commit f1233f19a6a9fe58f52946f50df4772f5b136761 Author: dtucker@openbsd.org Date: Wed Apr 20 01:13:47 2022 +0000 upstream: Import regenerated moduli OpenBSD-Commit-ID: f9a0726d957cf10692a231996a1f34e7f9cdfeb0 commit fec014785de198b9a325d1b94e324bb958c5fe7b Author: djm@openbsd.org Date: Wed Apr 20 04:19:11 2022 +0000 upstream: Try to continue running local I/O for channels in state OPEN during SSH transport rekeying. The most visible benefit is that it should make ~-escapes work in the client (e.g. to exit) if the connection happened to have stalled during a rekey event. Based work by and ok dtucker@ OpenBSD-Commit-ID: a66e8f254e92edd4ce09c9f750883ec8f1ea5f45 commit e68154b0d4f0f5085a050ea896955da1b1be6e30 Author: dtucker@openbsd.org Date: Wed Apr 20 01:13:47 2022 +0000 upstream: Import regenerated moduli OpenBSD-Commit-ID: f9a0726d957cf10692a231996a1f34e7f9cdfeb0 commit 69928b106d8f0fa15b88cf3850d992ed81c44ae0 Author: tj@openbsd.org Date: Sat Apr 16 00:22:31 2022 +0000 upstream: list the correct version number for when usage of the sftp protocol became default and fix a typo from ed maste OpenBSD-Commit-ID: 24e1795ed2283fdeacf16413c2f07503bcdebb31 commit 21042a05c0b304c16f655efeec97438249d2e2cc Author: dtucker@openbsd.org Date: Tue Apr 12 05:09:49 2022 +0000 upstream: Correct path for system known hosts file in description of IgnoreUserKnownHosts. Patch from Martin Vahlensieck via tech@ OpenBSD-Commit-ID: 9b7784f054fa5aa4d63cb36bd563889477127215 commit 53f4aff60a7c1a08a23917bd47496f8901c471f5 Author: Darren Tucker Date: Sat Apr 16 14:33:20 2022 +1000 Resync moduli.5 with upstream. 1.18: remove duplicate publication year; carsten dot kunze at arcor dot de 1.19: ssh-keygen's -G/-T have been replaced with -M generate/screen. commit d2b888762b9844eb0d8eb59909cdf5af5159f810 Author: Darren Tucker Date: Sat Apr 16 14:31:13 2022 +1000 Retire fbsd6 test VM. It's long since out of support, relatively slow (it's i686) and the compiler has trouble with PIE. commit cd1f70009860a154b51230d367c55ea5f9a4504e Author: djm@openbsd.org Date: Mon Apr 11 22:52:08 2022 +0000 upstream: clear io_want/io_ready flags at start of poll() cycle; avoids plausible spin during rekeying if channel io_want flags are reused across cycles. ok markus@ deraadt@ OpenBSD-Commit-ID: 91034f855b7c73cd2591657c49ac30f10322b967 commit aa1920302778273f7f94c2091319aba199068ca0 Author: dtucker@openbsd.org Date: Fri Apr 8 05:43:39 2022 +0000 upstream: Note that curve25519-sha256 was later published in RFC8731. ok djm@ OpenBSD-Commit-ID: 2ac2b5d642d4cf5918eaec8653cad9a4460b2743 commit 4673fa8f2be983f2f88d5afd754adb1a2a39ec9e Author: djm@openbsd.org Date: Fri Apr 8 04:40:40 2022 +0000 upstream: two defensive changes from Tobias Stoeckmann via GHPR287 enforce stricter invarient for sshbuf_set_parent() - never allow a buffer to have a previously-set parent changed. In sshbuf_reset(), if the reallocation fails, then zero the entire buffer and not the (potentially smaller) default initial alloc size. OpenBSD-Commit-ID: 14583203aa5d50ad38d2e209ae10abaf8955e6a9 commit 26eef015e2d2254375e13afaaf753b78932b1bf5 Author: Damien Miller Date: Mon Apr 11 16:07:09 2022 +1000 Revert "update build-aux files to match autoconf-2.71" This reverts commit 0a8ca39fac6ad19096b6c263436f8b2dd51606f2. It turns out that the checked-in copies of these files are actually newer than autoconf-2.71's copies, so this was effectively a downgrade. Spotted by Bo Anderson via github commit 0a8ca39fac6ad19096b6c263436f8b2dd51606f2 Author: Damien Miller Date: Fri Apr 8 14:48:58 2022 +1000 update build-aux files to match autoconf-2.71 i.e. config.guess, config.sub and install-sh commit 94eb6858efecc1b4f02d8a6bd35e149f55c814c8 Author: Damien Miller Date: Wed Apr 6 10:47:48 2022 +1000 update version numbers for release commit 8e4a8eadf4fe74e65e6492f34250f8cf7d67e8da Author: djm@openbsd.org Date: Mon Apr 4 22:45:25 2022 +0000 upstream: openssh-9.0 OpenBSD-Commit-ID: 0dfb461188f4513ec024c1534da8c1ce14c20b64 commit a9f23ea2e3227f406880c2634d066f6f50fa5eaa Author: naddy@openbsd.org Date: Thu Mar 31 17:58:44 2022 +0000 upstream: ssh: document sntrup761x25519-sha512@openssh.com as default KEX OpenBSD-Commit-ID: 12545bfa10bcbf552d04d9d9520d0f4e98b0e171 commit 9ec2713d122af79d66ebb9c1d6d9ae8621a8945f Author: naddy@openbsd.org Date: Thu Mar 31 17:27:27 2022 +0000 upstream: man pages: add missing commas between subordinate and main clauses jmc@ dislikes a comma before "then" in a conditional, so leave those untouched. ok jmc@ OpenBSD-Commit-ID: 9520801729bebcb3c9fe43ad7f9776ab4dd05ea3 commit 3741df98ffaaff92b474ee70d8ef276b5882f85a Author: Darren Tucker Date: Mon Apr 4 23:52:11 2022 +1000 Disable security key on fbsd6 test host. commit 32c12236f27ae83bfe6d2983b67c9bc67a83a417 Author: Darren Tucker Date: Mon Apr 4 15:16:51 2022 +1000 Specify TEST_SHELL=bash on AIX. The system shells cause the agent-restrict test to fail due to some quoting so explicitly specify bash until we can get configure to autmatically work around that. commit 90452c8b69d065b7c7c285ff78b81418a75bcd76 Author: Darren Tucker Date: Fri Apr 1 23:38:44 2022 +1100 Only return events from ppoll that were requested. If the underlying system's select() returns bits that were not in the request set, our ppoll() implementation can return revents for events not requested, which can apparently cause a hang. Only return revents for activity in the requested event set. bz#3416, analysis and fix by yaroslav.kuzmin at vmssoftware com, ok djm@ commit 6c49eb5fabc56f4865164ed818aa5112d09c31a8 Author: Darren Tucker Date: Fri Apr 1 23:21:40 2022 +1100 Only run regression tests on slow VMs. commit f67e47903977b42cb6abcd5565a61bd7293e4dc3 Author: Darren Tucker Date: Fri Apr 1 23:21:06 2022 +1100 Increase test timeout to allow slow VMs to finish commit 02488c1b54065ddc4f25835dbd2618b2a2fe21f5 Author: Darren Tucker Date: Fri Apr 1 16:27:38 2022 +1100 Use bash or ksh if available for SH in Makefile. commit 34c7018c316af4773e432066de28d0ef9d0888cd Author: Darren Tucker Date: Fri Apr 1 14:56:54 2022 +1100 Set Makefile SHELL as determined by configure. This should improve compatibility for users with non-POSIX shells. If using Makefile.in directly (eg make -f Makefile.in distprep) then SHELL will need to be specified on the command line (along with MANFMT in that particular case). ok djm@ commit 5b054d76402faab38c48377efd112426469553a0 Author: Darren Tucker Date: Fri Apr 1 13:16:47 2022 +1100 Skip slow tests on (very) slow test targets. commit b275818065b31a865142c48c2acf6a7c1655c542 Author: Damien Miller Date: Thu Mar 31 14:11:36 2022 +1100 depend commit 3fa539c3ffaabd6211995512d33e29150f88c5c5 Author: djm@openbsd.org Date: Thu Mar 31 03:07:03 2022 +0000 upstream: add a sftp client "cp" command that supports server-side copying of files. Useful for this task and for testing the copy-data extension. Patch from Mike Frysinger; ok dtucker@ OpenBSD-Commit-ID: 1bb1b950af0d49f0d5425b1f267e197aa1b57444 commit 7988bfc4b701c4b3fe9b36c8561a3d1c5d4c9a74 Author: djm@openbsd.org Date: Thu Mar 31 03:05:49 2022 +0000 upstream: add support for the "corp-data" protocol extension to allow server-side copies to be performed without having to go via the client. Patch by Mike Frysinger, ok dtucker@ OpenBSD-Commit-ID: 00aa510940fedd66dab1843b58682de4eb7156d5 commit 32dc1c29a4ac9c592ddfef0a4895eb36c1f567ba Author: djm@openbsd.org Date: Wed Mar 30 21:13:23 2022 +0000 upstream: select post-quantum KEX sntrup761x25519-sha512@openssh.com as the default; ok markus@ OpenBSD-Commit-ID: f02d99cbfce22dffec2e2ab1b60905fbddf48fb9 commit d6556de1db0822c76ba2745cf5c097d9472adf7c Author: djm@openbsd.org Date: Wed Mar 30 21:10:25 2022 +0000 upstream: fix poll() spin when a channel's output fd closes without data in the channel buffer. Introduce more exact packing of channel fds into the pollfd array. fixes bz3405 and bz3411; ok deraadt@ markus@ OpenBSD-Commit-ID: 06740737849c9047785622ad5d472cb6a3907d10 commit 8a74a96d25ca4d32fbf298f6c0ac5a148501777d Author: djm@openbsd.org Date: Wed Mar 30 04:33:09 2022 +0000 upstream: ssh is almost out of getopt() characters; note the remaining remaining available ones in a comment OpenBSD-Commit-ID: 48d38cef59d6bc8e84c6c066f6d601875d3253fd commit 6d4fc51adb9d8a42f67b5474f02f877422379de6 Author: djm@openbsd.org Date: Wed Mar 30 04:27:51 2022 +0000 upstream: avoid NULL deref via ssh-keygen -Y find-principals. bz3409, reported by Mateusz Adamowski OpenBSD-Commit-ID: a3b2c02438052ee858e0ee18e5a288586b5df2c5 commit e937514920335b92b543fd9be79cd6481d1eb0b6 Author: Darren Tucker Date: Mon Mar 28 17:51:03 2022 +1100 Add AIX 5.1 test target. commit 4bbe815ba974b4fd89cc3fc3e3ef1be847a0befe Author: Darren Tucker Date: Sat Mar 26 22:01:31 2022 +1100 Drop leading "v" from release version identifier. It's present in the git tags but not in the release tarball names. Also drop extra "/" from URL path. commit f5cdd3b3c275dffaebfca91df782dca29975e9ac Author: Darren Tucker Date: Sat Mar 26 16:28:04 2022 +1100 Use tarballs when testing LibreSSL releases. This means they'll still work when the combination of -portable and openbsd github repos no longer match. commit 24dc37d198f35a7cf71bf4d5384363c7ef4209d4 Author: Darren Tucker Date: Sat Mar 26 15:02:45 2022 +1100 Remove now-unused passwd variable. commit 5b467ceef2c356f0a77f5e8ab4eb0fac367e4d24 Author: Darren Tucker Date: Sat Mar 26 13:15:44 2022 +1100 Missing semicolon. commit 2923d026e55998133c0f6e5186dca2a3c0fa5ff5 Author: Darren Tucker Date: Sat Mar 26 12:49:50 2022 +1100 Factor out platform-specific locked account check. Also fixes an incorrect free on platforms with both libiaf and shadow passwords (probably only Unixware). Prompted by github PR#284, originally from @c3h2_ctf and stoeckmann@. commit d23efe4b12886ffe416be10bc0a7da6ca8aa72d1 Author: Darren Tucker Date: Sat Mar 26 08:13:46 2022 +1100 Add OpenWRT mips and mipsel test targets. commit 16ea8b85838dd7a4dbeba4e51ac4f43fd68b1e5b Author: djm@openbsd.org Date: Sun Mar 20 08:52:17 2022 +0000 upstream: don't leak argument list; bz3404, reported by Balu Gajjala ok dtucker@ OpenBSD-Commit-ID: fddc32d74e5dd5cff1a49ddd6297b0867eae56a6 commit a72bde294fe0518c9a44ba63864093a1ef2425e3 Author: djm@openbsd.org Date: Sun Mar 20 08:51:21 2022 +0000 upstream: make addargs() and replacearg() a little more robust and improve error reporting make freeargs(NULL) a noop like the other free functions ok dtucker as part of bz3403 OpenBSD-Commit-ID: 15f86da83176978b4d1d288caa24c766dfa2983d commit 731087d2619fa7f01e675b23f57af10d745e8af2 Author: djm@openbsd.org Date: Fri Mar 18 04:04:11 2022 +0000 upstream: don't try to resolve ListenAddress directives in the sshd re-exec path - we're never going to use the result and if the operation fails then it can prevent connections from being accepted. Reported by Aaron Poffenberger; with / ok dtucker@ OpenBSD-Commit-ID: 44c53a43909a328e2f5ab26070fdef3594eded60 commit 1c83c082128694ddd11ac05fdf31d70312ff1763 Author: djm@openbsd.org Date: Fri Mar 18 02:50:21 2022 +0000 upstream: remove blank line OpenBSD-Commit-ID: d5e0182965b2fbfb03ad5f256d1a1ce5706bcddf commit 807be68684da7a1fe969c399ddce2fafb7997dcb Author: djm@openbsd.org Date: Fri Mar 18 02:32:22 2022 +0000 upstream: helpful comment OpenBSD-Commit-ID: e3315a45cb04e7feeb614d76ec80a9fe4ca0e8c7 commit a0b5816f8f1f645acdf74f7bc11b34455ec30bac Author: djm@openbsd.org Date: Fri Mar 18 02:31:25 2022 +0000 upstream: ssh-keygen -Y check-novalidate requires namespace or SEGV will ensue. Patch from Mateusz Adamowski via GHPR#307 OpenBSD-Commit-ID: 99e8ec38f9feb38bce6de240335be34aedeba5fd commit 5a252d54a63be30d5ba4be76210942d754a531c0 Author: djm@openbsd.org Date: Tue Mar 15 05:27:37 2022 +0000 upstream: improve DEBUG_CHANNEL_POLL debugging message OpenBSD-Commit-ID: 2275eb7bc4707d019b1a0194b9c92c0b78da848f commit ce324cf58ba2840e31afeb996935800780c8fa4b Author: cheloha@openbsd.org Date: Sun Mar 13 23:27:54 2022 +0000 upstream: ssh: xstrdup(): use memcpy(3) Copying the given string into the buffer with strlcpy(3) confers no benefit in this context because we have already determined the string's length with strlen(3) in order to allocate that buffer. Thread: https://marc.info/?l=openbsd-tech&m=164687525802691&w=2 ok dtucker@ millert@ OpenBSD-Commit-ID: f8bfc082e36e2d2dc4e1feece02fe274155ca11a commit 2893c5e764557f48f9d6a929e224ed49c59545db Author: Darren Tucker Date: Fri Mar 11 18:43:58 2022 +1100 Resync fmt_scaled. with OpenBSD. Fixes underflow reported in bz#3401. commit 5ae31a0fdd27855af29f48ff027491629fff5979 Author: Darren Tucker Date: Wed Mar 9 09:41:56 2022 +1100 Provide killpg implementation. Based on github PR#301 for Tandem NonStop. commit c41c84b439f4cd74d4fe44298a4b4037ddd7d2ae Author: Darren Tucker Date: Wed Mar 9 09:29:30 2022 +1100 Check for missing ftruncate prototype. From github PR#301 in conjunction with rsbeckerca. commit 8cf5275452a950869cb90eeac7d220b01f77b12e Author: Darren Tucker Date: Tue Mar 8 20:04:06 2022 +1100 Default to not using sandbox when cross compiling. On most systems poll(2) does not work when the number of FDs is reduced with setrlimit, so assume it doesn't when cross compiling and we can't run the test. bz#3398. commit 379b30120da53d7c84aa8299c26b18c51c2a0dac Author: djm@openbsd.org Date: Tue Mar 1 01:59:19 2022 +0000 upstream: pack pollfd array before server_accept_loop() ppoll() call, and terminate sshd if ppoll() returns errno==EINVAL avoids spin in ppoll when MaxStartups > RLIMIT_NOFILE, reported by Daniel Micay feedback/ok deraadt OpenBSD-Commit-ID: dbab1c24993ac977ec24d83283b8b7528f7c2c15 commit eceafbe0bdbbd9bd2f3cf024ccb350666a9934dd Author: naddy@openbsd.org Date: Sun Feb 27 01:33:59 2022 +0000 upstream: include rejected signature algorithm in error message and not the (useless) key type; ok djm@ OpenBSD-Commit-ID: d0c0f552a4d9161203e07e95d58a76eb602a76ff commit f2f3269423618a83157e18902385e720f9776007 Author: dtucker@openbsd.org Date: Fri Feb 25 09:46:24 2022 +0000 upstream: Remove the char * casts from arguments to do_lstat, do_readdir and do_stat paths since the underlying functions now take a const char *. Patch from vapier at gentoo.org. OpenBSD-Commit-ID: 9e4d964dbfb0ed683a2a2900711b88e7f1c0297b commit 4a66dac052c5ff5047161853f36904607649e4f9 Author: djm@openbsd.org Date: Fri Feb 25 02:09:27 2022 +0000 upstream: save an unneccessary alloc/free, based on patch from Martin Vahlensieck; ok dtucker@ OpenBSD-Commit-ID: 90ffbf1f837e509742f2c31a1fbf2c0fd376fd5f commit 6f117cb151efe138ac57bdd8e26165f350328f5f Author: Darren Tucker Date: Tue Mar 1 09:02:06 2022 +1100 Remove unused ivbits argument from chacha_keysetup commit 15974235dd528aeab0ec67fb92a0a1d733f62be2 Author: Darren Tucker Date: Tue Mar 1 09:00:20 2022 +1100 Add OPENBSD ORIGINAL marker. commit f2ff669347d320532e7c1b63cdf5c62f46e73150 Author: Darren Tucker Date: Mon Feb 28 22:21:36 2022 +1100 No unused param warnings for clang-12 and gcc-11. These have too many false positives in -Werror tests on the github CI since we often provide empty stub functions for functionality not needed for particular configurations. commit 96558ecd87adac62efa9a2b5479f686ab86b0be1 Author: Darren Tucker Date: Sat Feb 26 14:10:41 2022 +1100 Add debian-i386 test target. commit 284b6e5394652d519e31782e3b3cdfd7b21d1a81 Author: Darren Tucker Date: Sat Feb 26 14:06:14 2022 +1100 Allow ppoll_time64 in seccomp sandbox. Should fix sandbox violations on (some? at least i386 and armhf) 32bit Linux platforms. Patch from chutzpahu at gentoo.org and cjwatson at debian.org via bz#3396. commit 0132056efabc5edb85c3c7105d2fb6dee41843c6 Author: Darren Tucker Date: Fri Feb 25 19:47:48 2022 +1100 Improve handling of _getshort and _getlong. If the system native ones are exactly as required then use them, otherwise use the local versions mapped to another name to prevent name collisions. commit 8e206e0dd6b9f757b07979e48f53ad5bf9b7b52b Author: Darren Tucker Date: Fri Feb 25 15:14:22 2022 +1100 Constify utimes in compat library to match specs. Patch from vapier at chromium.org. commit 1b2920e3b63db2eddebeec7330ffe8b723055573 Author: Darren Tucker Date: Fri Feb 25 13:50:56 2022 +1100 ANSIfy getshort and getlong. These functions appear to have come from OpenBSD's lib/libc/net/res_comp.c which made this change in 2005. commit 54a86f4f6e1c43a2ca2be23ef799ab8910d4af70 Author: Darren Tucker Date: Fri Feb 25 13:23:04 2022 +1100 Use PICFLAG instead of hard coding -fPIC. commit 3016ba47035ac3561aabd48e2be70167fe157d6a Author: Darren Tucker Date: Fri Feb 25 11:37:11 2022 +1100 Add tests for latest releases of {Libre,Open}SSL. commit f107467179428a0e3ea9e4aa9738ac12ff02822d Author: Colin Watson Date: Thu Feb 24 16:04:18 2022 +0000 Improve detection of -fzero-call-used-regs=all support GCC doesn't tell us whether this option is supported unless it runs into the situation where it would need to emit corresponding code. commit 3383b2cac0e9275bc93c4b4760e6e048f537e1d6 Author: djm@openbsd.org Date: Wed Feb 23 21:21:49 2022 +0000 upstream: free(3) wants stdlib.h OpenBSD-Commit-ID: 227a8c70a95b4428c49e46863c9ef4bd318a3b8a commit a4537e79ab4ac6db4493c5158744b9ebde5efcb0 Author: djm@openbsd.org Date: Wed Feb 23 21:21:16 2022 +0000 upstream: put back the scp manpage changes for SFTP mode too OpenBSD-Commit-ID: 05dc53921f927e1b5e5694e1f3aa314549f2e768 commit 449bcb8403adfb9724805d02a51aea76046de185 Author: deraadt@openbsd.org Date: Wed Feb 23 19:01:00 2022 +0000 upstream: and we go back to testing sftp-scp after the 8.9 release... OpenBSD-Commit-ID: a80440168258adca543a4607b871327a279c569c commit 166456cedad3962b83b848b1e9caf80794831f0f Author: Damien Miller Date: Wed Feb 23 22:31:11 2022 +1100 makedepend commit 32ebaa0dbca5d0bb86e384e72bebc153f48413e4 Author: djm@openbsd.org Date: Wed Feb 23 11:18:13 2022 +0000 upstream: avoid integer overflow of auth attempts (harmless, caught by monitor) OpenBSD-Commit-ID: 488ad570b003b21e0cd9e7a00349cfc1003b4d86 commit 6e0258c64c901753df695e06498b26f9f4812ea6 Author: djm@openbsd.org Date: Wed Feb 23 11:17:10 2022 +0000 upstream: randomise the password used in fakepw OpenBSD-Commit-ID: 34e159f73b1fbf0a924a9c042d8d61edde293947 commit bf114d6f0a9df0b8369823d9a0daa6c72b0c4cc9 Author: djm@openbsd.org Date: Wed Feb 23 11:15:57 2022 +0000 upstream: use asprintf to construct .rhosts paths OpenBSD-Commit-ID: 8286e8d3d2c6ff916ff13d041d1713073f738a8b commit c07e154fbdc7285e9ec54e78d8a31f7325d43537 Author: djm@openbsd.org Date: Wed Feb 23 11:07:09 2022 +0000 upstream: openssh-8.9 OpenBSD-Commit-ID: 5c5f791c87c483cdab6d9266b43acdd9ca7bde0e commit bc16667b4a1c3cad7029304853c143a32ae04bd4 Author: Darren Tucker Date: Tue Feb 22 15:29:22 2022 +1100 Extend select+rlimit sanbox test to include poll. POSIX specifies that poll() shall fail if "nfds argument is greater than {OPEN_MAX}". The setrlimit sandbox sets this to effectively zero so this causes poll() to fail in the preauth privsep process. This is likely the underlying cause for the previously observed similar behaviour of select() on plaforms where it is implement in userspace on top of poll(). commit 6520c488de95366be031d49287ed243620399e23 Author: Darren Tucker Date: Tue Feb 22 13:08:59 2022 +1100 Add Alpine Linux test VM. commit a4b325a3fc82d11e0f5d61f62e7fde29415f7afb Author: Darren Tucker Date: Tue Feb 22 12:27:07 2022 +1100 Include sys/param.h if present. Needed for howmany() on MUSL systems such as Alpine. commit 5a102e9cb287a43bd7dfe594b775a89a8e94697c Author: Darren Tucker Date: Tue Feb 22 12:25:52 2022 +1100 Only include sys/poll.h if we don't have poll.h. Prevents warnings on MUSL based systems such as Alpine. commit 7c0d4ce911d5c58b6166b2db754a4e91f352adf5 Author: Damien Miller Date: Tue Feb 22 11:14:51 2022 +1100 disable agent-restrict test on minix3 Minix seems to have a platform-wide limit on the number of select(2) syscalls that can be concurrently issued. This test seems to exceed this limit. Refer to: https://github.com/Stichting-MINIX-Research-Foundation/minix/blob/R3.3.0/minix/servers/vfs/select.c#L114 https://github.com/Stichting-MINIX-Research-Foundation/minix/blob/R3.3.0/minix/servers/vfs/select.c#L30-L31 commit 81d33d8e3cf7ea5ce3a5653c6102b623e019428a Author: Darren Tucker Date: Mon Feb 21 21:27:20 2022 +1100 Skip agent-getpeereid when running as root. commit fbd772570a25436a33924d91c164d2b24021f010 Author: dtucker@openbsd.org Date: Sun Feb 20 03:47:26 2022 +0000 upstream: Aproximate realpath on the expected output by deduping leading slashes. Fixes test failure when user's home dir is / which is possible in some portable configurations. OpenBSD-Regress-ID: 53b8c53734f8893806961475c7106397f98d9f63 commit 336685d223a59f893faeedf0a562e053fd84058e Author: Darren Tucker Date: Sun Feb 20 13:30:52 2022 +1100 Really move DSA to end of list. In commit ad16a84e syncing from OpenBSD, RSA was accidentally moved to the end of the list instead of DSA. Spotted by andrew at fyfe.gb.net. commit 63bf4f49ed2fdf2da6f97136c9df0c8168546eb3 Author: Darren Tucker Date: Fri Feb 18 12:12:21 2022 +1100 Add test configs for MUSL C library. commit f7fc6a43f1173e8b2c38770bf6cee485a562d03b Author: Damien Miller Date: Thu Feb 17 22:54:19 2022 +1100 minix needs BROKEN_POLL too; chokes on /dev/null commit 667fec5d4fe4406745750a32f69b5d2e1a75e94b Author: djm@openbsd.org Date: Thu Feb 17 10:58:27 2022 +0000 upstream: check for EINTR/EAGAIN failures in the rfd fast-path; caught by dtucker's minix3 vm :) ok dtucker@ OpenBSD-Commit-ID: 2e2c895a3e82ef347aa6694394a76a438be91361 commit 41417dbda9fb55a0af49a8236e3ef9d50d862644 Author: Darren Tucker Date: Thu Feb 17 22:05:29 2022 +1100 Comment hurd test, the VM is currently broken. commit b2aee35a1f0dc798339b3fcf96136da71b7e3f6d Author: Damien Miller Date: Thu Feb 17 21:15:16 2022 +1100 find sk-dummy.so when build_dir != src_dir spotted by Corinna Vinschen; feedback & ok dtucker@ commit 62a2d4e50b2e89f2ef04576931895d5139a5d037 Author: Damien Miller Date: Wed Feb 16 16:26:17 2022 +1100 update versions in preparation for 8.9 release commit dd6d3dded721ac653ea73c017325e5bfeeec837f Author: djm@openbsd.org Date: Tue Feb 15 05:13:36 2022 +0000 upstream: document the unbound/host-bound options to PubkeyAuthentication; spotted by HARUYAMA Seigo OpenBSD-Commit-ID: 298f681b66a9ecd498f0700082c7a6c46e948981 commit df93529dd727fdf2fb290700cd4f1adb0c3c084b Author: Darren Tucker Date: Mon Feb 14 14:19:40 2022 +1100 Test if sshd accidentally acquires controlling tty When SSHD_ACQUIRES_CTTY is defined, test for the problematic behaviour in the STREAMS code before activating the workaround. ok djm@ commit 766176cfdbfd7ec38bb6118dde6e4daa0df34888 Author: Darren Tucker Date: Sat Feb 12 10:24:56 2022 +1100 Add cygwin-release test config. This tests the flags used to build the cygwin release binaries. commit b30698662b862f5397116d23688aac0764e0886e Author: Darren Tucker Date: Fri Feb 11 21:00:35 2022 +1100 Move SSHD_ACQUIRES_CTTY workaround into compat. On some (most? all?) SysV based systems with STREAMS based ptys, sshd could acquire a controlling terminal during pty setup when it pushed the "ptem" module, due to what is probably a bug in the STREAMS driver that's old enough to vote. Because it was the privileged sshd's controlling terminal, it was not available for the user's session, which ended up without one. This is known to affect at least Solaris <=10, derivatives such as OpenIndiana and several other SysV systems. See bz#245 for the backstory. In the we past worked around that by not calling setsid in the privileged sshd child, which meant it was not a session or process group leader. This solved controlling terminal problem because sshd was not eligble to acquire one, but had other side effects such as not cleaning up helper subprocesses in the SIGALRM handler since it was not PG leader. Recent cleanups in the signal handler uncovered this, resulting in the LoginGraceTime timer not cleaning up privsep unprivileged processes. This change moves the workaround into the STREAMS pty allocation code, by allocating a sacrificial pty to act as sshd's controlling terminal before allocating user ptys, so those are still available for users' sessions. On the down side: - this will waste a pty per ssh connection on affected platforms. On the up side: - it makes the process group behaviour consistent between platforms. - it puts the workaround nearest the code that actually causes the problem and competely out of the mainline code. - the workaround is only activated if you use the STREAMS code. If, say, Solaris 11 has the bug but also a working openpty() it doesn't matter that we defined SSHD_ACQUIRES_CTTY. - the workaround is only activated when the fist pty is allocated, ie in the post-auth privsep monitor. This means there's no risk of fd leaks to the unprivileged processes, and there's no effect on sessions that do not allocate a pty. Based on analysis and work by djm@, ok djm@ commit cd00b48cf10f3565936a418c1e6d7e48b5c36140 Author: Darren Tucker Date: Fri Feb 11 20:09:32 2022 +1100 Simplify handling of --with-ssl-dir. ok djm@ commit ea13fc830fc0e0dce2459f1fab2ec5099f73bdf0 Author: Darren Tucker Date: Fri Feb 11 13:39:29 2022 +1100 Stop testing OpenBSD HEAD on 6.9 and 7.0. HEAD is not guaranteed to work on previous stable branches, and at the moment is broken due to libfido API changes. commit 50b9e4a4514697ffb9592200e722de6b427cb9ff Author: dtucker@openbsd.org Date: Fri Feb 11 00:43:56 2022 +0000 upstream: Always initialize delim before passing to hpdelim2 which might not set it. Found by the Valgrind tests on github, ok deraadt@ OpenBSD-Commit-ID: c830c0db185ca43beff3f41c19943c724b4f636d commit 6ee53064f476cf163acd5521da45b11b7c57321b Author: Darren Tucker Date: Fri Feb 11 10:03:06 2022 +1100 Fix helper include path and remove excess code. Looks like test_hpdelim.c was imported twice into the same file. Spotted by kevin.brott at gmail com and chris at cataclysmal org. commit 9fa63a19f68bc87452d3cf5c577cafad2921b7a4 Author: Darren Tucker Date: Thu Feb 10 23:27:02 2022 +1100 Put poll.h inside ifdef. commit 3ac00dfeb54b252c15dcbf1971582e9e3b946de6 Author: Darren Tucker Date: Thu Feb 10 22:17:31 2022 +1100 We now support POLLPRI so actually define it. commit 25bd659cc72268f2858c5415740c442ee950049f Author: dtucker@openbsd.org Date: Sun Feb 6 22:58:33 2022 +0000 upstream: Add test for empty hostname with port. OpenBSD-Regress-ID: e19e89d3c432b68997667efea44cf015bbe2a7e3 commit a29af853cff41c0635f0378c00fe91bf9c91dea4 Author: dtucker@openbsd.org Date: Fri Feb 4 07:53:44 2022 +0000 upstream: Add unit tests for hpdelim. OpenBSD-Regress-ID: be97b85c19895e6a1ce13c639765a3b48fd95018 commit 9699151b039ecc5fad9ac6c6c02e9afdbd26f15f Author: djm@openbsd.org Date: Thu Feb 10 04:12:38 2022 +0000 upstream: revert for imminent OpenSSH release, which wil ship with scp in RCP mode. > revision 1.106 > date: 2021/10/15 14:46:46; author: deraadt; state: Exp; lines: +13 -9; commitid: w5n9B2RE38tFfggl; > openbsd 7.0 release shipped with the (hopefully last) scp that uses RCP > protocol for copying. Let's get back to testing the SFTP protocol. This will be put back once the OpenSSH release is done. OpenBSD-Commit-ID: 0c725481a78210aceecff1537322c0b2df03e768 commit 45279abceb37c3cbfac8ba36dde8b2c8cdd63d32 Author: dtucker@openbsd.org Date: Tue Feb 8 08:59:12 2022 +0000 upstream: Switch hpdelim interface to accept only ":" as delimiter. Historicallly, hpdelim accepted ":" or "/" as a port delimiter between hosts (or addresses) and ports. These days most of the uses for "/" are no longer accepted, so there are several places where it checks the delimiter to disallow it. Make hpdelim accept only ":" and use hpdelim2 in the other cases. ok djm@ OpenBSD-Commit-ID: 7e6420bd1be87590b6840973f5ad5305804e3102 commit a1bcbf04a7c2d81944141db7ecd0ba292d175a66 Author: pedro martelletto Date: Mon Feb 7 09:09:59 2022 +0100 fix typos in previous commit 56192518e329b39f063487bc2dc4d796f791eca0 Author: Damien Miller Date: Mon Feb 7 12:53:47 2022 +1100 compat code for fido_assert_set_clientdata() commit d6b5aa08fdcf9b527f8b8f932432941d5b76b7ab Author: djm@openbsd.org Date: Mon Feb 7 01:25:12 2022 +0000 upstream: use libfido2 1.8.0+ fido_assert_set_clientdata() instead of manually hashing data outselves. Saves a fair bit of code and makes life easier for some -portable platforms. OpenBSD-Commit-ID: 351dfaaa5ab1ee928c0e623041fca28078cff0e0 commit 86cc93fd3c26b2e0c7663c6394995fb04ebfbf3b Author: jsg@openbsd.org Date: Sun Feb 6 00:29:03 2022 +0000 upstream: remove please from manual pages ok jmc@ sthen@ millert@ OpenBSD-Commit-ID: 6543acb00f4f38a23472538e1685c013ca1a99aa commit ad16a84e64a8cf1c69c63de3fb9008320a37009c Author: dtucker@openbsd.org Date: Fri Feb 4 02:49:17 2022 +0000 upstream: Since they are deprecated, move DSA to the end of the default list of public keys so that they will be tried last. From github PR#295 from "ProBackup-nl", ok djm@ OpenBSD-Commit-ID: 7e5d575cf4971d4e2de92e0b6d6efaba53598bf0 commit 253de42753de85dde266e061b6fec12ca6589f7d Author: Damien Miller Date: Wed Feb 2 16:52:07 2022 +1100 portable-specific string array constification from Mike Frysinger commit dfdcc2220cf359c492d5d34eb723370e8bd8a19e Author: djm@openbsd.org Date: Tue Feb 1 23:37:15 2022 +0000 upstream: test 'ssh-keygen -Y find-principals' with wildcard principals; from Fabian Stelzer OpenBSD-Regress-ID: fbe4da5f0032e7ab496527a5bf0010fd700f8f40 commit 968e508967ef42480cebad8cf3172465883baa77 Author: dtucker@openbsd.org Date: Fri Jan 21 02:54:41 2022 +0000 upstream: Enable all supported ciphers and macs in the server before trying to benchmark them. Increase the data file size to get more signal. OpenBSD-Regress-ID: dc3697d9f7defdfc51c608782c8e750128e46eb6 commit 15b7199a1fd37eff4c695e09d573f3db9f4274b7 Author: djm@openbsd.org Date: Tue Feb 1 23:34:47 2022 +0000 upstream: allow 'ssh-keygen -Y find-principals' to match wildcard principals in allowed_signers files; from Fabian Stelzer OpenBSD-Commit-ID: 1e970b9c025b80717dddff5018fe5e6f470c5098 commit 541667fe6dc26d7881e55f0bb3a4baa6f3171645 Author: djm@openbsd.org Date: Tue Feb 1 23:32:51 2022 +0000 upstream: mark const string array contents const too, i.e. static const char *array => static const char * const array from Mike Frysinger OpenBSD-Commit-ID: a664e31ea6a795d7c81153274a5f47b22bdc9bc1 commit 8cfa73f8a2bde4c98773f33f974c650bdb40dd3c Author: djm@openbsd.org Date: Tue Feb 1 23:11:11 2022 +0000 upstream: better match legacy scp behaviour: show un-expanded paths in error messages. Spotted by and ok tb@ OpenBSD-Commit-ID: 866c8ffac5bd7d38ecbfc3357c8adfa58af637b7 commit 4e62c13ab419b4b224c8bc6a761e91fcf048012d Author: dtucker@openbsd.org Date: Tue Feb 1 07:57:32 2022 +0000 upstream: Remove explicit kill of privsep preauth child's PID in SIGALRM handler. It's no longer needed since the child will get terminated by the SIGTERM to the process group that cleans up any auth helpers, it simplifies the signal handler and removes the risk of a race when updating the PID. Based on analysis by HerrSpace in github PR#289, ok djm@ OpenBSD-Commit-ID: 2be1ffa28b4051ad9e33bb4371e2ec8a31d6d663 commit 2a7ccd2ec4022917b745af7186f514f365b7ebe9 Author: guenther@openbsd.org Date: Fri Jan 28 06:18:42 2022 +0000 upstream: When it's the possessive of 'it', it's spelled "its", without the apostrophe. OpenBSD-Commit-ID: fb6ab9c65bd31de831da1eb4631ddac018c5fae7 commit 8a0848cdd3b25c049332cd56034186b7853ae754 Author: Alex James Date: Sun Jan 30 16:13:36 2022 -0600 sandbox-seccomp-filter: allow gettid Some allocators (such as Scudo) use gettid while tracing allocations [1]. Allow gettid in preauth to prevent sshd from crashing with Scudo. [1]: https://github.com/llvm/llvm-project/blob/llvmorg-13.0.0/compiler-rt/lib/gwp_asan/common.cpp#L46 commit b30d32159dc3c7052f4bfdf36357996c905af739 Author: djm@openbsd.org Date: Sat Jan 22 00:49:34 2022 +0000 upstream: add a ssh_packet_process_read() function that reads from a fd directly into the transport input buffer. Use this in the client and server mainloops to avoid unnecessary copying. It also lets us use a more greedy read size without penalty. Yields a 2-3% performance gain on cipher-speed.sh (in a fairly unscientific test tbf) feedback dtucker@ ok markus@ OpenBSD-Commit-ID: df4112125bf79d8e38e79a77113e1b373078e632 commit a1a8efeaaa9cccb15cdc0a2bd7c347a149a3a7e3 Author: djm@openbsd.org Date: Sat Jan 22 00:45:31 2022 +0000 upstream: Use sshbuf_read() to read directly into the channel input buffer rather than into a stack buffer that needs to be copied again; Improves performance by about 1% on cipher-speed.sh feedback dtucker@ ok markus@ OpenBSD-Commit-ID: bf5e6e3c821ac3546dc8241d8a94e70d47716572 commit 29a76994e21623a1f84d68ebb9dc5a3c909fa3a7 Author: Damien Miller Date: Tue Jan 25 11:52:34 2022 +1100 depend commit 754e0d5c7712296a7a3a83ace863812604c7bc4f Author: djm@openbsd.org Date: Sat Jan 22 00:43:43 2022 +0000 upstream: Add a sshbuf_read() that attempts to read(2) directly in to a sshbuf; ok markus@ OpenBSD-Commit-ID: 2d8f249040a4279f3bc23c018947384de8d4a45b commit c7964fb9829d9ae2ece8b51a76e4a02e8449338d Author: djm@openbsd.org Date: Fri Jan 21 07:04:19 2022 +0000 upstream: add a helper for writing an error message to the stderr_buf and setting quit_pending; no functional change but saves a bunch of boilerplate OpenBSD-Commit-ID: 0747657cad6b9eabd514a6732adad537568e232d commit d23b4f7fdb1bd87e2cd7a9ae7c198ae99d347916 Author: djm@openbsd.org Date: Fri Jan 21 06:58:06 2022 +0000 upstream: correct comment and use local variable instead of long indirection; spotted by dtucker@ OpenBSD-Commit-ID: 5f65f5f69db2b7d80a0a81b08f390a63f8845965 commit d069b020a02b6e3935080204ee44d233e8158ebb Author: deraadt@openbsd.org Date: Fri Jan 21 00:53:40 2022 +0000 upstream: When poll(2) returns -1, for some error conditions pfd[].revents is not cleared. There are subtle errors in various programs. In this particular case, the program should error out. ok djm millert OpenBSD-Commit-ID: 00f839b16861f7fb2adcf122e95e8a82fa6a375c commit e204b34337a965feb439826157c191919fd9ecf8 Author: Damien Miller Date: Sat Jan 22 11:38:21 2022 +1100 restore tty force-read hack This portable-specific hack fixes a hang on exit for ttyful sessions on Linux and some SysVish Unix variants. It was accidentally disabled in commit 5c79952dfe1a (a precursor to the mainloop poll(2) conversion). Spotted by John in bz3383 commit 68085066b6bad43643b43f5957fcc5fd34782ccd Author: Corinna Vinschen Date: Fri Jan 21 03:22:56 2022 +1100 Fix signedness bug in Cygwin code The Cygwin-specific pattern match code has a bug. It checks the size_t value returned by mbstowcs for being < 0. The right thing to do is to check against (size_t) -1. Fix that. Signed-off-by: Corinna Vinschen commit 2e5cfed513e84444483baf1d8b31c40072b05103 Author: Darren Tucker Date: Thu Jan 20 13:26:27 2022 +1100 Improve compatibility of early exit trap handling. Dash (as used by the github runners) has some differences in its trap builtin: - it doesn't have -p (which is fine, that's not in posix). - it doesn't work in a subshell (which turns out to be in compliance with posix, which means bash isn't). - it doesn't work in a pipeline, ie "trap|cat" produces no output. commit 3fe6800b6027add478e648934cbb29d684e51943 Author: Darren Tucker Date: Thu Jan 20 00:49:57 2022 +1100 Move more tests out of valgrind-1 runner. commit 20da6ed136dd76e6a0b229ca3036ef9c7c7ef798 Author: Darren Tucker Date: Wed Jan 19 15:37:39 2022 +1100 Invoke EXIT handler early when using Valgrind. When using Valgrind, we need to wait for all invoked programs to complete before checking their valgrind logs. Some tests, notably agent-restrict, set an EXIT trap handler to clean up things like ssh-agent, but those do not get invoked until test-exec.sh exits. This causes the Valgrind wait to deadlock, so if present invoke the EXIT handler before checking the Valgrind logs. commit ad2e0580c87b0714cf166bca9d926a95ddeee1c8 Author: Darren Tucker Date: Tue Jan 18 12:55:21 2022 +1100 Remove line leftover from upstream sync. commit d1051c0f11a6b749027e26bbeb61b07df4b67e15 Author: djm@openbsd.org Date: Mon Jan 17 22:56:04 2022 +0000 upstream: when decompressing zlib compressed packets, use Z_SYNC_FLUSH instead of Z_PARTIAL_FLUSH as the latter is not actually specified as a valid mode for inflate(). There should be no practical change in behaviour as the compression side ensures a flush that should make all data available to the receiver in all cases. repoted by lamm AT ibm.com via bz3372; ok markus OpenBSD-Commit-ID: 67cfc1fa8261feae6d2cc0c554711c97867cc81b commit d5981b1883746b1ae178a46229c26b53af99e37a Author: djm@openbsd.org Date: Mon Jan 17 21:41:04 2022 +0000 upstream: make most of the sftp errors more idiomatic, following the general form of "[local/remote] operation path: error message"; ok markus OpenBSD-Commit-ID: 61364cd5f3a9fecaf8d63b4c38a42c0c91f8b571 commit ac7c9ec894ed0825d04ef69c55babb49bab1d32e Author: djm@openbsd.org Date: Mon Jan 17 21:39:51 2022 +0000 upstream: when transferring multiple files in SFTP mode, create the destination directory if it doesn't already exist to match olde-scp(1) behaviour. noticed by deraadt@ ok markus@ OpenBSD-Commit-ID: cf44dfa231d4112f697c24ff39d7ecf2e6311407 commit 39d17e189f8e72c34c722579d8d4e701fa5132da Author: djm@openbsd.org Date: Fri Jan 14 03:43:48 2022 +0000 upstream: allow pin-required FIDO keys to be added to ssh-agent(1). ssh-askpass will be used to request the PIN at authentication time. From Pedro Martelletto, ok djm OpenBSD-Commit-ID: de8189fcd35b45f632484864523c1655550e2950 commit 52423f64e13db2bdc31a51b32e999cb1bfcf1263 Author: djm@openbsd.org Date: Fri Jan 14 03:35:10 2022 +0000 upstream: ssh-sk: free a resident key's user id From Pedro Martelletto; ok dtucker & me OpenBSD-Commit-ID: 47be40d602b7a6458c4c71114df9b53d149fc2e9 commit 014e2f147a2788bfb3cc58d1b170dcf2bf2ee493 Author: djm@openbsd.org Date: Fri Jan 14 03:34:00 2022 +0000 upstream: sshsk_load_resident: don't preallocate resp resp is allocated by client_converse(), at which point we lose the original pointer. From Pedro Martelletto; ok dtucker & me OpenBSD-Commit-ID: 1f1b5ea3282017d6584dfed4f8370dc1db1f44b1 commit c88265f207dfe0e8bdbaf9f0eda63ed6b33781cf Author: djm@openbsd.org Date: Fri Jan 14 03:32:52 2022 +0000 upstream: sshsk_sign: trim call to sshkey_fingerprint() the resulting fingerprint doesn't appear to be used for anything, and we end up leaking it. from Pedro Martelletto; ok dtucker & me OpenBSD-Commit-ID: 5625cf6c68f082bc2cbbd348e69a3ed731d2f9b7 commit 1cd1b2eac39661b849d5a4b4b56363e22bb5f61e Author: djm@openbsd.org Date: Fri Jan 14 03:31:52 2022 +0000 upstream: use status error message to communicate ~user expansion failures; provides better experience for scp in sftp mode, where ~user paths are more likely to be used; spotted jsg, feedback jsg & deraadt ok jsg & markus (forgot to include this file in previous commit) OpenBSD-Commit-ID: d37cc4c8c861ce48cd6ea9899e96aaac3476847b commit a1d42a6ce0398da3833bedf374ef2571af7fea50 Author: Damien Miller Date: Fri Jan 14 13:49:32 2022 +1100 fix edge case in poll(2) wrapper Correct handling of select(2) exceptfds. These should only be consulted for POLLPRI flagged pfds and not unconditionally converted to POLLERR. with and ok dtucker@ commit 976b9588b4b5babcaceec4767a241c11a67a5ccb Author: Darren Tucker Date: Fri Jan 14 13:46:35 2022 +1100 Wrap OpenSSL includes in unit tests in ifdef. Fixes unit test on systems that do not have OpenSSL headers installed. commit c171879374b2e8b07157503f5639ed0bce59ce89 Author: Darren Tucker Date: Thu Jan 13 15:53:33 2022 +1100 Remove sort wrapper. agent-restrict now takes care of this itself. commit 9cc2654403f1a686bb26c07a6ac790edf334cef5 Author: dtucker@openbsd.org Date: Thu Jan 13 04:53:16 2022 +0000 upstream: Set LC_ALL in both local and remote shells so that sorted output matches regardless of what the user's shell sets it to. ok djm@ OpenBSD-Regress-ID: 4e97dd69a68b05872033175a4c2315345d01837f commit 7a75f748cb2dd2f771bf70ea72698aa027996ab1 Author: dtucker@openbsd.org Date: Thu Jan 13 04:22:10 2022 +0000 upstream: Avoid %'s in commands (not used in OpenBSD, but used in -portable's Valgrind test) being interpretted as printf format strings. OpenBSD-Regress-ID: dc8655db27ac4acd2c386c4681bf42a10d80b043 commit 6c435bd4994d71442192001483a1cdb846e5ffcd Author: Darren Tucker Date: Wed Jan 12 16:58:13 2022 +1100 Stop on first test failure to minimize logs. commit 4bc2ba6095620a4484b708ece12842afd8c7685b Author: dtucker@openbsd.org Date: Wed Jan 12 07:18:37 2022 +0000 upstream: Use egrep when searching for an anchored string. OpenBSD-Regress-ID: dd114a2ac27ac4b06f9e4a586d3f6320c54aeeb4 commit 6bf2efa2679da1e8e60731f41677b2081dedae2c Author: Darren Tucker Date: Wed Jan 12 18:25:06 2022 +1100 Add "rev" command replacement if needed. commit 72bcd7993dadaf967bb3d8564ee31cbf38132b5d Author: dtucker@openbsd.org Date: Wed Jan 12 03:30:32 2022 +0000 upstream: Don't log NULL hostname in restricted agent code, printf("%s", NULL) is not safe on all platforms. with & ok djm OpenBSD-Commit-ID: faf10cdae4adde00cdd668cd1f6e05d0a0e32a02 commit acabefe3f8fb58c867c99fed9bbf84dfa1771727 Author: djm@openbsd.org Date: Tue Jan 11 22:33:16 2022 +0000 upstream: remove hardcoded domain and use window.location.host, so this can be run anywhere OpenBSD-Regress-ID: 2ac2ade3b6227d9c547351d3ccdfe671e62b7f92 commit 96da0946e44f34adc0397eb7caa6ec35a3e79891 Author: dtucker@openbsd.org Date: Tue Jan 11 02:56:19 2022 +0000 upstream: "void" functions should not return anything. From Tim Rice via -portable. OpenBSD-Commit-ID: ce6616304f4c9881b46413e616b226c306830e2a commit a882a09722c9f086c9edb65d0c4022fd965ec1ed Author: djm@openbsd.org Date: Tue Jan 11 01:26:47 2022 +0000 upstream: suppress "Connection to xxx closed" messages at LogLevel >= error bz3378; ok dtucker@ OpenBSD-Commit-ID: d5bf457d5d2eb927b81d0663f45248a31028265c commit 61a1a6af22e17fc94999a5d1294f27346e6c4668 Author: Damien Miller Date: Wed Jan 12 08:57:49 2022 +1100 OS X poll(2) is broken; use compat replacement Darwin's poll(2) implementation is broken. For character-special devices like /dev/null, it returns POLLNVAL when polled with POLLIN. Apparently this is Apple bug 3710161, which is AFAIK not public, but a websearch will find other OSS projects rediscovering it periodically since it was first identified in 2005 (!!) commit 613a6545fc5a9542753b503cbe5906538a640b60 Author: Darren Tucker Date: Tue Jan 11 20:56:01 2022 +1100 libhardended_malloc.so moved into out dir. commit 61761340be5e11046556623f8f5412b236cefa95 Author: Tim Rice Date: Mon Jan 10 11:07:04 2022 -0800 Make USL compilers happy UX:acomp: ERROR: "sftp-server.c", line 567: void function cannot return value commit 3ef403f351e80a59b6f7e9d43cb82c181855483c Author: Darren Tucker Date: Mon Jan 10 21:07:38 2022 +1100 Add wrapper for "sort" to set LC_ALL=C. Found by djm, this should make sorts stable and reduce test flakiness. commit bd69e29f5716090181dbe0b8272eb7eab1a383bb Author: dtucker@openbsd.org Date: Sat Jan 8 07:55:26 2022 +0000 upstream: Remove errant "set -x" left over from debugging. OpenBSD-Regress-ID: cd989268e034264cec5df97be7581549032c87dc commit 1a7c88e26fd673813dc5f61c4ac278564845e004 Author: dtucker@openbsd.org Date: Sat Jan 8 07:01:13 2022 +0000 upstream: Enable all supported hostkey algorithms (but no others). Allows hostbased test to pass when built without OpenSSL. OpenBSD-Regress-ID: 5ddd677a68b672517e1e78460dc6ca2ccc0a9562 commit 12b457c2a42ff271e7967d9bedd068cebb048db9 Author: djm@openbsd.org Date: Sat Jan 8 07:37:32 2022 +0000 upstream: use status error message to communicate ~user expansion failures; provides better experience for scp in sftp mode, where ~user paths are more likely to be used; spotted jsg, feedback jsg & deraadt ok jsg & markus OpenBSD-Commit-ID: fc610ce00ca0cdc2ecdabbd49ce7cb82033f905f commit 63670d4e9030bcee490d5a9cce561373ac5b3b23 Author: djm@openbsd.org Date: Sat Jan 8 07:36:11 2022 +0000 upstream: fix some corner-case bugs in scp sftp-mode handling of ~-prefixed paths; spotted by jsg; feedback jsg & deraadt, ok jsg & markus OpenBSD-Commit-ID: d1697dbaaa9f0f5649d69be897eab25c7d37c222 commit e14940bbec57fc7d3ce0644dbefa35f5a8ec97d0 Author: djm@openbsd.org Date: Sat Jan 8 07:34:57 2022 +0000 upstream: more idiomatic error messages; spotted by jsg & deraadt ok jsg & markus OpenBSD-Commit-ID: 43618c692f3951747b4151c477c7df22afe2bcc8 commit 9acddcd5918c623f7ebf454520ffe946a8f15e90 Author: djm@openbsd.org Date: Sat Jan 8 07:33:54 2022 +0000 upstream: add a variant of send_status() that allows overriding the default, generic error message. feedback/ok markus & jsg OpenBSD-Commit-ID: 81f251e975d759994131b717ee7c0b439659c40f commit 961411337719d4cd78f1ab33e4ac549f3fa22f50 Author: djm@openbsd.org Date: Sat Jan 8 07:32:45 2022 +0000 upstream: refactor tilde_expand_filename() and make it handle ~user paths with no trailing slash; feedback/ok markus and jsg OpenBSD-Commit-ID: a2ab365598a902f0f14ba6a4f8fb2d07a9b5d51d commit dc38236ab6827dec575064cac65c8e7035768773 Author: dtucker@openbsd.org Date: Thu Jan 6 22:14:25 2022 +0000 upstream: Don't explicitly set HostbasedAuthentication in sshd_config. It defaults to "no", and not explicitly setting it allows us to enable it for the (optional) hostbased test. OpenBSD-Regress-ID: aa8e3548eb5793721641d26e56c29f363b767c0c commit e12d912ddf1c873cb72e5de9a197afbe0b6622d2 Author: dtucker@openbsd.org Date: Thu Jan 6 21:46:56 2022 +0000 upstream: Add test for hostbased auth. It requires some external setup (see comments at the top) and thus is disabled unless TEST_SSH_HOSTBASED_AUTH and SUDO are set. OpenBSD-Regress-ID: 3ec8ba3750c5b595fc63e7845d13483065a4827a commit a48533a8da6a0f4f05ecd055dc8048047e53569e Author: Damien Miller Date: Fri Jan 7 09:24:26 2022 +1100 depend commit d9dbb5d9a0326e252d3c7bc13beb9c2434f59409 Author: djm@openbsd.org Date: Thu Jan 6 22:06:51 2022 +0000 upstream: allow hostbased auth to select RSA keys when only RSA/SHA2 are configured (this is the default case); ok markus@ OpenBSD-Commit-ID: 411c18c7bde40c60cc6dfb7017968577b4d4a827 commit fdb1d58d0d3888b042e5a500f6ce524486aaf782 Author: djm@openbsd.org Date: Thu Jan 6 22:05:42 2022 +0000 upstream: add a helper function to match a key type to a list of signature algorithms. RSA keys can make signatures with multiple algorithms, so some special handling is required. ok markus@ OpenBSD-Commit-ID: 03b41b2bda06fa4cd9c84cef6095033b9e49b6ff commit 11e8c4309a5086a45fbbbc87d0af5323c6152914 Author: djm@openbsd.org Date: Thu Jan 6 22:04:20 2022 +0000 upstream: log some details on hostkeys that ssh loads for hostbased authn ok markus@ OpenBSD-Commit-ID: da17061fa1f0e58cb31b88478a40643e18233e38 commit c6706f661739514a34125aa3136532a958929510 Author: djm@openbsd.org Date: Thu Jan 6 22:03:59 2022 +0000 upstream: log signature algorithm during verification by monitor; ok markus OpenBSD-Commit-ID: 02b92bb42c4d4bf05a051702a56eb915151d9ecc commit 8832402bd500d1661ccc80a476fd563335ef6cdc Author: djm@openbsd.org Date: Thu Jan 6 22:02:52 2022 +0000 upstream: piece of UpdateHostkeys client strictification: when updating known_hosts with new keys, ignore NULL keys (forgot to include in prior commit) OpenBSD-Commit-ID: 49d2eda6379490e1ceec40c3b670b973f63dea08 commit c2d9ced1da0276961d86690b3bd7ebdaca7fdbf7 Author: djm@openbsd.org Date: Thu Jan 6 22:01:14 2022 +0000 upstream: include rejected signature algorithm in error message and not the (useless) key type; ok markus OpenBSD-Commit-ID: 4180b5ec7ab347b43f84e00b1972515296dab023 commit 7aa7b096cf2bafe2777085abdeed5ce00581f641 Author: djm@openbsd.org Date: Thu Jan 6 22:00:18 2022 +0000 upstream: make ssh-keysign use the requested signature algorithm and not the default for the keytype. Part of unbreaking hostbased auth for RSA/SHA2 keys. ok markus@ OpenBSD-Commit-ID: b5639a14462948970da3a8020dc06f9a80ecccdc commit 291721bc7c840d113a49518f3fca70e86248b8e8 Author: djm@openbsd.org Date: Thu Jan 6 21:57:28 2022 +0000 upstream: stricter UpdateHostkey signature verification logic on the client- side. Require RSA/SHA2 signatures for RSA hostkeys except when RSA/SHA1 was explicitly negotiated during initial KEX; bz3375 ok markus@ OpenBSD-Commit-ID: 46e75e8dfa2c813781805b842580dcfbd888cf29 commit 0fa33683223c76289470a954404047bc762be84c Author: djm@openbsd.org Date: Thu Jan 6 21:55:23 2022 +0000 upstream: Fix signature algorithm selection logic for UpdateHostkeys on the server side. The previous code tried to prefer RSA/SHA2 for hostkey proofs of RSA keys, but missed some cases. This will use RSA/SHA2 signatures for RSA keys if the client proposed these algorithms in initial KEX. bz3375 Mostly by Dmitry Belyavskiy with some tweaks by me. ok markus@ OpenBSD-Commit-ID: c17ba0c3236340d2c6a248158ebed042ac6a8029 commit 17877bc81db3846e6e7d4cfb124d966bb9c9296b Author: djm@openbsd.org Date: Thu Jan 6 21:48:38 2022 +0000 upstream: convert ssh, sshd mainloops from select() to poll(); feedback & ok deraadt@ and markus@ has been in snaps for a few months OpenBSD-Commit-ID: a77e16a667d5b194dcdb3b76308b8bba7fa7239c commit 5c79952dfe1aa36105c93b3f383ce9be04dee384 Author: djm@openbsd.org Date: Thu Jan 6 21:46:23 2022 +0000 upstream: prepare for conversion of ssh, sshd mainloop from select() to poll() by moving FD_SET construction out of channel handlers into separate functions. ok markus OpenBSD-Commit-ID: 937fbf2a4de12b19fb9d5168424e206124807027 commit 24c5187edfef4651a625b7d5d692c8c7e794f71f Author: djm@openbsd.org Date: Wed Jan 5 21:54:37 2022 +0000 upstream: add a comment so I don't make this mistake again OpenBSD-Commit-ID: 69c7f2362f9de913bb29b6318580c5a1b52c921e commit 7369900441929058263a17f56aa67e05ff7ec628 Author: djm@openbsd.org Date: Wed Jan 5 21:50:00 2022 +0000 upstream: fix cut-and-pasto in error message OpenBSD-Commit-ID: 4cc5c619e4b456cd2e9bb760d17e3a9c84659198 commit 294c11b1c7d56d3fb61e329603a782315ed70c62 Author: djm@openbsd.org Date: Wed Jan 5 08:25:05 2022 +0000 upstream: select all RSA hostkey algorithms for UpdateHostkeys tests, not just RSA-SHA1 OpenBSD-Regress-ID: b40e62b65863f2702a0c10aca583b2fe76772bd8 commit 2ea1108c30e3edb6f872dfc1e6da10b041ddf2c0 Author: djm@openbsd.org Date: Wed Jan 5 04:56:15 2022 +0000 upstream: regress test both sshsig message hash algorithms, possible now because the algorithm is controllable via the CLI OpenBSD-Regress-ID: 0196fa87acc3544b2b4fd98de844a571cb09a39f commit 2327c306b5d4a2b7e71178e5a4d139af9902c2b0 Author: djm@openbsd.org Date: Wed Jan 5 04:50:11 2022 +0000 upstream: allow selection of hash at sshsig signing time; code already supported either sha512 (default) or sha256, but plumbing wasn't there mostly by Linus Nordberg OpenBSD-Commit-ID: 1b536404b9da74a84b3a1c8d0b05fd564cdc96cd commit 56e941d0a00d6d8bae88317717d5e1b7395c9529 Author: djm@openbsd.org Date: Wed Jan 5 04:27:54 2022 +0000 upstream: add missing -O option to usage() for ssh-keygen -Y sign; from Linus Nordberg OpenBSD-Commit-ID: 4e78feb4aa830727ab76bb2e3d940440ae1d7af0 commit 141a14ec9b0924709c98df2dd8013bde5d8d12c7 Author: djm@openbsd.org Date: Wed Jan 5 04:27:01 2022 +0000 upstream: move sig_process_opts() to before sig_sign(); no functional code change OpenBSD-Commit-ID: da02d61f5464f72b4e8b299f83e93c3b657932f9 commit 37a14249ec993599a9051731e4fb0ac5e976aec1 Author: djm@openbsd.org Date: Wed Jan 5 04:10:39 2022 +0000 upstream: regression test for find-principals NULL deref; from Fabian Stelzer OpenBSD-Regress-ID: f845a8632a5a7d5ae26978004c93e796270fd3e5 commit eb1f042142fdaba93f6c9560cf6c91ae25f6884a Author: djm@openbsd.org Date: Wed Jan 5 04:02:42 2022 +0000 upstream: NULL deref when using find-principals when matching an allowed_signers line that contains a namespace restriction, but no restriction specified on the command-line; report and fix from Fabian Stelzer OpenBSD-Commit-ID: 4a201b86afb668c908d1a559c6af456a61f4b145 commit 8f3b18030579f395eca2181da31a5f945af12a59 Author: dtucker@openbsd.org Date: Tue Jan 4 08:38:53 2022 +0000 upstream: Log command invocation while debugging. This will aid in manually reproducing failing commands. OpenBSD-Regress-ID: b4aba8d5ac5675ceebeeeefa3261ce344e67333a commit bbf285164df535f0d38c36237f007551bbdae27f Author: Darren Tucker Date: Sun Dec 26 10:31:15 2021 +1100 Always save config.h as build artifact. Should allow better comparison between failing and succeeding test platforms. commit 03bd4ed0db699687c5cd83405d26f81d2dc28d22 Author: Darren Tucker Date: Sat Dec 25 16:42:51 2021 +1100 Add OpenBSD 7.0 target. Retire 6.8. commit c45a752f0de611afd87755c2887c8a24816d08ee Author: jsg@openbsd.org Date: Sat Jan 1 05:55:06 2022 +0000 upstream: spelling OpenBSD-Commit-ID: c63e43087a64d0727af13409c708938e05147b62 commit c672f83a89a756564db0d3af9934ba0e1cf8fa3e Author: djm@openbsd.org Date: Tue Jan 4 07:20:33 2022 +0000 upstream: unbreak test: was picking up system ssh-add instead of the one supposedly being tested. Spotted by dtucker and using his VM zoo (which includes some systems old enough to lack ed25519 key support) OpenBSD-Regress-ID: 7976eb3df11cc2ca3af91030a6a8c0cef1590bb5 commit a23698c3082ffe661abed14b020eac9b0c25eb9f Author: djm@openbsd.org Date: Sat Jan 1 04:18:06 2022 +0000 upstream: fix memleak in process_extension(); oss-fuzz issue #42719 OpenBSD-Commit-ID: d8d49f840162fb7b8949e3a5adb8107444b6de1e commit cb885178f36b83d0f14cfe9f345d2068103feed0 Author: jsg@openbsd.org Date: Sat Jan 1 01:55:30 2022 +0000 upstream: spelling ok dtucker@ OpenBSD-Commit-ID: bfc7ba74c22c928de2e257328b3f1274a3dfdf19 commit 6b977f8080a32c5b3cbb9edb634b9d5789fb79be Author: djm@openbsd.org Date: Sun Dec 26 23:34:41 2021 +0000 upstream: split method list search functionality from authmethod_lookup() into a separate authmethod_byname(), for cases where we don't need to check whether a method is enabled, etc. use this to fix the "none" authentication method regression reported by Nam Nguyen via bugs@ ok deraadt@ OpenBSD-Commit-ID: 8cd188dc3a83aa8abe5b7693e762975cd8ea8a17 commit 0074aa2c8d605ee7587279a22cdad4270b4ddd07 Author: jmc@openbsd.org Date: Wed Dec 22 06:56:41 2021 +0000 upstream: sort -H and -h in SYNOPSIS/usage(); tweak the -H text; ok djm OpenBSD-Commit-ID: 90721643e41e9e09deb5b776aaa0443456ab0965 commit 1c9853a68b2319f2e5f929179735e8fbb9988a67 Author: Darren Tucker Date: Wed Dec 22 19:33:10 2021 +1100 Use SHA.*_HMAC_BLOCK_SIZE if needed. If the platform has a native SHA2, does not define SHA.*_BLOCK_LENGTH but does define SHA.*_HMAC_BLOCK_SIZE (eg Solaris) then use the latter. Should fix --without-openssl build on Solaris. commit 715c892f0a5295b391ae92c26ef4d6a86ea96e8e Author: Damien Miller Date: Wed Dec 22 09:02:50 2021 +1100 remove sys/param.h in -portable, after upstream commit 7a7c69d8b4022b1e5c0afb169c416af8ce70f3e8 Author: Damien Miller Date: Mon Dec 20 13:05:20 2021 +1100 add agent-restrict.sh file, missed in last commit commit f539136ca51a4976644db5d0be8158cc1914c72a Author: djm@openbsd.org Date: Sun Dec 19 22:20:12 2021 +0000 upstream: regression test for destination restrictions in ssh-agent OpenBSD-Regress-ID: 3c799d91e736b1753b4a42d80c42fc40de5ad33d commit 6e4980eb8ef94c04874a79dd380c3f568e8416d6 Author: anton@openbsd.org Date: Sat Dec 18 06:53:59 2021 +0000 upstream: Make use of ntests variable, pointed out by clang 13. OpenBSD-Regress-ID: 4241a3d21bdfa1630ed429b6d4fee51038d1be72 commit 3eead8158393b697f663ec4301e3c7b6f24580b1 Author: deraadt@openbsd.org Date: Tue Dec 14 21:25:27 2021 +0000 upstream: sys/param.h cleanup, mostly using MINIMUM() and ok dtucker OpenBSD-Regress-ID: 172a4c45d3bcf92fa6cdf6c4b9db3f1b3abe4db0 commit 266678e19eb0e86fdf865b431b6e172e7a95bf48 Author: djm@openbsd.org Date: Sun Dec 19 22:15:42 2021 +0000 upstream: document host-bound publickey authentication OpenBSD-Commit-ID: ea6ed91779a81f06d961e30ecc49316b3d71961b commit 3d00024b3b156aa9bbd05d105f1deb9cb088f6f7 Author: djm@openbsd.org Date: Sun Dec 19 22:15:21 2021 +0000 upstream: document agent protocol extensions OpenBSD-Commit-ID: 09e8bb391bbaf24c409b75a4af44e0cac65405a7 commit c385abf76511451bcba78568167b1cd9e90587d5 Author: djm@openbsd.org Date: Sun Dec 19 22:14:47 2021 +0000 upstream: PubkeyAuthentication=yes|no|unbound|host-bound Allow control over which pubkey methods are used. Added out of concern that some hardware devices may have difficulty signing the longer pubkey authentication challenges. This provides a way for them to disable the extension. It's also handy for testing. feedback / ok markus@ OpenBSD-Commit-ID: ee52580db95c355cf6d563ba89974c210e603b1a commit 34b1e9cc7654f41cd4c5b1cc290b999dcf6579bb Author: djm@openbsd.org Date: Sun Dec 19 22:14:12 2021 +0000 upstream: document destination-constrained keys feedback / ok markus@ OpenBSD-Commit-ID: cd8c526c77268f6d91c06adbee66b014d22d672e commit a6d7677c4abcfba268053e5867f2acabe3aa371b Author: djm@openbsd.org Date: Sun Dec 19 22:13:55 2021 +0000 upstream: Use hostkey parsed from hostbound userauth request Require host-bound userauth requests for forwarded SSH connections. The hostkey parsed from the host-bound userauth request is now checked against the most recently bound session ID / hostkey on the agent socket and the signature refused if they do not match. ok markus@ OpenBSD-Commit-ID: d69877c9a3bd8d1189a5dbdeceefa432044dae02 commit baaff0ff4357cc5a079621ba6e2d7e247b765061 Author: djm@openbsd.org Date: Sun Dec 19 22:13:33 2021 +0000 upstream: agent support for parsing hostkey-bound signatures Allow parse_userauth_request() to work with blobs from publickey-hostbound-v00@openssh.com userauth attempts. Extract hostkey from these blobs. ok markus@ OpenBSD-Commit-ID: 81c064255634c1109477dc65c3e983581d336df8 commit 3e16365a79cdeb2d758cf1da6051b1c5266ceed7 Author: djm@openbsd.org Date: Sun Dec 19 22:13:12 2021 +0000 upstream: EXT_INFO negotiation of hostbound pubkey auth the EXT_INFO packet gets a new publickey-hostbound@openssh.com to advertise the hostbound public key method. Client side support to parse this feature flag and set the kex->flags indicator if the expected version is offered (currently "0"). ok markus@ OpenBSD-Commit-ID: 4cdb2ca5017ec1ed7a9d33bda95c1d6a97b583b0 commit 94ae0c6f0e35903b695e033bf4beacea1d376bb1 Author: djm@openbsd.org Date: Sun Dec 19 22:12:54 2021 +0000 upstream: client side of host-bound pubkey authentication Add kex->flags member to enable the publickey-hostbound-v00@openssh.com authentication method. Use the new hostbound method in client if the kex->flags flag was set, and include the inital KEX hostkey in the userauth request. Note: nothing in kex.c actually sets the new flag yet ok markus@ OpenBSD-Commit-ID: 5a6fce8c6c8a77a80ee1526dc467d91036a5910d commit 288fd0218dbfdcb05d9fbd1885904bed9b6d42e6 Author: djm@openbsd.org Date: Sun Dec 19 22:12:30 2021 +0000 upstream: sshd side of hostbound public key auth This is identical to the standard "publickey" method, but it also includes the initial server hostkey in the message signed by the client. feedback / ok markus@ OpenBSD-Commit-ID: 7ea01bb7238a560c1bfb426fda0c10a8aac07862 commit dbb339f015c33d63484261d140c84ad875a9e548 Author: djm@openbsd.org Date: Sun Dec 19 22:12:07 2021 +0000 upstream: prepare for multiple names for authmethods allow authentication methods to have one additional name beyond their primary name. allow lookup by this synonym Use primary name for authentication decisions, e.g. for PermitRootLogin=publickey Pass actual invoked name to the authmethods, so they can tell whether they were requested via the their primary name or synonym. ok markus@ OpenBSD-Commit-ID: 9e613fcb44b8168823195602ed3d09ffd7994559 commit 39f00dcf44915f20684160f0a88d3ef8a3278351 Author: djm@openbsd.org Date: Sun Dec 19 22:11:39 2021 +0000 upstream: ssh-agent side of destination constraints Gives ssh-agent the ability to parse restrict-destination-v00@openssh.com constraints and to apply them to keys. Check constraints against the hostkeys recorded for a SocketEntry when attempting a signature, adding, listing or deleting keys. Note that the "delete all keys" request will remove constrained keys regardless of location. feedback Jann Horn & markus@ ok markus@ OpenBSD-Commit-ID: 84a7fb81106c2d609a6ac17469436df16d196319 commit ce943912df812c573a33d00bf9e5435b7fcca3f7 Author: djm@openbsd.org Date: Sun Dec 19 22:11:06 2021 +0000 upstream: ssh-add side of destination constraints Have ssh-add accept a list of "destination constraints" that allow restricting where keys may be used in conjunction with a ssh-agent/ssh that supports session ID/hostkey binding. Constraints are specified as either "[user@]host-pattern" or "host-pattern>[user@]host-pattern". The first form permits a key to be used to authenticate as the specified user to the specified host. The second form permits a key that has previously been permitted for use at a host to be available via a forwarded agent to an additional host. For example, constraining a key with "user1@host_a" and "host_a>host_b". Would permit authentication as "user1" at "host_a", and allow the key to be available on an agent forwarded to "host_a" only for authentication to "host_b". The key would not be visible on agent forwarded to other hosts or usable for authentication there. Internally, destination constraints use host keys to identify hosts. The host patterns are used to obtain lists of host keys for that destination that are communicated to the agent. The user/hostkeys are encoded using a new restrict-destination-v00@openssh.com key constraint. host keys are looked up in the default client user/system known_hosts files. It is possible to override this set on the command-line. feedback Jann Horn & markus@ ok markus@ OpenBSD-Commit-ID: 6b52cd2b637f3d29ef543f0ce532a2bce6d86af5 commit 5e950d765727ee0b20fc3d2cbb0c790b21ac2425 Author: djm@openbsd.org Date: Sun Dec 19 22:10:24 2021 +0000 upstream: ssh-add side of destination constraints Have ssh-add accept a list of "destination constraints" that allow restricting where keys may be used in conjunction with a ssh-agent/ssh that supports session ID/hostkey binding. Constraints are specified as either "[user@]host-pattern" or "host-pattern>[user@]host-pattern". The first form permits a key to be used to authenticate as the specified user to the specified host. The second form permits a key that has previously been permitted for use at a host to be available via a forwarded agent to an additional host. For example, constraining a key with "user1@host_a" and "host_a>host_b". Would permit authentication as "user1" at "host_a", and allow the key to be available on an agent forwarded to "host_a" only for authentication to "host_b". The key would not be visible on agent forwarded to other hosts or usable for authentication there. Internally, destination constraints use host keys to identify hosts. The host patterns are used to obtain lists of host keys for that destination that are communicated to the agent. The user/hostkeys are encoded using a new restrict-destination-v00@openssh.com key constraint. host keys are looked up in the default client user/system known_hosts files. It is possible to override this set on the command-line. feedback Jann Horn & markus@ ok markus@ OpenBSD-Commit-ID: ef47fa9ec0e3c2a82e30d37ef616e245df73163e commit 4c1e3ce85e183a9d0c955c88589fed18e4d6a058 Author: djm@openbsd.org Date: Sun Dec 19 22:09:23 2021 +0000 upstream: ssh-agent side of binding record session ID/hostkey/forwarding status for each active socket. Attempt to parse data-to-be-signed at signature request time and extract session ID from the blob if it is a pubkey userauth request. ok markus@ OpenBSD-Commit-ID: a80fd41e292b18b67508362129e9fed549abd318 commit e9497ecf73f3c16667288bce48d4e3d7e746fea1 Author: djm@openbsd.org Date: Sun Dec 19 22:08:48 2021 +0000 upstream: ssh client side of binding send session ID, hostkey, signature and a flag indicating whether the agent connection is being forwarded to ssh agent each time a connection is opened via a new "session-bind@openssh.com" agent extension. ok markus@ OpenBSD-Commit-ID: 2f154844fe13167d3ab063f830d7455fcaa99135 commit b42c61d6840d16ef392ed0f365e8c000734669aa Author: djm@openbsd.org Date: Sun Dec 19 22:08:06 2021 +0000 upstream: Record session ID, host key and sig at intital KEX These will be used later for agent session ID / hostkey binding ok markus@ OpenBSD-Commit-ID: a9af29e33772b18e3e867c6fa8ab35e1694a81fe commit 26ca33d186473d58a32d812e19273ce078b6ffff Author: djm@openbsd.org Date: Tue Dec 7 22:06:45 2021 +0000 upstream: better error message for FIDO keys when we can't match them to a token OpenBSD-Commit-ID: 58255c2a1980088f4ed144db67d879ada2607650 commit adb0ea006d7668190f0c42aafe3a2864d352e34a Author: Darren Tucker Date: Wed Dec 15 10:50:33 2021 +1100 Correct value for IPTOS_DSCP_LE. It needs to allow for the preceeding two ECN bits. From daisuke.higashi at gmail.com via OpenSSH bz#3373, ok claudio@, job@, djm@. commit 3dafd3fe220bd9046f11fcf5191a79ec8800819f Author: Darren Tucker Date: Fri Dec 10 11:57:30 2021 +1100 Increase timeout for test step. commit 5aefb05cd5b843e975b191d6ebb7ddf8de35c112 Author: Darren Tucker Date: Fri Dec 10 10:27:27 2021 +1100 Update the list of tests that don't work on Minix. While there, remove CC (configure will now find clang) and make the test list easier to update via cut and paste. commit 1c09bb1b2e207d091cec299c49416c23d24a1b31 Author: Darren Tucker Date: Fri Dec 10 10:12:57 2021 +1100 Add minix host tuple. Define SETEUID_BREAKS_SETUID for it which should make privsep work. commit a2188579032cf080213a78255373263466cb90cc Author: jsg@openbsd.org Date: Sun Dec 5 12:28:27 2021 +0000 upstream: fix unintended sizeof pointer in debug path ok markus@ OpenBSD-Commit-ID: b9c0481ffc0cd801e0840e342e6a282a85aac93c commit da40355234068c82f1a36196f2d18dd2d81aaafd Author: naddy@openbsd.org Date: Sat Dec 4 00:05:39 2021 +0000 upstream: RSA/SHA-1 is not used by default anymore on the server OpenBSD-Commit-ID: 64abef6cfc3e53088225f6b8a1dcd86d52dc8353 commit e9c71498a083a8b502aa831ea931ce294228eda0 Author: djm@openbsd.org Date: Thu Dec 2 23:45:36 2021 +0000 upstream: hash full host:port when asked to hash output, fixes hashes for non- default ports. bz3367 ok dtucker@ OpenBSD-Commit-ID: 096021cc847da7318ac408742f2d0813ebe9aa73 commit b5601202145a03106012c22cb8980bcac2949f0b Author: djm@openbsd.org Date: Thu Dec 2 23:23:13 2021 +0000 upstream: improve the testing of credentials against inserted FIDO keys a little more: ask the token whether a particular key belongs to it in cases where the token support on-token user- verification (e.g. biometrics) rather than just assuming that it will accept it. Will reduce spurious "Confirm user presence" notifications for key handles that relate to FIDO keys that are not currently inserted in at least some cases. Motivated by bz3366; by Pedro Martelletto OpenBSD-Commit-ID: ffac7f3215842397800e1ae2e20229671a55a63d commit ca709e27c41c90f4565b17282c48dca7756e083c Author: djm@openbsd.org Date: Thu Dec 2 22:40:05 2021 +0000 upstream: move check_sk_options() up so we can use it earlier OpenBSD-Commit-ID: 67fe98ba1c846d22035279782c4664c1865763b4 commit b711bc01a7ec76bb6a285730990cbce9b8ca5773 Author: dtucker@openbsd.org Date: Thu Dec 2 22:35:05 2021 +0000 upstream: ssh-rsa is no longer in the default for PubkeyAcceptedAlgorithms. OpenBSD-Commit-ID: 34a9e1bc30966fdcc922934ae00f09f2596cd73c commit dc91ceea33cd4a9f05be953e8d8062f732db5c8a Author: djm@openbsd.org Date: Thu Dec 2 02:44:44 2021 +0000 upstream: don't put the tty into raw mode when SessionType=none, avoids ^c being unable to kill such a session. bz3360; ok dtucker@ OpenBSD-Commit-ID: 83960c433052303b643b4c380ae2f799ac896f65 commit e6e7d2654a13ba10141da7b42ea683ea4eeb1f38 Author: Damien Miller Date: Mon Nov 29 14:11:03 2021 +1100 previous commit broke bcrypt_pbkdf() Accidentally reverted part of the conversion to use SHA512 from SUPERCOP instead of OpenBSD-style libc SHA512. commit c0459588b8d00b73e506c6095958ecfe62a4a7ba Author: Darren Tucker Date: Mon Nov 29 14:03:19 2021 +1100 Fix typo in Neils' name. commit 158bf854e2a22cf09064305f4a4e442670562685 Author: Damien Miller Date: Mon Nov 29 12:30:22 2021 +1100 sync bcrypt-related files with OpenBSD The main change is that Niels Provos kindly agreed to rescind the BSD license advertising clause, shifting them to the 3-term BSD license. This was the last thing in OpenSSH that used the advertising clause. commit e8976d92a42883ff6b8991438f07df60c2c0d82d Author: Damien Miller Date: Mon Nov 29 12:29:29 2021 +1100 depend commit 8249afeec013e557fe7491a72ca3285de03e25b1 Author: djm@openbsd.org Date: Sun Nov 28 07:21:26 2021 +0000 upstream: sshsig: return "key not found" when searching empty files rather than "internal error" OpenBSD-Commit-ID: e2ccae554c78d7a7cd33fc5d217f35be7e2507ed commit 9e3227d4dbb5ad9c9091b4c14982cab4bba87b4d Author: djm@openbsd.org Date: Sun Nov 28 07:15:10 2021 +0000 upstream: ssh-keygen -Y match-principals doesn't accept any -O options at present, so don't say otherwise in SYNOPSIS; spotted jmc@ OpenBSD-Commit-ID: 9cc43a18f4091010741930b48b3db2f2e4f1d35c commit 56db1f4a4cf5039fc3b42e84c4b16291fdff32b1 Author: djm@openbsd.org Date: Sun Nov 28 07:14:29 2021 +0000 upstream: fix indenting in last commit OpenBSD-Commit-ID: 8b9ba989815d0dec1fdf5427a4a4b58eb9cac4d2 commit 50bea24a9a9bdebad327c76e700def3261f5694e Author: djm@openbsd.org Date: Sun Nov 28 07:10:18 2021 +0000 upstream: missing initialisation for oerrno OpenBSD-Commit-ID: 05d646bba238080259bec821c831a6f0b48d2a95 commit 5a0f4619041d09cd29f3a08da41db5040372bdd1 Author: Darren Tucker Date: Sun Nov 28 15:31:37 2021 +1100 Correct ifdef to activate poll() only if needed. commit d4035c81a71237f690edd7eda32bef7d63fd9528 Author: djm@openbsd.org Date: Sat Nov 27 07:23:35 2021 +0000 upstream: whitespac e OpenBSD-Regress-ID: b9511d41568056bda489e13524390167889908f8 commit a443491e6782ef0f5a8bb87a5536c8ee4ff233a1 Author: djm@openbsd.org Date: Sat Nov 27 07:20:58 2021 +0000 upstream: regression test for match-principals. Mostly by Fabian Stelzer OpenBSD-Regress-ID: ced0bec89af90935103438986bbbc4ad1df9cfa7 commit 78230b3ec8cbabc1e7de68732dc5cbd4837c6675 Author: djm@openbsd.org Date: Sat Nov 27 07:14:46 2021 +0000 upstream: Add ssh-keygen -Y match-principals operation to perform matching of principals names against an allowed signers file. Requested by and mostly written by Fabian Stelzer, towards a TOFU model for SSH signatures in git. Some tweaks by me. "doesn't bother me" deraadt@ OpenBSD-Commit-ID: 8d1b71f5a4127bc5e10a880c8ea6053394465247 commit 15db86611baaafb24c40632784dabf82e3ddb1a7 Author: djm@openbsd.org Date: Thu Nov 25 23:02:24 2021 +0000 upstream: debug("func: ...") -> debug_f("...") OpenBSD-Commit-ID: d58494dc05c985326a895adfbe16fbd5bcc54347 commit b7ffbb17e37f59249c31f1ff59d6c5d80888f689 Author: Darren Tucker Date: Fri Nov 19 18:53:46 2021 +1100 Allow for fd = -1 in compat ppoll overflow check. Fixes tests on at least FreeBSD 6, possibly others. commit 04b172da5b96a51b0d55c905b423ababff9f4e0b Author: Darren Tucker Date: Fri Nov 19 16:01:51 2021 +1100 Don't auto-enable Capsicum sandbox on FreeBSD 9/10. Since we changed from select() to ppoll() tests have been failing. This seems to be because FreeBSD 10 (and presumably 9) do not allow ppoll() in the privsep process and sshd will fail with "Not permitted in capability mode". Setting CAP_EVENT on the FDs doesn't help, but weirdly, poll() works without that. Those versions are EOL so this situation is unlikely to change. commit a823f39986e7b879f26412e64c15630e1cfa0dc5 Author: djm@openbsd.org Date: Thu Nov 18 03:53:48 2021 +0000 upstream: regression test for ssh-keygen -Y find-principals fix; from Fabian Stelzer ok djm markus OpenBSD-Regress-ID: 34fe4088854c1a2eb4c0c51cc4676ba24096bac4 commit 199c4df66c0e39dd5c3333b162af274678c0501d Author: djm@openbsd.org Date: Thu Nov 18 21:32:11 2021 +0000 upstream: less confusing debug message; bz#3365 OpenBSD-Commit-ID: 836268d3642c2cdc84d39b98d65837f5241e4a50 commit 97f9b6e61316c97a32dad94b7a37daa9b5f6b836 Author: djm@openbsd.org Date: Thu Nov 18 21:11:01 2021 +0000 upstream: avoid xmalloc(0) for PKCS#11 keyid for ECDSA keys (we already did this for RSA keys). Avoids fatal errors for PKCS#11 libraries that return empty keyid, e.g. Microchip ATECC608B "cryptoauthlib"; bz#3364 OpenBSD-Commit-ID: 054d4dc1d6a99a2e6f8eebc48207b534057c154d commit c74aa0eb73bd1edf79947d92d9c618fc3424c4a6 Author: djm@openbsd.org Date: Thu Nov 18 03:50:41 2021 +0000 upstream: ssh-keygen -Y find-principals was verifying key validity when using ca certs but not with simple key lifetimes within the allowed signers file. Since it returns the first keys principal it finds this could result in a principal with an expired key even though a valid one is just below. patch from Fabian Stelzer; feedback/ok djm markus OpenBSD-Commit-ID: b108ed0a76b813226baf683ab468dc1cc79e0905 commit d902d728dfd81622454260e23bc09d5e5a9a795e Author: Darren Tucker Date: Thu Nov 18 23:44:07 2021 +1100 Correct calculation of tv_nsec in poll(). commit 21dd5a9a3fb35e8299a1fbcf8d506f1f6b752b85 Author: Darren Tucker Date: Thu Nov 18 23:11:37 2021 +1100 Add compat implementation of ppoll using pselect. commit b544ce1ad4afb7ee2b09f714aa63efffc73fa93a Author: Darren Tucker Date: Thu Nov 18 23:05:34 2021 +1100 Put poll.h inside ifdef HAVE_POLL_H. commit 875408270c5a7dd69ed5449e5d85bd7120c88f70 Author: djm@openbsd.org Date: Thu Nov 18 03:31:44 2021 +0000 upstream: check for POLLHUP wherever we check for POLLIN OpenBSD-Commit-ID: 6aa6f3ec6b17c3bd9bfec672a917f003a76d93e5 commit 36b5e37030d35bbaa18ba56825b1af55971d18a0 Author: djm@openbsd.org Date: Thu Nov 18 03:07:59 2021 +0000 upstream: fd leak in sshd listen loop error path; from Gleb Smirnoff OpenBSD-Commit-ID: a7a2be27a690a74bf2381bc16cea38e265657412 commit b99498d0c93f1edd04857b318308a66b28316bd8 Author: djm@openbsd.org Date: Thu Nov 18 03:07:20 2021 +0000 upstream: check for POLLHUP as well as POLLIN in sshd listen loop; ok deraadt millert OpenBSD-Commit-ID: a4f1244c5a9c2b08dac4f3b1dc22e9d1dc60c587 commit 1f3055d788e8cf80851eb1728b535d57eb0dba6a Author: djm@openbsd.org Date: Thu Nov 18 03:06:03 2021 +0000 upstream: check for POLLHUP as well as POLLIN, handle transient IO errors as well as half-close on the output side; ok deraadt millert OpenBSD-Commit-ID: de5c5b9939a37476d256328cbb96305bdecf511e commit 9778a15fa6dbdac6a95bf15865c2688b4bd6944e Author: Damien Miller Date: Thu Nov 18 10:16:55 2021 +1100 adjust seccomp filter for select->poll conversion Needed to add ppoll syscall but also to relax the fallback rlimit sandbox. Linux poll() fails with EINVAL if npfds > RLIMIT_NOFILE, so we have to allow a single fd in the rlimit. commit fcd8d895bbb849c64f0aed934e3303d37f696f5d Author: Damien Miller Date: Thu Nov 18 10:16:44 2021 +1100 update depends commit 76292787a1e93e668f10e36b4bf59ce0ae28e156 Author: Damien Miller Date: Thu Nov 18 09:26:20 2021 +1100 compat for timespecsub() and friends commit fd7e7de4ddb4399c7e929b44f2bbfc118eddfcf8 Author: djm@openbsd.org Date: Wed Nov 17 21:06:39 2021 +0000 upstream: set num_listen_socks to 0 on close-all instead of -1, which interferes with the new poll()-based listen loop; spotted and debugged by anton@+deraadt@ OpenBSD-Commit-ID: f7ab8ab124f615a2e0c45fee14c38d2f2abbabbd commit fd9343579afac30a971f06643a669733d9acb407 Author: deraadt@openbsd.org Date: Sun Nov 14 18:47:43 2021 +0000 upstream: use ppoll() instead of pselect() with djm OpenBSD-Commit-ID: 980f87c9564d5d2ad55722b7a6f44f21284cd215 commit 092d29b232ef1a19609a5316ed7e4d896bb2e696 Author: deraadt@openbsd.org Date: Sun Nov 14 06:15:36 2021 +0000 upstream: match .events with .fd better OpenBSD-Commit-ID: 77eef212ca0add905949532af390164489c5984b commit 8d642c9a90fa4ed5a3effd785fb3591e14de00cd Author: deraadt@openbsd.org Date: Sun Nov 14 03:25:10 2021 +0000 upstream: convert select() to poll() ok djm OpenBSD-Commit-ID: b53e4940ff10dd24f8d16e8db8ef1970015d7ead commit 6582a31c388968f4073af2bd8621880735c3d42b Author: deraadt@openbsd.org Date: Sat Nov 13 21:14:13 2021 +0000 upstream: replace select() with ppoll(), including converting timeval's to timespec's to make things easier. back and forth and ok; djm OpenBSD-Commit-ID: 89d3b23c60875da919e7820f9de6213286ffbec9 commit 7c025c005550c86a40200a2bcdd355d09413d61a Author: deraadt@openbsd.org Date: Sat Nov 13 17:26:13 2021 +0000 upstream: It really looks like pledge "stdio dns" is possible earlier. Discussed with mestre OpenBSD-Commit-ID: 610873de63a593e0ac7bbbcb7a0f2894d36f4c01 commit 06acb04c20ee483fe4757bd12aec870cc4bb1076 Author: deraadt@openbsd.org Date: Fri Nov 12 05:23:49 2021 +0000 upstream: aggressively pre-fill the pollfd array with fd=-1 OpenBSD-Commit-ID: c2a525de8f83c1a04405bd79122c424140552a5b commit 7eec76793dec06e8f06b6cf71f9473141c69d109 Author: deraadt@openbsd.org Date: Thu Nov 11 15:32:32 2021 +0000 upstream: Convert from select() to ppoll(). Along the way, I observed that the select() code was using exceptfds incorrectly.. ok millert OpenBSD-Commit-ID: 548e05bfc31b2af02319eb3d051286d4128dec96 commit e665ed2d0c24fe11d5470ce72fa1e187377d3fc4 Author: Darren Tucker Date: Fri Nov 12 22:55:27 2021 +1100 Switch from LibreSSL 3.4.0 to 3.4.1. The LibreSSL 3.4.0 release has an OPENBSD_BRANCH that points to "master" and that branch no longer has the files LibreSSL expects and thus it will no longer build, breaking the test. commit 21b6b5a06c8c53c548d25e6074c5240e88e2ef34 Author: djm@openbsd.org Date: Wed Nov 10 06:29:25 2021 +0000 upstream: add the sntrup761x25519-sha512@openssh.com hybrid ECDH/x25519 + Streamlined NTRU Prime post-quantum KEX to the default KEXAlgorithms list (after the ECDH methods but before the prime-group DH ones). ok markus@ OpenBSD-Commit-ID: 22b77e27a04e497a10e22f138107579652854210 commit 239da797cbf07a640d7b1ea02d3f99ace3ef792d Author: djm@openbsd.org Date: Wed Nov 10 06:25:08 2021 +0000 upstream: fix ssh-keysign for KEX algorithms that use SHA384/512 exchange hashes; feedback/ok markus@ OpenBSD-Commit-ID: 09a8fda1c081f5de1e3128df64f28b7bdadee239 commit 6997a592ecb1013df0c6d7f8df3e6517827aef11 Author: djm@openbsd.org Date: Mon Nov 8 21:32:49 2021 +0000 upstream: improve error message when trying to expand a ~user path for a user that doesn't exist; better matches what the shell does ok deraadt@ OpenBSD-Commit-ID: 1ddefa3c3a78b69ce13d1b8f67bc9f2cefd23ad6 commit 10b899a15c88eb40eb5f73cd0fa84ef0966f79c9 Author: Darren Tucker Date: Wed Nov 10 12:34:25 2021 +1100 Don't trust closefrom() on Linux. glibc's closefrom implementation does not work in a chroot when the kernel does not have close_range. It tries to read from /proc/self/fd and when that fails dies with an assertion of sorts. Instead, call close_range ourselves from our compat code and fall back if that fails. bz#3349, with william.wilson at canonical.com and fweimer at redhat.com. commit eb1f63195a9a38b519536a5b398d9939261ec081 Author: dtucker@openbsd.org Date: Sat Nov 6 10:13:39 2021 +0000 upstream: Plug a couple of minor mem leaks. From beldmit at gmail.com via github PR#283, ok markus@ OpenBSD-Commit-ID: ec1fa7d305d46226861c3ca6fb9c9beb2ada2892 commit e4f501bf1d3b53f1cc23d9521fd7c5163307b760 Author: djm@openbsd.org Date: Fri Nov 5 03:10:58 2021 +0000 upstream: move cert_filter_principals() to earlier in the file for reuse; no code change OpenBSD-Commit-ID: 598fa9528b656b2f38bcc3cf5b6f3869a8c115cf commit 59c60f96fee321c7f38f00372826d37f289534af Author: deraadt@openbsd.org Date: Wed Nov 3 22:00:56 2021 +0000 upstream: Many downstreams expect ssh to compile as non-C99... OpenBSD-Commit-ID: e6aa3e08bda68e5fb838fc8a49b1d2dfc38ee783 commit 7a78fe63b0b28ef7231913dfefe9d08f9bc41c61 Author: Darren Tucker Date: Sat Nov 6 21:07:03 2021 +1100 Skip getline() on HP-UX 10.x. HP-UX 10.x has a getline() implementation in libc that does not behave as we expect so don't use it. With correction from Thorsten Glaser and typo fix from Larkin Nickle. commit 343ae252ebb35c6ecae26b447bf1551a7666720e Author: Damien Miller Date: Wed Nov 3 12:08:21 2021 +1100 basic SECURITY.md (refers people to the website) commit ed45a0168638319e0a710633f6085b96b9cec656 Author: djm@openbsd.org Date: Tue Nov 2 22:57:27 2021 +0000 upstream: crank SSH_SK_VERSION_MAJOR to match recent change in usr/bin/ssh OpenBSD-Regress-ID: 113d181c7e3305e138db9b688cdb8b0a0019e552 commit f3c34df860c4c1ebddacb973954e58167d9dbade Author: djm@openbsd.org Date: Tue Nov 2 22:56:40 2021 +0000 upstream: Better handle FIDO keys on tokens that provide user verification (UV) on the device itself, including biometric keys. Query the token during key creation to determine whether it supports on-token UV and, if so, clear the SSH_SK_USER_VERIFICATION_REQD flag in the key so that ssh(1) doesn't automatically prompty for PIN later. When making signatures with the key, query the token's capabilities again and check whether the token is able (right now) to perform user- verification without a PIN. If it is then the PIN prompt is bypassed and user verification delegated to the token. If not (e.g. the token is biometric capable, but no biometric are enrolled), then fall back to user verification via the usual PIN prompt. Work by Pedro Martelletto; ok myself and markus@ NB. cranks SSH_SK_VERSION_MAJOR OpenBSD-Commit-ID: e318a8c258d9833a0b7eb0236cdb68b5143b2f27 commit 0328a081f38c09d2d4d650e94461a47fb5eef536 Author: djm@openbsd.org Date: Fri Oct 29 03:03:06 2021 +0000 upstream: sshsig: add tests for signing key validity and find-principals - adds generic find-principals tests (this command had none before) - tests certs with a timeboxed validity both with and without a restriced lifetime for the CA - test for a revoked CA cert by Fabian Stelzer OpenBSD-Regress-ID: 9704b2c6df5b8ccfbdf2c06c5431f5f8cad280c9 commit ccd358e1e25e25c13f0825996283cbf7a1647a3b Author: djm@openbsd.org Date: Fri Oct 29 02:48:19 2021 +0000 upstream: avoid signedness warning; spotted in -portable OpenBSD-Regress-ID: 4cacc126086487c0ea7f3d86b42dec458cf0d0c6 commit 2741f52beb11490d7033a25e56ed0496f0c78006 Author: djm@openbsd.org Date: Fri Oct 29 03:20:46 2021 +0000 upstream: ssh-keygen: make verify-time argument parsing optional From Fabian Stelzer OpenBSD-Commit-ID: 1ff35e4c366a45a073663df90381be6a8ef4d370 commit a1217d363b88b32cfe54c4f02c6c1cf4bdefdd23 Author: Damien Miller Date: Fri Oct 29 13:48:34 2021 +1100 unbreak fuzz harness for recent changes commit 68e522ed8183587c9367fa3842c5b75f64f3d12b Author: Darren Tucker Date: Fri Oct 29 13:32:24 2021 +1100 Use -Wbitwise-instead-of-logical if supported. commit be28b23012aa3fa323be7ec84863cf238927c078 Author: Damien Miller Date: Thu Oct 28 16:24:53 2021 +1100 use -Wmisleading-indentation cflag if available ok dtucker@ commit 2e6f5f24dd2f9217f4ab8b737ed428d5d5278f91 Author: Damien Miller Date: Thu Oct 28 16:24:44 2021 +1100 depend commit a5ab4882348d26addc9830a44e053238dfa2cb58 Author: Damien Miller Date: Thu May 6 10:08:30 2021 +1000 remove built-in support for md5crypt() Users of MD5-hashed password should arrange for ./configure to link against libxcrypt or similar. Though it would be better to avoid use of MD5 password hashing entirely, it's arguably worse than DEScrypt. feedback and ok dtucker@ commit c5de1fffa6328b8246b87da28fa9df05813f76a3 Author: djm@openbsd.org Date: Thu Oct 28 02:55:30 2021 +0000 upstream: increment SSH_SK_VERSION_MAJOR to match last change OpenBSD-Regress-ID: 17873814d1cbda97f49c8528d7b5ac9cadf6ddc0 commit 0001d04e55802d5bd9d6dece1081a99aa4ba2828 Author: djm@openbsd.org Date: Thu Oct 28 02:54:18 2021 +0000 upstream: When downloading resident keys from a FIDO token, pass back the user ID that was used when the key was created and append it to the filename the key is written to (if it is not the default). Avoids keys being clobbered if the user created multiple resident keys with the same application string but different user IDs. feedback Pedro Martelletto; ok markus NB. increments SSH_SK_VERSION_MAJOR OpenBSD-Commit-ID: dbd658b5950f583106d945641a634bc6562dd3a3 commit d4bed5445646e605c383a4374fa962e23bf9e3a3 Author: deraadt@openbsd.org Date: Sun Oct 24 21:24:17 2021 +0000 upstream: For open/openat, if the flags parameter does not contain O_CREAT, the 3rd (variadic) mode_t parameter is irrelevant. Many developers in the past have passed mode_t (0, 044, 0644, or such), which might lead future people to copy this broken idiom, and perhaps even believe this parameter has some meaning or implication or application. Delete them all. This comes out of a conversation where tb@ noticed that a strange (but intentional) pledge behaviour is to always knock-out high-bits from mode_t on a number of system calls as a safety factor, and his bewilderment that this appeared to be happening against valid modes (at least visually), but no sorry, they are all irrelevant junk. They could all be 0xdeafbeef. ok millert OpenBSD-Commit-ID: 503d11633497115688c0c6952686524f01f53121 commit d575cf44895104e0fcb0629920fb645207218129 Author: Darren Tucker Date: Fri Oct 22 23:27:41 2021 +1100 kitchensink test target now needs krb5. commit 4ae39cada214e955bcfd3448ff28f0ed18886706 Author: Darren Tucker Date: Fri Oct 22 22:54:33 2021 +1100 Test both MIT KRB5 and Heimdal. commit 22b2681d88619e5247dc53c9f112058a7e248d48 Author: dtucker@openbsd.org Date: Fri Oct 22 10:51:57 2021 +0000 upstream: Plug mem addrinfo mem leaks. Prevent mem leaks in the (unlikely) event that getaddrinfo returns no addresses. ALso, remove an unneeded NULL check in addr_ntop. From khaleesicodes via github PR#281, ok deraadt@ OpenBSD-Commit-ID: e8a5afc686376637c355c5f7e122dc4b080b9c1a commit 27c8c343b610263f83ac2328735feeb881c6c92f Author: dtucker@openbsd.org Date: Fri Oct 22 09:22:04 2021 +0000 upstream: Remove unnecessary semicolons ... in case statements. From khaleesicodes via github PR#280. OpenBSD-Commit-ID: e1e89360b65775cff83e77ce040b342015caf4ed commit e7eb73b8d1fe1008d92433ea949491ce654bfaba Author: dtucker@openbsd.org Date: Fri Oct 22 09:19:34 2021 +0000 upstream: Fix typos in comments. From khaleesicodes via github PR#280. OpenBSD-Commit-ID: 26fdd83652c40f098bf7c685e8ebb9eb72cc45fc commit 052a9d8494175e24312daa6c132665e58c17fe6e Author: deraadt@openbsd.org Date: Fri Oct 15 14:46:46 2021 +0000 upstream: switch scp(1) back to sftp protocol. openbsd 7.0 release shipped with the (hopefully last) scp that uses RCP protocol for copying. Let's get back to testing the SFTP protocol. OpenBSD-Commit-ID: 9eaa35d95fd547b78b0a043b3f518e135f151f30 commit a07664646bf6d293f5bbd45a5de54f3c36bb85da Author: Darren Tucker Date: Fri Oct 22 14:00:05 2021 +1100 Source configs script so setup_ci can use settings commit 34df52c201c6b47e5a46b50c215e4d98a8bf6587 Author: Darren Tucker Date: Fri Oct 22 09:42:14 2021 +1100 Install libedit and pam based on config flags. commit 8c626cc563e8d21d844d06f9971a9ee01de6aa2a Author: Darren Tucker Date: Thu Oct 21 16:53:39 2021 +1100 Don't use 'here string", it's not POSIX. commit 086a4b5977472aefa3de918b88efad0faf83b2b1 Author: Darren Tucker Date: Thu Oct 21 15:33:27 2021 +1100 Remove -Werror from compiler package to install. commit 5a7a4687507d057f9b5e7497f3d3f82e64753c02 Author: Darren Tucker Date: Thu Oct 21 15:00:53 2021 +1100 Build with -Werror on most recent gcc and clang. commit 4d2cbdb525d673acf941d48a7044fcf03125611a Author: Darren Tucker Date: Fri Oct 15 12:59:06 2021 +1100 Include string.h and stdio.h for strerror. commit fff13aaa262b7b3ec83ed21e29674cbf331780a7 Author: Darren Tucker Date: Fri Oct 15 12:43:36 2021 +1100 Include error reason if trace disabling fails. commit d4b38144c02f3faa5271e5fb35df93507e06f1b4 Author: Darren Tucker Date: Tue Oct 12 22:55:51 2021 +1100 Add tcmalloc test target. commit 002d65b0a30063c6e49bf8a53e709d8d5a0d45c1 Author: dtucker@openbsd.org Date: Sat Oct 9 10:52:42 2021 +0000 upstream: Document that CASignatureAlgorithms, ExposeAuthInfo and PubkeyAuthOptions can be used in a Match block. Patch from eehakkin via github PR#277. OpenBSD-Commit-ID: c0a63f5f52e918645967ac022b28392da4b866aa commit 40bd3709dddaae3a1b6113748bec3faa6a607531 Author: Darren Tucker Date: Thu Oct 7 15:55:49 2021 +1100 Skip SK unit tests when built without security-key commit 482f73be10f10b93f818df19fcc8a912c0c371fc Author: Darren Tucker Date: Thu Oct 7 15:55:04 2021 +1100 Include relevant env vars on command line. Makes it easier to reproduce a build by cut/pasting the configure line. commit ef5916b8acd9b1d2f39fad4951dae03b00dbe390 Author: Darren Tucker Date: Thu Oct 7 14:28:02 2021 +1100 Only enable sk-* key types if ENABLE_SK is defined commit 52d4232b493a9858fe616e28a8bbcc89afa2ad4d Author: Darren Tucker Date: Wed Oct 6 18:14:37 2021 +1100 Disable security key on minix3. The test doesn't work so disable. commit 7cd062c3a29669b8d7dc2a97e6575f4dcb7d35a2 Author: Darren Tucker Date: Wed Oct 6 17:45:28 2021 +1100 Add USE_LIBC_SHA2 for (at least) NetBSD 9. commit 639c440f6c3c2a8216a5eb9455ef13bf4204089c Author: Darren Tucker Date: Wed Oct 6 17:09:31 2021 +1100 Define OPENSSL_NO_SHA including OpenSSL from test. We don't use SHA256 from OpenSSL in the sk-dummy module and the definitions can conflict with system sha2.h (eg on NetBSD) so define OPENSSL_NO_SHA so we don't attempt to redefine them. commit 8f4be526a338d06624f146fa26007bb9dd3a4f7b Author: Darren Tucker Date: Wed Oct 6 15:40:58 2021 +1100 Disable security key on NetBSD4 test. sk-dummy used for the security key test includes both sha2.h and OpenSSL causing the definitions conflict so disable security key support on this platform. commit 3b353ae58aa07a1cbbeb1da3ace21fc0dcccd66a Author: Damien Miller Date: Wed Oct 6 15:07:01 2021 +1100 clean regress/misc/sk-dummy in cleandir target commit 57680a2ab43518c5ccbd8242c40482106cde6ac1 Author: dtucker@openbsd.org Date: Sat Oct 2 03:17:01 2021 +0000 upstream: Dynamically allocate encoded HashKnownHosts and free as appropriate. Saves 1k of static storage and prevents snprintf "possible truncation" warnings from newer compilers (although in this case it's false positive since the actual sizes are limited by the output size of the SHA1). ok djm@ OpenBSD-Commit-ID: e254ae723f7e3dce352c7d5abc4b6d87faf61bf4 commit e3e62deb549fde215b777d95276c304f84bf00c6 Author: djm@openbsd.org Date: Wed Oct 6 03:35:13 2021 +0000 upstream: use libc SHA256 functions; make this work when compiled !WITH_OPENSSL OpenBSD-Regress-ID: fda0764c1097cd42f979ace29b07eb3481259890 commit 12937d867019469ebce83c2ff614cdc6688fc2d8 Author: dtucker@openbsd.org Date: Fri Oct 1 05:20:20 2021 +0000 upstream: Add test for ssh hashed known_hosts handling. OpenBSD-Regress-ID: bcef3b3cd5a1ad9899327b4b2183de2541aaf9cf commit 5a37cc118f464416d08cd0291a9b1611d8de9943 Author: Damien Miller Date: Wed Oct 6 13:16:21 2021 +1100 fix broken OPENSSL_HAS_ECC test spotted by dtucker - -commit 16a25414f303cd6790eb967aeb962040e32c9c7a -Author: Damien Miller -Date: Fri Oct 1 22:40:06 2021 +1000 - - make sk-dummy.so work without libcrypto installed - -commit dee22129bbc61e25b1003adfa2bc584c5406ef2d -Author: Damien Miller -Date: Fri Oct 1 16:35:49 2021 +1000 - - make OPENSSL_HAS_ECC checks more thorough - - ok dtucker - -commit 872595572b6c9a584ed754165e8b7c4c9e7e1d61 -Author: Damien Miller -Date: Fri Oct 1 16:35:05 2021 +1000 - - fix FIDO key support for !OPENSSL_HAS_ECC case - - ok dtucker - -commit 489741dc68366940d369ac670b210b4834a6c272 -Author: Damien Miller -Date: Fri Oct 1 14:51:37 2021 +1000 - - enable security key support for --without-openssl - -commit c978565c8589acfe4ea37ab5099d39c84158c713 -Author: Damien Miller -Date: Fri Oct 1 13:27:50 2021 +1000 - - need stdlib.h for free(3) - -commit 76a398edfb51951b2d65d522d7b02c72304db300 -Author: dtucker@openbsd.org -Date: Thu Sep 30 05:26:26 2021 +0000 - - upstream: Fix up whitespace left by previous - - change removing privsep. No other changes. - - OpenBSD-Regress-ID: 87adec225d8afaee4d6a91b2b71203f52bf14b15 - -commit ddcb53b7a7b29be65d57562302b2d5f41733e8dd -Author: dtucker@openbsd.org -Date: Thu Sep 30 05:20:08 2021 +0000 - - upstream: Remove references to privsep. - - This removes several do..while loops but does not change the - indentation of the now-shallower loops, which will be done in a separate - whitespace-only commit to keep changes of style and substance separate. - - OpenBSD-Regress-ID: 4bed1a0249df7b4a87c965066ce689e79472a8f7 - -commit ece2fbe486164860de8df3f8b943cccca3085eff -Author: dtucker@openbsd.org -Date: Thu Sep 30 04:22:50 2021 +0000 - - upstream: Use "skip" instead of "fatal" - - if SUDO isn't set for the *-command tests. This means running "make tests" - without SUDO set will perform all of the tests that it can instead of - failing on the ones it cannot run. - - OpenBSD-Regress-ID: bd4dbbb02f34b2e8c890558ad4a696248def763a - -commit bb754b470c360e787a99fb4e88e2668198e97b41 -Author: djm@openbsd.org -Date: Fri Oct 1 04:50:36 2021 +0000 - - upstream: unbreak FIDO sk-ed25519 key enrollment for OPENSSL=no builds; - - ok dtucker@ - - OpenBSD-Commit-ID: 6323a5241728626cbb2bf0452cf6a5bcbd7ff709 - -commit 207648d7a6415dc915260ca75850404dbf9f0a0b -Author: Darren Tucker -Date: Wed Sep 29 20:03:58 2021 +1000 - - Include stdlib.h for arc4random_uniform prototype. - -commit 696aadc854582c164d5fc04933d2f3e212dc0e06 -Author: Darren Tucker -Date: Wed Sep 29 20:00:30 2021 +1000 - - Look for clang after cc and gcc. - -commit a3c6375555026d85dbf811fab566b9f76f196144 -Author: Darren Tucker -Date: Wed Sep 29 19:30:59 2021 +1000 - - Use backticks instead of $(..) for portability. - - Older shells (eg /bin/sh on Solaris 10) don't support $() syntax. - -commit 958aaa0387133d51f84fe9c8f30bca03025f2867 -Author: Darren Tucker -Date: Wed Sep 29 18:53:32 2021 +1000 - - Skip file-based tests by default on Mac OS. - - The file-based tests need OpenSSL so skip them. - -commit 55c8bdf6e9afb0f9fa8e4f10c25c7f0081b48fd0 -Author: Darren Tucker -Date: Wed Sep 29 18:42:47 2021 +1000 - - Build without OpenSSL on Mac OS. - - Modern versions don't ship enough libcrypto to build against. - -commit c9172193ea975415facf0afb356d87df21535f88 -Author: Darren Tucker -Date: Wed Sep 29 18:33:38 2021 +1000 - - Remove TEST_SSH_ECC. - - Convert the only remaining user of it to runtime detection using ssh -Q. - -commit 5e6d28b7874b0deae95d2c68947c45212d32e599 -Author: Darren Tucker -Date: Wed Sep 29 17:48:09 2021 +1000 - - Split c89 test openssl setting out. - -commit c4ac7f98e230e83c015678dc958b1ffe828564ad -Author: Darren Tucker -Date: Wed Sep 29 17:40:50 2021 +1000 - - Expand TEST_SHELL consistently with other vars. - -commit cfe5f7b0eb7621bfb0a756222de0431315c2ab8b -Author: Darren Tucker -Date: Wed Sep 29 17:26:50 2021 +1000 - - Replace `pwd` with make variable in regress cmd. - -commit 899be59da5fbc3372444bd0fbe74af48313bed33 -Author: Darren Tucker -Date: Wed Sep 29 17:14:33 2021 +1000 - - Get BUILDDIR from autoconf. - - Use this to replace `pwd`s in regress test command line. - -commit c8d92d3d4f7d560146f2f936156ec4dac3fc5811 -Author: Darren Tucker -Date: Wed Sep 29 13:28:56 2021 +1000 - - Add make clean step to tests. - -commit 360fb41ef8359619ab90b0d131c914494e55d3dd -Author: Darren Tucker -Date: Wed Sep 29 11:36:13 2021 +1000 - - Test all available clang and gcc versions. - -commit 4fb49899d7da22952d35a4bc4c9bdb2311087893 -Author: djm@openbsd.org -Date: Wed Sep 29 01:32:21 2021 +0000 - - upstream: Test certificate hostkeys held in ssh-agent too. Would have - - caught regression fixed in sshd r1.575 - - ok markus@ - - OpenBSD-Regress-ID: 1f164d7bd89f83762db823eec4ddf2d2556145ed - -commit ce4854e12e749a05646e5775e9deb8cfaf49a755 -Author: djm@openbsd.org -Date: Wed Sep 29 01:33:32 2021 +0000 - - upstream: add some debug output showing how many key file/command lines - - were processed. Useful to see whether a file or command actually has keys - present - - OpenBSD-Commit-ID: 0bd9ff94e84e03a22df8e6c12f6074a95d27f23c - -commit 15abdd523501c349b703d9a27e2bb4252ad921ef -Author: dtucker@openbsd.org -Date: Tue Sep 28 11:14:50 2021 +0000 - - upstream: Make prototype for rijndaelEncrypt match function - - including the bounds. Fixes error in portable where GCC>=11 takes notice of - the bounds. ok deraadt@ - - OpenBSD-Commit-ID: cdd2f05fd1549e1786a70871e513cf9e9cf099a6 - -commit d1d29ea1d1ef1a1a54b209f062ec1dcc8399cf03 -Author: dtucker@openbsd.org -Date: Tue Sep 28 11:10:05 2021 +0000 - - upstream: Import regenerated moduli. - - OpenBSD-Commit-ID: 4bec5db13b736b64b06a0fca704cbecc2874c8e1 - -commit 39f2111b1d5f00206446257377dcce58cc72369f -Author: Darren Tucker -Date: Wed Sep 29 10:53:55 2021 +1000 - - Add new compiler hardening flags. - - Add -fzero-call-used-regs and -ftrivial-auto-var-init to the list of - compiler hardening flags that configure checks for. These are supported - by clang and gcc, and make ROP gadgets less useful and mitigate - stack-based infoleaks respectively. ok djm@ - -commit bf944e3794eff5413f2df1ef37cddf96918c6bde -Author: Damien Miller -Date: Mon Sep 27 00:03:19 2021 +1000 - - initgroups needs grp.h - -commit 8c5b5655149bd76ea21026d7fe73ab387dbc3bc7 -Author: djm@openbsd.org -Date: Sun Sep 26 14:01:11 2021 +0000 - - upstream: openssh-8.8 - - OpenBSD-Commit-ID: 12357794602ac979eb7312a1fb190c453f492ec4 - -commit f3cbe43e28fe71427d41cfe3a17125b972710455 -Author: djm@openbsd.org -Date: Sun Sep 26 14:01:03 2021 +0000 - - upstream: need initgroups() before setresgid(); reported by anton@, - - ok deraadt@ - - OpenBSD-Commit-ID: 6aa003ee658b316960d94078f2a16edbc25087ce - -commit 8acaff41f7518be40774c626334157b1b1c5583c -Author: Damien Miller -Date: Sun Sep 26 22:16:36 2021 +1000 - - update version numbers for release - -commit d39039ddc0010baa91c70a0fa0753a2699bbf435 -Author: kn@openbsd.org -Date: Sat Sep 25 09:40:33 2021 +0000 - - upstream: RSA/SHA-1 is not used by default anymore - - OK dtucker deraadt djm - - OpenBSD-Commit-ID: 055c51a221c3f099dd75c95362f902da1b8678c6 - -commit 9b2ee74e3aa8c461eb5552a6ebf260449bb06f7e -Author: Darren Tucker -Date: Fri Sep 24 11:08:03 2021 +1000 - - Move the fgrep replacement to hostkey-rotate.sh. - - The fgrep replacement for buggy greps doesn't work in the sftp-glob test - so move it to just where we know it's needed. - -commit f7039541570d4b66d76e6f574544db176d8d5c02 -Author: Darren Tucker -Date: Fri Sep 24 08:04:14 2021 +1000 - - Replacement function for buggy fgrep. - - GNU (f)grep <=2.18, as shipped by FreeBSD<=12 and NetBSD<=9 will - occasionally fail to find ssh host keys in the hostkey-rotate test. - If we have those versions, use awk instead. - -commit f6a660e5bf28a01962af87568e118a2d2e79eaa0 -Author: David Manouchehri -Date: Thu Sep 23 17:03:18 2021 -0400 - - Don't prompt for yes/no questions. - -commit 7ed1a3117c09f8c3f1add35aad77d3ebe1b85b4d -Author: djm@openbsd.org -Date: Mon Sep 20 06:53:56 2021 +0000 - - upstream: fix missing -s in SYNOPSYS and usage() as well as a - - capitalisation mistake; spotted by jmc@ - - OpenBSD-Commit-ID: 0ed8ee085c7503c60578941d8b45f3a61d4c9710 - -commit 8c07170135dde82a26886b600a8bf6fb290b633d -Author: dtucker@openbsd.org -Date: Mon Sep 20 04:02:13 2021 +0000 - - upstream: Fix "Allocated port" debug message - - for unix domain sockets. From peder.stray at gmail.com via github PR#272, - ok deraadt@ - - OpenBSD-Commit-ID: 8d5ef3fbdcdd29ebb0792b5022a4942db03f017e - -commit 277d3c6adfb128b4129db08e3d65195d94b55fe7 -Author: djm@openbsd.org -Date: Mon Sep 20 01:55:42 2021 +0000 - - upstream: Switch scp back to use the old protocol by default, ahead of - - release. We'll wait a little longer for people to pick up sftp-server(8) that - supports the extension that scp needs for ~user paths to continue working in - SFTP protocol mode. Discussed with deraadt@ - - OpenBSD-Commit-ID: f281f603a705fba317ff076e7b11bcf2df941871 - -commit ace19b34cc15bea3482be90450c1ed0cd0dd0669 -Author: djm@openbsd.org -Date: Sat Sep 18 02:03:25 2021 +0000 - - upstream: better error message for ~user failures when the - - sftp-server lacks the expand-path extension; ok deraadt@ - - OpenBSD-Commit-ID: 9c1d965d389411f7e86f0a445158bf09b8f9e4bc - -commit 6b1238ba971ee722a310d95037b498ede5539c03 -Author: djm@openbsd.org -Date: Thu Sep 16 15:22:22 2021 +0000 - - upstream: make some more scp-in-SFTP mode better match Unix idioms - - suggested by deraadt@ - - OpenBSD-Commit-ID: 0f2439404ed4cf0b0be8bf49a1ee734836e1ac87 - -commit e694f8ac4409931e67d08ac44ed251b20b10a957 -Author: djm@openbsd.org -Date: Thu Sep 16 15:11:19 2021 +0000 - - upstream: allow log_stderr==2 to prefix log messages with argv[0] - - use this to make scp's SFTP mode error messages more scp-like - - prompted by and ok deraadt@ - - OpenBSD-Commit-ID: 0e821dbde423fc2280e47414bdc22aaa5b4e0733 - -commit 8a7a06ee505cb833e613f74a07392e9296286c30 -Author: Darren Tucker -Date: Fri Sep 17 13:03:31 2021 +1000 - - Test against LibreSSL 3.2.6, 3.3.4, 3.4.0. - -commit c25c84074a47f700dd6534995b4af4b456927150 -Author: djm@openbsd.org -Date: Thu Sep 16 05:36:03 2021 +0000 - - upstream: missing space character in ssh -G output broke the - - t-sshcfgparse regression test; spotted by anton@ - - OpenBSD-Commit-ID: bcc36fae2f233caac4baa8e58482da4aa350eed0 - -commit a4bee1934bf5e5575fea486628f4123d6a29dff8 -Author: djm@openbsd.org -Date: Wed Sep 15 06:56:01 2021 +0000 - - upstream: allow CanonicalizePermittedCNAMEs=none in ssh_config; ok - - markus@ - - OpenBSD-Commit-ID: 668a82ba8e56d731b26ffc5703213bfe071df623 - -commit d0fffc88c8fe90c1815c6f4097bc8cbcabc0f3dd -Author: mbuhl@openbsd.org -Date: Tue Sep 14 11:04:21 2021 +0000 - - upstream: put back the mux_ctx memleak fix for SSH_CHANNEL_MUX_CLIENT - - OK mfriedl@ - - OpenBSD-Commit-ID: 1aba1da828956cacaadb81a637338734697d9798 - -commit 19b3d846f06697c85957ab79a63454f57f8e22d6 -Author: schwarze@openbsd.org -Date: Sat Sep 11 09:05:50 2021 +0000 - - upstream: Do not ignore SIGINT while waiting for input if editline(3) - - is not used. Instead, in non-interactive mode, exit sftp(1), like for other - serious errors. As pointed out by dtucker@, when compiled without editline(3) - support in portable OpenSSH, the el == NULL branch is also used for - interactive mode. In that case, discard the input line and provide a fresh - prompt to the user just like in the case where editline(3) is used. OK djm@ - - OpenBSD-Commit-ID: 7d06f4d3ebba62115527fafacf38370d09dfb393 - -commit ba61123eef9c6356d438c90c1199a57a0d7bcb0a -Author: djm@openbsd.org -Date: Sat Sep 11 00:40:24 2021 +0000 - - upstream: when using SFTP protocol, continue transferring files after a - - transfer error occurs. This matches original scp/rcp behaviour. ok dtucker@ - - OpenBSD-Commit-ID: dfe4558d71dd09707e9b5d6e7d2e53b793da69fa - -commit b0ec59a708b493c6f3940336b1a537bcb64dd2a7 -Author: dtucker@openbsd.org -Date: Fri Sep 10 11:38:38 2021 +0000 - - upstream: Document that non-interactive commands are run via the user's - - shell using the -c flag. ok jmc@ - - OpenBSD-Commit-ID: 4f0d912077732eead10423afd1acf4fc0ceec477 - -commit 66a658b5d9e009ea11f8a0ca6e69c7feb2d851ea -Author: dtucker@openbsd.org -Date: Fri Sep 10 10:26:02 2021 +0000 - - upstream: Document behaviour of arguments following non-interactive - - commands. Prompted by github PR#139 from EvanTheB, feedback & ok djm@ jmc@ - - OpenBSD-Commit-ID: fc758d1fe0471dfab4304fcad6cd4ecc3d79162a - -commit 1d47e28e407d1f95fdf8f799be23f48dcfa5206b -Author: dtucker@openbsd.org -Date: Fri Sep 10 07:11:11 2021 +0000 - - upstream: Clarify which file's attributes -p preserves, and that - - it's specifically the file mode bits. bz#3340 from calestyo at scientia.net, - ok djm@ jmc@ - - OpenBSD-Commit-ID: f09e6098ed1c4be00c730873049825f8ee7cb884 - -commit b344db7a413478e4c21e4cadba4a970ad3e6128a -Author: djm@openbsd.org -Date: Fri Sep 10 05:46:09 2021 +0000 - - upstream: openssh-7.4 was incorrectly listed twice; spotted by - - Dmitry Belyavskiy, ok dtucker@ - - OpenBSD-Commit-ID: 4b823ae448f6e899927ce7b04225ac9e489f58ef - -commit 9136d6239ad7a4a293e0418a49b69e70c76d58b8 -Author: jmc@openbsd.org -Date: Thu Sep 9 06:17:39 2021 +0000 - - upstream: - move CAVEATS to its correct order - use the term - - "legacy" protocol rather than "original", as the latter made the text - misleading - uppercase SCP - - ok djm - - OpenBSD-Commit-ID: 8479255746d5fa76a358ee59e7340fecf4245ff0 - -commit 2d678c5e3bdc2f5c99f7af5122e9d054925d560d -Author: David Carlier -Date: Wed Sep 8 19:49:54 2021 +0100 - - Disable tracing on FreeBSD using procctl. - - Placed at the start of platform_disable_tracing() to prevent declaration - after code errors from strict C89 compilers (in the unlikely event that - more than one method is enabled). - -commit 73050fa38fb36ae3326d768b574806352b97002d -Author: djm@openbsd.org -Date: Wed Sep 8 23:31:39 2021 +0000 - - upstream: Use the SFTP protocol by default. The original scp/rcp - - protocol remains available via the -O flag. - - Note that ~user/ prefixed paths in SFTP mode require a protocol extension - that was first shipped in OpenSSH 8.7. - - ok deraadt, after baking in snaps for a while without incident - - OpenBSD-Commit-ID: 23588976e28c281ff5988da0848cb821fec9213c - -commit c4565e69ffa2485cff715aa842ea7a350296bfb6 -Author: Darren Tucker -Date: Wed Sep 8 21:09:49 2021 +1000 - - Really fix test on OpenSSL 1.1.1 stable. - -commit 79f1bb5f56cef3ae9276207316345b8309248478 -Author: Darren Tucker -Date: Wed Sep 8 18:51:39 2021 +1000 - - Correct OpenSSL 1.1.1 stable identifier. - -commit b6255593ed5ccbe5e7d3d4b26b2ad31ad4afc232 -Author: Darren Tucker -Date: Wed Sep 8 18:39:44 2021 +1000 - - Increment nfds when coming from startup_pipe. - - If we have to increase nfds because startup_pipe[0] is above any of the - descriptors passed in the fd_sets, we also need to add 1 to nfds since - select takes highest FD number plus one. bz#3345 from yaroslav.kuzmin - at vmssoftware.com. - -commit a3e92a6794817df6012ac8546aea19652cc91b61 -Author: Darren Tucker -Date: Wed Sep 8 13:45:10 2021 +1000 - - Tests for OpenSSL 3.0.0 release & 1.1.1 branch. - -commit 4afe431da98ec1cf6a2933fe5658f4fd68dee9e2 -Author: djm@openbsd.org -Date: Wed Sep 8 03:23:44 2021 +0000 - - upstream: correct my mistake in previous fix; spotted by halex - - OpenBSD-Commit-ID: 3cc62d92e3f70006bf02468fc146bfc36fffa183 - -commit ca0e455b9331213ff9505a21b94c38e34faa2bba -Author: djm@openbsd.org -Date: Tue Sep 7 06:03:51 2021 +0000 - - upstream: avoid NULL deref in -Y find-principals. Report and fix - - from Carlo Marcelo Arenas Belón - MIME-Version: 1.0 - Content-Type: text/plain; charset=UTF-8 - Content-Transfer-Encoding: 8bit - - OpenBSD-Commit-ID: 6238486f8ecc888d6ccafcd9ad99e621bb41f1e0 - -commit 37616807f150fb46610bbd5031c31af4857ad1e9 -Author: millert@openbsd.org -Date: Mon Sep 6 00:36:01 2021 +0000 - - upstream: revision 1.381 neglected to remove - - sChallengeResponseAuthentication from the enum. Noticed by - christos@zoulas.com. OK dtucker@ - - OpenBSD-Commit-ID: b533283a4dd6d04a867da411a4c7a8fbc90e34ff - -commit 7acb3578cdfec0b3d34501408071f7a96c1684ea -Author: Darren Tucker -Date: Sun Sep 5 20:45:42 2021 +1000 - - Correct version_num for OpenSSL dev branch. - -commit 65bb01111320dfd0d25e21e1fd4d3f2b77532669 -Author: Darren Tucker -Date: Sun Sep 5 19:37:39 2021 +1000 - - Test against OpenSSL 3 branch as well as dev. - - Now that OpenSSL development has moved to 3.1, test against the most - recent version of the openssl-3.0 branch too. - -commit 864ed0d5e04a503b97202c776b7cf3f163f3eeaa -Author: Darren Tucker -Date: Sun Sep 5 19:33:22 2021 +1000 - - OpenSSL development is now 3.1.* - -commit a60209a586a928f92ab323bf23bd07f57093342e -Author: dtucker@openbsd.org -Date: Fri Sep 3 07:43:23 2021 +0000 - - upstream: Use .Cm instead of .Dq in StrictHostKeyChecking list for - - consistency. Patch from scop via github PR#257, ok jmc@ - - OpenBSD-Commit-ID: 3652a91564570779431802c31224fb4a9cf39872 - -commit 8d1d9eb6de37331e872700e9e399a3190cca1242 -Author: dtucker@openbsd.org -Date: Fri Sep 3 07:27:03 2021 +0000 - - upstream: Mention using ssh -i for specifying the public key file - - in the case where the private key is loaded into ssh-agent but is not present - locally. Based on patch from rafork via github PR#215, ok jmc@ - - OpenBSD-Commit-ID: 2282e83b0ff78d2efbe705883b67240745fa5bb2 - -commit eb4362e5e3aa7ac26138b11e44d8c191910aff64 -Author: dtucker@openbsd.org -Date: Fri Sep 3 05:25:50 2021 +0000 - - upstream: Refer to KEX "algorithms" instead of "methods" to match - - other references and improve consistency. Patch from scop via github PR#241, - ok djm@ - - OpenBSD-Commit-ID: 840bc94ff6861b28d8603c8e8c16499bfb65e32c - -commit b3318946ce5725da43c4bf7eeea1b73129c47d2a -Author: dtucker@openbsd.org -Date: Fri Sep 3 05:12:25 2021 +0000 - - upstream: Remove redundant attrib_clear in upload_dir_internal. - - The subsequent call to stat_to_attrib clears the struct as its first step - anyway. From pmeinhardt via github PR#220, ok djm@ - - OpenBSD-Commit-ID: f5234fc6d7425b607e179acb3383f21716f3029e - -commit 7cc3fe28896e653956a6a2eed0a25d551b83a029 -Author: dtucker@openbsd.org -Date: Fri Sep 3 04:11:13 2021 +0000 - - upstream: Add test for client termination status on signal. - - Based on patch from Alexxz via github PR#235 with some tweaks, to - match patch in bz#3281. - - OpenBSD-Regress-ID: d87c7446fb8b5f8b45894fbbd6875df326e729e2 - -commit 5428b0d239f6b516c81d1dd15aa9fe9e60af75d4 -Author: deraadt@openbsd.org -Date: Thu Sep 2 21:03:54 2021 +0000 - - upstream: sys/param.h is not needed for any visible reason - - OpenBSD-Commit-ID: 8bdea2d0c75692e4c5777670ac039d4b01c1f368 - -commit 1ff38f34b4c4545eb28106629cafa1e0496bc726 -Author: Shchelkunov Artem -Date: Wed Aug 11 18:07:58 2021 +0500 - - Fix memory leak in error path. - - *info is allocated via xstrdup but was leaked in the PAM_AUTH_ERR path. - From github PR#266. - -commit cb37e2f0c0ca4fef844ed7edc5d0e3b7d0e83f6a -Author: dtucker@openbsd.org -Date: Wed Sep 1 03:16:06 2021 +0000 - - upstream: Fix ssh-rsa fallback for old PuTTY interop tests. - - OpenBSD-Regress-ID: a19ac929da604843a5b5f0f48d2c0eb6e0773d37 - -commit 8b02ef0f28dc24cda8cbcd8b7eb02bda8f8bbe59 -Author: dtucker@openbsd.org -Date: Wed Sep 1 00:50:27 2021 +0000 - - upstream: Add a function to skip remaining tests. - - Many tests skip tests for various reasons but not in a consistent way and - don't always clean up, so add that and switch the tests that do that over. - - OpenBSD-Regress-ID: 72d2ec90a3ee8849486956a808811734281af735 - -commit d486845c07324c04240f1674ac513985bd356f66 -Author: dtucker@openbsd.org -Date: Tue Aug 31 07:13:59 2021 +0000 - - upstream: Specify path to PuTTY keys. - - Portable needs this and it makes no difference on OpenBSD, so resync - them. (Id sync only, Portable already had this.) - - OpenBSD-Regress-ID: 33f6f66744455886d148527af8368811e4264162 - -commit d22b299115e27606e846b23490746f69fdd4fb38 -Author: dtucker@openbsd.org -Date: Tue Aug 31 06:13:23 2021 +0000 - - upstream: Better compat tests with old PuTTY. - - When running PuTTY interop tests and using a PuTTY version older than - 0.76, re-enable the ssh-rsa host key algorithm (the 256 and 512 variants - of RSA were added some time between 0.73 and 0.76). - - OpenBSD-Regress-ID: e6138d6987aa705fa1e4f216db0bb386e1ff38e1 - -commit 87ad70d605c3e39c9b8aa275db27120d7cc09b77 -Author: Darren Tucker -Date: Tue Aug 31 17:04:50 2021 +1000 - - Resync PuTTY interop tests. - - Resync behaviour when REGRESS_INTEROP_PUTTY is not set with OpenBSD. - -commit e47b82a7bf51021afac218bf59a3be121827653d -Author: dtucker@openbsd.org -Date: Tue Aug 31 01:25:27 2021 +0000 - - upstream: Specify hostkeyalgorithms in SSHFP test. - - Specify host key algorithms in sshd's default set for the SSHFP test, - from djm@. Make the reason for when the test is skipped a bit clearer. - - OpenBSD-Regress-ID: 4f923dfc761480d5411de17ea6f0b30de3e32cea - -commit 7db3e0a9e8477c018757b59ee955f7372c0b55fb -Author: djm@openbsd.org -Date: Mon Aug 30 01:15:45 2021 +0000 - - upstream: adapt to RSA/SHA1 deprectation - - OpenBSD-Regress-ID: 952397c39a22722880e4de9d1c50bb1a14f907bb - -commit 2344750250247111a6c3c6a4fe84ed583a61cc11 -Author: djm@openbsd.org -Date: Sun Aug 29 23:53:10 2021 +0000 - - upstream: After years of forewarning, disable the RSA/SHA-1 - - signature algorithm by default. It is feasible to create colliding SHA1 - hashes, so we need to deprecate its use. - - RSA/SHA-256/512 remains available and will be transparently selected - instead of RSA/SHA1 for most SSH servers released in the last five+ - years. There is no need to regenerate RSA keys. - - The use of RSA/SHA1 can be re-enabled by adding "ssh-rsa" to the - PubkeyAcceptedAlgorithms directives on the client and server. - - ok dtucker deraadt - - OpenBSD-Commit-ID: 189bcc4789c7254e09e23734bdd5def8354ff1d5 - -commit 56c4455d3b54b7d481c77c82115c830b9c8ce328 -Author: djm@openbsd.org -Date: Sun Aug 29 23:44:07 2021 +0000 - - upstream: wrap at 80 columns - - OpenBSD-Commit-ID: 47ca2286d6b52a9747f34da16d742879e1a37bf0 - -commit 95401eea8503943449f712e5f3de52fc0bc612c5 -Author: Darren Tucker -Date: Fri Aug 20 18:14:13 2021 +1000 - - Replace shell function with ssh-keygen -A. - - Prevents the init script in the SysV package from trying (and failing) - to generate unsupported key types. Remove now-unused COMMENT_OUT_ECC. - ok tim@ - -commit d83ec9ed995a76ed1d5c65cf10b447222ec86131 -Author: Darren Tucker -Date: Fri Aug 20 15:39:05 2021 +1000 - - Remove obsolete Redhat PAM config and init script. - -commit e1a596186c81e65a34ce13076449712d3bf97eb4 -Author: Damien Miller -Date: Fri Aug 20 14:03:49 2021 +1000 - - depend - -commit 5450606c8f7f7a0d70211cea78bc2dab74ab35d1 -Author: Damien Miller -Date: Fri Aug 20 13:59:43 2021 +1000 - - update version numbers - -commit feee2384ab8d694c770b7750cfa76a512bdf8246 -Author: djm@openbsd.org -Date: Fri Aug 20 03:22:55 2021 +0000 - - upstream: openssh-8.7 - - OpenBSD-Commit-ID: 8769dff0fd76ae3193d77bf83b439adee0f300cd - -commit 9a2ed62173cc551b2b5f479460bb015b19499de8 -Author: Darren Tucker -Date: Fri Aug 20 10:48:13 2021 +1000 - - Also check pid in pselect_notify_setup. - - Spotted by djm@. - -commit deaadcb93ca15d4f38aa38fb340156077792ce87 -Author: Darren Tucker -Date: Fri Aug 20 08:39:33 2021 +1000 - - Prefix pselect functions to clarify debug messages - -commit 10e45654cff221ca60fd35ee069df67208fcf415 -Author: Darren Tucker -Date: Fri Aug 20 08:30:42 2021 +1000 - - Fix race in pselect replacement code. - - On the second and subsequent calls to pselect the notify_pipe was not - added to the select readset, opening up a race that om G. Christensen - discovered on multiprocessor Solaris <=9 systems. - - Also reinitialize notify_pipe if the pid changes. This will prevent a - parent and child from using the same FD, although this is not an issue - in the current structure it might be in future. - -commit 464ba22f1e38d25402e5ec79a9b8d34a32df5a3f -Author: Darren Tucker -Date: Wed Aug 18 12:51:30 2021 +1000 - - Check compiler for c99 declarations after code. - - The sntrup761 reference code contains c99-style declarations after code - so don't try to build that if the compiler doesn't support it. - -commit 7d878679a4b155a359d32104ff473f789501748d -Author: Darren Tucker -Date: Tue Aug 17 15:12:04 2021 +1000 - - Remove trailing backslash on regress-unit-binaries - -commit b71b2508f17c68c5d9dbbe537686d81cedb9a781 -Author: Darren Tucker -Date: Tue Aug 17 07:59:27 2021 +1000 - - Put stdint.h inside HAVE_STDINT_H. - - From Tom G. Christensen. - -commit 6a24567a29bd7b4ab64e1afad859ea845cbc6b8c -Author: Darren Tucker -Date: Mon Aug 16 14:13:02 2021 +1000 - - Improve github test driver script. - - - use a trap to always output any failed regress logs (since the script - sets -e, the existing log output is never invoked). - - pass LTESTS and SKIP_LTESTS when re-running with sshd options (eg. - UsePAM). - -commit b467cf13705f59ed348b620722ac098fe31879b7 -Author: Darren Tucker -Date: Mon Aug 16 11:32:23 2021 +1000 - - Remove deprecated ubuntu-16.04 test targets. - - Github has deprecated ubuntu-16.04 and it will be removed on 20 - September. - -commit 20e6eefcdf78394f05e453d456c1212ffaa6b6a4 -Author: Darren Tucker -Date: Sun Aug 15 23:25:26 2021 +1000 - - Skip agent ptrace test on hurd. - -commit 7c9115bbbf958fbf85259a061c1122e2d046aabf -Author: Darren Tucker -Date: Sun Aug 15 19:37:22 2021 +1000 - - Add hurd test target. - -commit 7909a566f6c6a78fcd30708dc49f4e4f9bb80ce3 -Author: Darren Tucker -Date: Sun Aug 15 12:45:10 2021 +1000 - - Skip scp3 tests on all dfly58 and 60 configs. - -commit e65198e52cb03534e8c846d1bca74c310b1526de -Author: Tim Rice -Date: Sat Aug 14 13:08:07 2021 -0700 - - openbsd-compat/openbsd-compat.h: put bsd-signal.h before bsd-misc.h - to get sigset_t from signal.h needed for the pselect replacement. - -commit e50635640f79920d9375e0155cb3f4adb870eee5 -Author: Darren Tucker -Date: Fri Aug 13 13:21:00 2021 +1000 - - Test OpenSSH from OpenBSD head on 6.8 and 6.9. - -commit e0ba38861c490c680117b7fe0a1d61a181cd00e7 -Author: Darren Tucker -Date: Fri Aug 13 13:00:14 2021 +1000 - - Skip scp3 test on dragonfly 58 and 60. - - The tests hang, so skip until we figure them out. - -commit dcce2a2bcf007bf817a2fb0dce3db83fa9201e92 -Author: djm@openbsd.org -Date: Thu Aug 12 23:59:25 2021 +0000 - - upstream: mention that CASignatureAlgorithms accepts +/- similarly to - - the other algorithm list directives; ok jmc bz#3335 - - OpenBSD-Commit-ID: 0d46b53995817052c78e2dce9dbd133963b073d9 - -commit 090a82486e5d7a8f7f16613d67e66a673a40367f -Author: schwarze@openbsd.org -Date: Thu Aug 12 09:59:00 2021 +0000 - - upstream: In the editline(3) branch of the sftp(1) event loop, - - handle SIGINT rather than ignoring it, such that the user can use Ctrl-C to - discard the currently edited command line and get a fresh prompt, just like - in ftp(1), bc(1), and in shells. - - It is critical to not use ssl_signal() for this particular case - because that function unconditionally sets SA_RESTART, but here we - need the signal to interrupt the read(2) in the el_gets(3) event loop. - - OK dtucker@ deraadt@ - - OpenBSD-Commit-ID: 8025115a773f52e9bb562eaab37ea2e021cc7299 - -commit e1371e4f58404d6411d9f95eb774b444cea06a26 -Author: naddy@openbsd.org -Date: Wed Aug 11 14:07:54 2021 +0000 - - upstream: scp: tweak man page and error message for -3 by default - - Now that the -3 option is enabled by default, flip the documentation - and error message logic from "requires -3" to "blocked by -R". - - ok djm@ - - OpenBSD-Commit-ID: a872592118444fb3acda5267b2a8c3d4c4252020 - -commit 49f46f6d77328a3d10a758522b670a3e8c2235e7 -Author: naddy@openbsd.org -Date: Wed Aug 11 14:05:19 2021 +0000 - - upstream: scp: do not spawn ssh with two -s flags for - - remote-to-remote copies - - Do not add another "-s" to the argument vector every time an SFTP - connection is initiated. Instead, introduce a subsystem flag to - do_cmd() and add "-s" when the flag is set. - - ok djm@ - - OpenBSD-Commit-ID: 25df69759f323661d31b2e1e790faa22e27966c1 - -commit 2a2cd00783e1da45ee730b7f453408af1358ef5b -Author: djm@openbsd.org -Date: Wed Aug 11 08:55:04 2021 +0000 - - upstream: test -Oprint-pubkey - - OpenBSD-Regress-ID: 3d51afb6d1f287975fb6fddd7a2c00a3bc5094e0 - -commit b9f4635ea5bc33ed5ebbacf332d79bae463b0f54 -Author: djm@openbsd.org -Date: Wed Aug 11 08:54:17 2021 +0000 - - upstream: when verifying sshsig signatures, support an option - - (-Oprint-pubkey) to dump the full public key to stdout; based on patch from - Fabian Stelzer; ok markus@ - - OpenBSD-Commit-ID: 0598000e5b9adfb45d42afa76ff80daaa12fc3e2 - -commit 750c1a45ba4e8ad63793d49418a0780e77947b9b -Author: djm@openbsd.org -Date: Wed Aug 11 05:21:32 2021 +0000 - - upstream: oops, missed one more %p - - OpenBSD-Commit-ID: e7e62818d1564cc5cd9086eaf7a51cbd1a9701eb - -commit b5aa27b69ab2e1c13ac2b5ad3f8f7d389bad7489 -Author: djm@openbsd.org -Date: Wed Aug 11 05:20:17 2021 +0000 - - upstream: remove a bunch of %p in format strings; leftovers of - - debuggings past. prompted by Michael Forney, ok dtucker@ - - OpenBSD-Commit-ID: 4853a0d6c9cecaba9ecfcc19066e52d3a8dcb2ac - -commit 419aa01123db5ff5dbc68b2376ef23b222862338 -Author: Darren Tucker -Date: Wed Aug 11 09:21:09 2021 +1000 - - Add includes.h to compat tests. - - On platforms where closefrom returns void (eg glibc>=2.34) the prototype - for closefrom in its compat tests would cause compile errors. Remove - this and have the tests pull in the compat headers in the same way as - the main code. bz#3336. - -commit 931f592f26239154eea3eb35a086585897b1a185 -Author: djm@openbsd.org -Date: Tue Aug 10 03:35:45 2021 +0000 - - upstream: adapt to scp -M flag change; make scp3.sh test SFTP mode too - - OpenBSD-Regress-ID: 43fea26704a0f0b962b53c1fabcb68179638f9c0 - -commit 391ca67fb978252c48d20c910553f803f988bd37 -Author: djm@openbsd.org -Date: Tue Aug 10 03:33:34 2021 +0000 - - upstream: Prepare for a future where scp(1) uses the SFTP protocol by - - default. Replace recently added -M option to select the protocol with -O - (olde) and -s (SFTP) flags, and label the -s flag with a clear warning that - it will be removed in the near future (so no, don't use it in scripts!). - - prompted by/feedback from deraadt@ - - OpenBSD-Commit-ID: 92ad72cc6f0023c9be9e316d8b30eb6d8d749cfc - -commit bfdd4b722f124a4fa9173d20dd64dd0fc69856be -Author: djm@openbsd.org -Date: Mon Aug 9 23:56:36 2021 +0000 - - upstream: make scp -3 the default for remote-to-remote copies. It - - provides a much better and more intuitive user experience and doesn't require - exposing credentials to the source host. - - thanks naddy@ for catching the missing argument in usage() - - "Yes please!" - markus@ - "makes a lot of sense" - deraadt@ - "the right thing to do" - dtucker@ - - OpenBSD-Commit-ID: d0d2af5f0965c5192ba5b2fa461c9f9b130e5dd9 - -commit 2f7a3b51cef689ad9e93d0c6c17db5a194eb5555 -Author: djm@openbsd.org -Date: Mon Aug 9 23:49:31 2021 +0000 - - upstream: make scp in SFTP mode try to use relative paths as much - - as possible. Previosuly, it would try to make relative and ~/-rooted paths - absolute before requesting transfers. - - prompted by and much discussion deraadt@ - ok markus@ - - OpenBSD-Commit-ID: 46639d382ea99546a4914b545fa7b00fa1be5566 - -commit 2ab864010e0a93c5dd95116fb5ceaf430e2fc23c -Author: djm@openbsd.org -Date: Mon Aug 9 23:47:44 2021 +0000 - - upstream: SFTP protocol extension to allow the server to expand - - ~-prefixed paths, in particular ~user ones. Allows scp in sftp mode to accept - these paths, like scp in rcp mode does. - - prompted by and much discussion deraadt@ - ok markus@ - - OpenBSD-Commit-ID: 7d794def9e4de348e1e777f6030fc9bafdfff392 - -commit 41b019ac067f1d1f7d99914d0ffee4d2a547c3d8 -Author: djm@openbsd.org -Date: Mon Aug 9 23:44:32 2021 +0000 - - upstream: when scp is in SFTP mode, try to deal better with ~ - - prefixed paths. ~user paths aren't supported, but ~/ paths will be accepted - and prefixed with the SFTP server starting directory (more to come) - - prompted by and discussed with deraadt@ - ok markus@ - - OpenBSD-Commit-ID: 263a071f14555c045fd03132a8fb6cbd983df00d - -commit b4b3f3da6cdceb3fd168b5fab69d11fba73bd0ae -Author: djm@openbsd.org -Date: Mon Aug 9 07:21:01 2021 +0000 - - upstream: on fatal errors, make scp wait for ssh connection before - - exiting avoids LogLevel=verbose (or greater) messages from ssh appearing - after scp has returned exited and control has returned to the shell; ok - markus@ - - (this was originally committed as r1.223 along with unrelated stuff that - I rolled back in r1.224) - - OpenBSD-Commit-ID: 1261fd667ad918484889ed3d7aec074f3956a74b - -commit 2ae7771749e0b4cecb107f9d4860bec16c3f4245 -Author: djm@openbsd.org -Date: Mon Aug 9 07:19:12 2021 +0000 - - upstream: rever r1.223 - I accidentally committed unrelated changes - - OpenBSD-Commit-ID: fb73f3865b2647a27dd94db73d6589506a9625f9 - -commit 986abe94d481a1e82a01747360bd767b96b41eda -Author: djm@openbsd.org -Date: Mon Aug 9 07:16:09 2021 +0000 - - upstream: show only the final path component in the progress meter; - - more useful with long paths (that may truncate) and better matches - traditional scp behaviour; spotted by naddy@ ok deraadt@ - - OpenBSD-Commit-ID: 26b544d0074f03ebb8a3ebce42317d8d7ee291a3 - -commit 2b67932bb3176dee4fd447af4368789e04a82b93 -Author: djm@openbsd.org -Date: Mon Aug 9 07:13:54 2021 +0000 - - upstream: on fatal errors, make scp wait for ssh connection before - - exiting avoids LogLevel=verbose (or greater) messages from ssh appearing - after scp has returned exited and control has returned to the shell; ok - markus@ - - OpenBSD-Commit-ID: ef9dab5ef5ae54a6a4c3b15d380568e94263456c diff --git a/PROTOCOL b/PROTOCOL index 27804d0cadbd..d453c779be92 100644 --- a/PROTOCOL +++ b/PROTOCOL @@ -1,715 +1,748 @@ This documents OpenSSH's deviations and extensions to the published SSH protocol. Note that OpenSSH's sftp and sftp-server implement revision 3 of the SSH filexfer protocol described in: https://www.openssh.com/txt/draft-ietf-secsh-filexfer-02.txt Newer versions of the draft will not be supported, though some features are individually implemented as extensions described below. The protocol used by OpenSSH's ssh-agent is described in the file PROTOCOL.agent 1. Transport protocol changes 1.1. transport: Protocol 2 MAC algorithm "umac-64@openssh.com" This is a new transport-layer MAC method using the UMAC algorithm (rfc4418). This method is identical to the "umac-64" method documented in: https://www.openssh.com/txt/draft-miller-secsh-umac-01.txt 1.2. transport: Protocol 2 compression algorithm "zlib@openssh.com" This transport-layer compression method uses the zlib compression algorithm (identical to the "zlib" method in rfc4253), but delays the start of compression until after authentication has completed. This avoids exposing compression code to attacks from unauthenticated users. The method is documented in: https://www.openssh.com/txt/draft-miller-secsh-compression-delayed-00.txt 1.3. transport: New public key algorithms "ssh-rsa-cert-v01@openssh.com", "ssh-dsa-cert-v01@openssh.com", "ecdsa-sha2-nistp256-cert-v01@openssh.com", "ecdsa-sha2-nistp384-cert-v01@openssh.com" and "ecdsa-sha2-nistp521-cert-v01@openssh.com" OpenSSH introduces new public key algorithms to support certificate authentication for users and host keys. These methods are documented in the file PROTOCOL.certkeys 1.4. transport: Elliptic Curve cryptography OpenSSH supports ECC key exchange and public key authentication as specified in RFC5656. Only the ecdsa-sha2-nistp256, ecdsa-sha2-nistp384 and ecdsa-sha2-nistp521 curves over GF(p) are supported. Elliptic curve points encoded using point compression are NOT accepted or generated. 1.5 transport: Protocol 2 Encrypt-then-MAC MAC algorithms OpenSSH supports MAC algorithms, whose names contain "-etm", that perform the calculations in a different order to that defined in RFC 4253. These variants use the so-called "encrypt then MAC" ordering, calculating the MAC over the packet ciphertext rather than the plaintext. This ordering closes a security flaw in the SSH transport protocol, where decryption of unauthenticated ciphertext provided a "decryption oracle" that could, in conjunction with cipher flaws, reveal session plaintext. Specifically, the "-etm" MAC algorithms modify the transport protocol to calculate the MAC over the packet ciphertext and to send the packet length unencrypted. This is necessary for the transport to obtain the length of the packet and location of the MAC tag so that it may be verified without decrypting unauthenticated data. As such, the MAC covers: mac = MAC(key, sequence_number || packet_length || encrypted_packet) where "packet_length" is encoded as a uint32 and "encrypted_packet" contains: byte padding_length byte[n1] payload; n1 = packet_length - padding_length - 1 byte[n2] random padding; n2 = padding_length 1.6 transport: AES-GCM OpenSSH supports the AES-GCM algorithm as specified in RFC 5647. Because of problems with the specification of the key exchange the behaviour of OpenSSH differs from the RFC as follows: AES-GCM is only negotiated as the cipher algorithms "aes128-gcm@openssh.com" or "aes256-gcm@openssh.com" and never as an MAC algorithm. Additionally, if AES-GCM is selected as the cipher the exchanged MAC algorithms are ignored and there doesn't have to be a matching MAC. 1.7 transport: chacha20-poly1305@openssh.com authenticated encryption OpenSSH supports authenticated encryption using ChaCha20 and Poly1305 as described in PROTOCOL.chacha20poly1305. 1.8 transport: curve25519-sha256@libssh.org key exchange algorithm OpenSSH supports the use of ECDH in Curve25519 for key exchange as described at: http://git.libssh.org/users/aris/libssh.git/plain/doc/curve25519-sha256@libssh.org.txt?h=curve25519 This is identical to curve25519-sha256 as later published in RFC8731. +1.9 transport: ping facility + +OpenSSH implements a transport level ping message SSH2_MSG_PING +and a corresponding SSH2_MSG_PONG reply. + +#define SSH2_MSG_PING 192 +#define SSH2_MSG_PONG 193 + +The ping message is simply: + + byte SSH_MSG_PING + string data + +The reply copies the data (which may be the empty string) from the +ping: + + byte SSH_MSG_PONG + string data + +Replies are sent in order. They are sent immediately except when rekeying +is in progress, in which case they are queued until rekeying completes. + +The server advertises support for these messages using the +SSH2_MSG_EXT_INFO mechanism (RFC8308), with the following message: + + string "ping@openssh.com" + string "0" (version) + +The ping/reply message is implemented at the transport layer rather +than as a named global or channel request to allow pings with very +short packet lengths, which would not be possible with other +approaches. + 2. Connection protocol changes 2.1. connection: Channel write close extension "eow@openssh.com" The SSH connection protocol (rfc4254) provides the SSH_MSG_CHANNEL_EOF message to allow an endpoint to signal its peer that it will send no more data over a channel. Unfortunately, there is no symmetric way for an endpoint to request that its peer should cease sending data to it while still keeping the channel open for the endpoint to send data to the peer. This is desirable, since it saves the transmission of data that would otherwise need to be discarded and it allows an endpoint to signal local processes of the condition, e.g. by closing the corresponding file descriptor. OpenSSH implements a channel extension message to perform this signalling: "eow@openssh.com" (End Of Write). This message is sent by an endpoint when the local output of a session channel is closed or experiences a write error. The message is formatted as follows: byte SSH_MSG_CHANNEL_REQUEST uint32 recipient channel string "eow@openssh.com" boolean FALSE On receiving this message, the peer SHOULD cease sending data of the channel and MAY signal the process from which the channel data originates (e.g. by closing its read file descriptor). As with the symmetric SSH_MSG_CHANNEL_EOF message, the channel does remain open after a "eow@openssh.com" has been sent and more data may still be sent in the other direction. This message does not consume window space and may be sent even if no window space is available. NB. due to certain broken SSH implementations aborting upon receipt of this message (in contravention of RFC4254 section 5.4), this message is only sent to OpenSSH peers (identified by banner). Other SSH implementations may be listed to receive this message upon request. 2.2. connection: disallow additional sessions extension "no-more-sessions@openssh.com" Most SSH connections will only ever request a single session, but a attacker may abuse a running ssh client to surreptitiously open additional sessions under their control. OpenSSH provides a global request "no-more-sessions@openssh.com" to mitigate this attack. When an OpenSSH client expects that it will never open another session (i.e. it has been started with connection multiplexing disabled), it will send the following global request: byte SSH_MSG_GLOBAL_REQUEST string "no-more-sessions@openssh.com" char want-reply On receipt of such a message, an OpenSSH server will refuse to open future channels of type "session" and instead immediately abort the connection. Note that this is not a general defence against compromised clients (that is impossible), but it thwarts a simple attack. NB. due to certain broken SSH implementations aborting upon receipt of this message, the no-more-sessions request is only sent to OpenSSH servers (identified by banner). Other SSH implementations may be listed to receive this message upon request. 2.3. connection: Tunnel forward extension "tun@openssh.com" OpenSSH supports layer 2 and layer 3 tunnelling via the "tun@openssh.com" channel type. This channel type supports forwarding of network packets with datagram boundaries intact between endpoints equipped with interfaces like the BSD tun(4) device. Tunnel forwarding channels are requested by the client with the following packet: byte SSH_MSG_CHANNEL_OPEN string "tun@openssh.com" uint32 sender channel uint32 initial window size uint32 maximum packet size uint32 tunnel mode uint32 remote unit number The "tunnel mode" parameter specifies whether the tunnel should forward layer 2 frames or layer 3 packets. It may take one of the following values: SSH_TUNMODE_POINTOPOINT 1 /* layer 3 packets */ SSH_TUNMODE_ETHERNET 2 /* layer 2 frames */ The "tunnel unit number" specifies the remote interface number, or may be 0x7fffffff to allow the server to automatically choose an interface. A server that is not willing to open a client-specified unit should refuse the request with a SSH_MSG_CHANNEL_OPEN_FAILURE error. On successful open, the server should reply with SSH_MSG_CHANNEL_OPEN_SUCCESS. Once established the client and server may exchange packet or frames over the tunnel channel by encapsulating them in SSH protocol strings and sending them as channel data. This ensures that packet boundaries are kept intact. Specifically, packets are transmitted using normal SSH_MSG_CHANNEL_DATA packets: byte SSH_MSG_CHANNEL_DATA uint32 recipient channel string data The contents of the "data" field for layer 3 packets is: uint32 packet length uint32 address family byte[packet length - 4] packet data The "address family" field identifies the type of packet in the message. It may be one of: SSH_TUN_AF_INET 2 /* IPv4 */ SSH_TUN_AF_INET6 24 /* IPv6 */ The "packet data" field consists of the IPv4/IPv6 datagram itself without any link layer header. The contents of the "data" field for layer 2 packets is: uint32 packet length byte[packet length] frame The "frame" field contains an IEEE 802.3 Ethernet frame, including header. 2.4. connection: Unix domain socket forwarding OpenSSH supports local and remote Unix domain socket forwarding using the "streamlocal" extension. Forwarding is initiated as per TCP sockets but with a single path instead of a host and port. Similar to direct-tcpip, direct-streamlocal is sent by the client to request that the server make a connection to a Unix domain socket. byte SSH_MSG_CHANNEL_OPEN string "direct-streamlocal@openssh.com" uint32 sender channel uint32 initial window size uint32 maximum packet size string socket path string reserved uint32 reserved Similar to forwarded-tcpip, forwarded-streamlocal is sent by the server when the client has previously send the server a streamlocal-forward GLOBAL_REQUEST. byte SSH_MSG_CHANNEL_OPEN string "forwarded-streamlocal@openssh.com" uint32 sender channel uint32 initial window size uint32 maximum packet size string socket path string reserved for future use The reserved field is not currently defined and is ignored on the remote end. It is intended to be used in the future to pass information about the socket file, such as ownership and mode. The client currently sends the empty string for this field. Similar to tcpip-forward, streamlocal-forward is sent by the client to request remote forwarding of a Unix domain socket. byte SSH2_MSG_GLOBAL_REQUEST string "streamlocal-forward@openssh.com" boolean TRUE string socket path Similar to cancel-tcpip-forward, cancel-streamlocal-forward is sent by the client cancel the forwarding of a Unix domain socket. byte SSH2_MSG_GLOBAL_REQUEST string "cancel-streamlocal-forward@openssh.com" boolean FALSE string socket path 2.5. connection: hostkey update and rotation "hostkeys-00@openssh.com" and "hostkeys-prove-00@openssh.com" OpenSSH supports a protocol extension allowing a server to inform a client of all its protocol v.2 host keys after user-authentication has completed. byte SSH_MSG_GLOBAL_REQUEST string "hostkeys-00@openssh.com" char 0 /* want-reply */ string[] hostkeys Upon receiving this message, a client should check which of the supplied host keys are present in known_hosts. Note that the server may send key types that the client does not support. The client should disregard such keys if they are received. If the client identifies any keys that are not present for the host, it should send a "hostkeys-prove@openssh.com" message to request the server prove ownership of the private half of the key. byte SSH_MSG_GLOBAL_REQUEST string "hostkeys-prove-00@openssh.com" char 1 /* want-reply */ string[] hostkeys When a server receives this message, it should generate a signature using each requested key over the following: string "hostkeys-prove-00@openssh.com" string session identifier string hostkey These signatures should be included in the reply, in the order matching the hostkeys in the request: byte SSH_MSG_REQUEST_SUCCESS string[] signatures When the client receives this reply (and not a failure), it should validate the signatures and may update its known_hosts file, adding keys that it has not seen before and deleting keys for the server host that are no longer offered. These extensions let a client learn key types that it had not previously encountered, thereby allowing it to potentially upgrade from weaker key algorithms to better ones. It also supports graceful key rotation: a server may offer multiple keys of the same type for a period (to give clients an opportunity to learn them using this extension) before removing the deprecated key from those offered. 2.6. connection: SIGINFO support for "signal" channel request The SSH channels protocol (RFC4254 section 6.9) supports sending a signal to a session attached to a channel. OpenSSH supports one extension signal "INFO@openssh.com" that allows sending SIGINFO on BSD-derived systems. 3. Authentication protocol changes 3.1. Host-bound public key authentication This is trivial change to the traditional "publickey" authentication method. The authentication request is identical to the original method but for the name and one additional field: byte SSH2_MSG_USERAUTH_REQUEST string username string "ssh-connection" string "publickey-hostbound-v00@openssh.com" bool has_signature string pkalg string public key string server host key Because the entire SSH2_MSG_USERAUTH_REQUEST message is included in the signed data, this ensures that a binding between the destination user, the server identity and the session identifier is visible to the signer. OpenSSH uses this binding via signed data to implement per-key restrictions in ssh-agent. A server may advertise this method using the SSH2_MSG_EXT_INFO mechanism (RFC8308), with the following message: string "publickey-hostbound@openssh.com" string "0" (version) Clients should prefer host-bound authentication when advertised by server. 4. SFTP protocol changes 4.1. sftp: Reversal of arguments to SSH_FXP_SYMLINK When OpenSSH's sftp-server was implemented, the order of the arguments to the SSH_FXP_SYMLINK method was inadvertently reversed. Unfortunately, the reversal was not noticed until the server was widely deployed. Since fixing this to follow the specification would cause incompatibility, the current order was retained. For correct operation, clients should send SSH_FXP_SYMLINK as follows: uint32 id string targetpath string linkpath 4.2. sftp: Server extension announcement in SSH_FXP_VERSION OpenSSH's sftp-server lists the extensions it supports using the standard extension announcement mechanism in the SSH_FXP_VERSION server hello packet: uint32 3 /* protocol version */ string ext1-name string ext1-version string ext2-name string ext2-version ... string extN-name string extN-version Each extension reports its integer version number as an ASCII encoded string, e.g. "1". The version will be incremented if the extension is ever changed in an incompatible way. The server MAY advertise the same extension with multiple versions (though this is unlikely). Clients MUST check the version number before attempting to use the extension. 4.3. sftp: Extension request "posix-rename@openssh.com" This operation provides a rename operation with POSIX semantics, which are different to those provided by the standard SSH_FXP_RENAME in draft-ietf-secsh-filexfer-02.txt. This request is implemented as a SSH_FXP_EXTENDED request with the following format: uint32 id string "posix-rename@openssh.com" string oldpath string newpath On receiving this request the server will perform the POSIX operation rename(oldpath, newpath) and will respond with a SSH_FXP_STATUS message. This extension is advertised in the SSH_FXP_VERSION hello with version "1". 4.4. sftp: Extension requests "statvfs@openssh.com" and "fstatvfs@openssh.com" These requests correspond to the statvfs and fstatvfs POSIX system interfaces. The "statvfs@openssh.com" request operates on an explicit pathname, and is formatted as follows: uint32 id string "statvfs@openssh.com" string path The "fstatvfs@openssh.com" operates on an open file handle: uint32 id string "fstatvfs@openssh.com" string handle These requests return a SSH_FXP_STATUS reply on failure. On success they return the following SSH_FXP_EXTENDED_REPLY reply: uint32 id uint64 f_bsize /* file system block size */ uint64 f_frsize /* fundamental fs block size */ uint64 f_blocks /* number of blocks (unit f_frsize) */ uint64 f_bfree /* free blocks in file system */ uint64 f_bavail /* free blocks for non-root */ uint64 f_files /* total file inodes */ uint64 f_ffree /* free file inodes */ uint64 f_favail /* free file inodes for to non-root */ uint64 f_fsid /* file system id */ uint64 f_flag /* bit mask of f_flag values */ uint64 f_namemax /* maximum filename length */ The values of the f_flag bitmask are as follows: #define SSH_FXE_STATVFS_ST_RDONLY 0x1 /* read-only */ #define SSH_FXE_STATVFS_ST_NOSUID 0x2 /* no setuid */ Both the "statvfs@openssh.com" and "fstatvfs@openssh.com" extensions are advertised in the SSH_FXP_VERSION hello with version "2". 4.5. sftp: Extension request "hardlink@openssh.com" This request is for creating a hard link to a regular file. This request is implemented as a SSH_FXP_EXTENDED request with the following format: uint32 id string "hardlink@openssh.com" string oldpath string newpath On receiving this request the server will perform the operation link(oldpath, newpath) and will respond with a SSH_FXP_STATUS message. This extension is advertised in the SSH_FXP_VERSION hello with version "1". 4.6. sftp: Extension request "fsync@openssh.com" This request asks the server to call fsync(2) on an open file handle. uint32 id string "fsync@openssh.com" string handle On receiving this request, a server will call fsync(handle_fd) and will respond with a SSH_FXP_STATUS message. This extension is advertised in the SSH_FXP_VERSION hello with version "1". 4.7. sftp: Extension request "lsetstat@openssh.com" This request is like the "setstat" command, but sets file attributes on symlinks. It is implemented as a SSH_FXP_EXTENDED request with the following format: uint32 id string "lsetstat@openssh.com" string path ATTRS attrs See the "setstat" command for more details. This extension is advertised in the SSH_FXP_VERSION hello with version "1". 4.8. sftp: Extension request "limits@openssh.com" This request is used to determine various limits the server might impose. Clients should not attempt to exceed these limits as the server might sever the connection immediately. uint32 id string "limits@openssh.com" The server will respond with a SSH_FXP_EXTENDED_REPLY reply: uint32 id uint64 max-packet-length uint64 max-read-length uint64 max-write-length uint64 max-open-handles The 'max-packet-length' applies to the total number of bytes in a single SFTP packet. Servers SHOULD set this at least to 34000. The 'max-read-length' is the largest length in a SSH_FXP_READ packet. Even if the client requests a larger size, servers will usually respond with a shorter SSH_FXP_DATA packet. Servers SHOULD set this at least to 32768. The 'max-write-length' is the largest length in a SSH_FXP_WRITE packet the server will accept. Servers SHOULD set this at least to 32768. The 'max-open-handles' is the maximum number of active handles that the server allows (e.g. handles created by SSH_FXP_OPEN and SSH_FXP_OPENDIR packets). Servers MAY count internal file handles against this limit (e.g. system logging or stdout/stderr), so clients SHOULD NOT expect to open this many handles in practice. If the server doesn't enforce a specific limit, then the field may be set to 0. This implies the server relies on the OS to enforce limits (e.g. available memory or file handles), and such limits might be dynamic. The client SHOULD take care to not try to exceed reasonable limits. This extension is advertised in the SSH_FXP_VERSION hello with version "1". 4.9. sftp: Extension request "expand-path@openssh.com" This request supports canonicalisation of relative paths and those that need tilde-expansion, i.e. "~", "~/..." and "~user/..." These paths are expanded using shell-like rules and the resultant path is canonicalised similarly to SSH2_FXP_REALPATH. It is implemented as a SSH_FXP_EXTENDED request with the following format: uint32 id string "expand-path@openssh.com" string path Its reply is the same format as that of SSH2_FXP_REALPATH. This extension is advertised in the SSH_FXP_VERSION hello with version "1". 4.10. sftp: Extension request "copy-data" This request asks the server to copy data from one open file handle and write it to a different open file handle. This avoids needing to transfer the data across the network twice (a download followed by an upload). byte SSH_FXP_EXTENDED uint32 id string "copy-data" string read-from-handle uint64 read-from-offset uint64 read-data-length string write-to-handle uint64 write-to-offset The server will copy read-data-length bytes starting from read-from-offset from the read-from-handle and write them to write-to-handle starting from write-to-offset, and then respond with a SSH_FXP_STATUS message. It's equivalent to issuing a series of SSH_FXP_READ requests on read-from-handle and a series of requests of SSH_FXP_WRITE on write-to-handle. If read-from-handle and write-to-handle are the same, the server will fail the request and respond with a SSH_FX_INVALID_PARAMETER message. If read-data-length is 0, then the server will read data from the read-from-handle until EOF is reached. This extension is advertised in the SSH_FXP_VERSION hello with version "1". This request is identical to the "copy-data" request documented in: https://tools.ietf.org/html/draft-ietf-secsh-filexfer-extensions-00#section-7 4.11. sftp: Extension request "home-directory" This request asks the server to expand the specified user's home directory. An empty username implies the current user. This can be used by the client to expand ~/ type paths locally. byte SSH_FXP_EXTENDED uint32 id string "home-directory" string username This extension is advertised in the SSH_FXP_VERSION hello with version "1". This provides similar information as the "expand-path@openssh.com" extension. This request is identical to the "home-directory" request documented in: https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-extensions-00#section-5 4.12. sftp: Extension request "users-groups-by-id@openssh.com" This request asks the server to return user and/or group names that correspond to one or more IDs (e.g. as returned from a SSH_FXP_STAT request). This may be used by the client to provide usernames in directory listings. byte SSH_FXP_EXTENDED uint32 id string "users-groups-by-id@openssh.com" string uids string gids Where "uids" and "gids" consists of one or more integer user or group identifiers: uint32 id-0 ... The server will reply with a SSH_FXP_EXTENDED_REPLY: byte SSH_FXP_EXTENDED_REPLY string usernames string groupnames Where "username" and "groupnames" consists of names in identical request order to "uids" and "gids" respectively: string name-0 ... If a name cannot be identified for a given user or group ID, an empty string will be returned in its place. It is acceptable for either "uids" or "gids" to be an empty set, in which case the respective "usernames" or "groupnames" list will also be empty. This extension is advertised in the SSH_FXP_VERSION hello with version "1". 5. Miscellaneous changes 5.1 Public key format OpenSSH public keys, as generated by ssh-keygen(1) and appearing in authorized_keys files, are formatted as a single line of text consisting of the public key algorithm name followed by a base64-encoded key blob. The public key blob (before base64 encoding) is the same format used for the encoding of public keys sent on the wire: as described in RFC4253 section 6.6 for RSA and DSA keys, RFC5656 section 3.1 for ECDSA keys and the "New public key formats" section of PROTOCOL.certkeys for the OpenSSH certificate formats. 5.2 Private key format OpenSSH private keys, as generated by ssh-keygen(1) use the format described in PROTOCOL.key by default. As a legacy option, PEM format (RFC7468) private keys are also supported for RSA, DSA and ECDSA keys and were the default format before OpenSSH 7.8. 5.3 KRL format OpenSSH supports a compact format for Key Revocation Lists (KRLs). This format is described in the PROTOCOL.krl file. 5.4 Connection multiplexing OpenSSH's connection multiplexing uses messages as described in PROTOCOL.mux over a Unix domain socket for communications between a master instance and later clients. 5.5. Agent protocol extensions OpenSSH extends the usual agent protocol. These changes are documented in the PROTOCOL.agent file. -$OpenBSD: PROTOCOL,v 1.48 2022/11/07 01:53:01 dtucker Exp $ +$OpenBSD: PROTOCOL,v 1.49 2023/08/28 03:28:43 djm Exp $ diff --git a/PROTOCOL.agent b/PROTOCOL.agent index 44e463674f19..1c4841147a2d 100644 --- a/PROTOCOL.agent +++ b/PROTOCOL.agent @@ -1,84 +1,84 @@ The SSH agent protocol is described in -https://tools.ietf.org/html/draft-miller-ssh-agent-04 +https://tools.ietf.org/html/draft-miller-ssh-agent This file documents OpenSSH's extensions to the agent protocol. 1. session-bind@openssh.com extension This extension allows a ssh client to bind an agent connection to a particular SSH session identifier as derived from the initial key exchange (as per RFC4253 section 7.2) and the host key used for that exchange. This binding is verifiable at the agent by including the initial KEX signature made by the host key. The message format is: byte SSH_AGENTC_EXTENSION (0x1b) string session-bind@openssh.com string hostkey string session identifier string signature bool is_forwarding Where 'hostkey' is the encoded server host public key, 'session identifier' is the exchange hash derived from the initial key exchange, 'signature' is the server's signature of the session identifier using the private hostkey, as sent in the final SSH2_MSG_KEXDH_REPLY/SSH2_MSG_KEXECDH_REPLY message of the initial key exchange. 'is_forwarding' is a flag indicating whether this connection should be bound for user authentication or forwarding. When an agent received this message, it will verify the signature and check the consistency of its contents, including refusing to accept a duplicate session identifier, or any attempt to bind a connection previously bound for authentication. It will then record the binding for the life of the connection for use later in testing per-key destination constraints. 2. restrict-destination-v00@openssh.com key constraint extension The key constraint extension supports destination- and forwarding path- restricted keys. It may be attached as a constraint when keys or smartcard keys are added to an agent. byte SSH_AGENT_CONSTRAIN_EXTENSION (0xff) string restrict-destination-v00@openssh.com constraint[] constraints Where a constraint consists of: string from_username (must be empty) string from_hostname keyspec[] from_hostkeys string to_username string to_hostname keyspec[] to_hostkeys And a keyspec consists of: string keyblob bool is_ca When receiving this message, the agent will ensure that the 'from_username' field is empty, and that 'to_hostname' and 'to_hostkeys' have been supplied (empty 'from_hostname' and 'from_hostkeys' are valid and signify the initial hop from the host running ssh-agent). The agent will then record the constraint against the key. Subsequent operations on this key including add/remove/request identities and, in particular, signature requests will check the key constraints against the session-bind@openssh.com bindings recorded for the agent connection over which they were received. 3. SSH_AGENT_CONSTRAIN_MAXSIGN key constraint This key constraint allows communication to an agent of the maximum number of signatures that may be made with an XMSS key. The format of the constraint is: byte SSH_AGENT_CONSTRAIN_MAXSIGN (0x03) uint32 max_signatures This option is only valid for XMSS keys. -$OpenBSD: PROTOCOL.agent,v 1.19 2023/04/12 08:53:54 jsg Exp $ +$OpenBSD: PROTOCOL.agent,v 1.20 2023/10/03 23:56:10 djm Exp $ diff --git a/README b/README index e44e44ced60b..6e41c8657b82 100644 --- a/README +++ b/README @@ -1,53 +1,53 @@ -See https://www.openssh.com/releasenotes.html#9.4p1 for the release +See https://www.openssh.com/releasenotes.html#9.5p1 for the release notes. Please read https://www.openssh.com/report.html for bug reporting instructions and note that we do not use Github for bug reporting or patch/pull-request management. This is the port of OpenBSD's excellent OpenSSH[0] to Linux and other Unices. OpenSSH is based on the last free version of Tatu Ylonen's sample implementation with all patent-encumbered algorithms removed (to external libraries), all known security bugs fixed, new features reintroduced and many other clean-ups. OpenSSH has been created by Aaron Campbell, Bob Beck, Markus Friedl, Niels Provos, Theo de Raadt, and Dug Song. It has a homepage at https://www.openssh.com/ This port consists of the re-introduction of autoconf support, PAM support, EGD/PRNGD support and replacements for OpenBSD library functions that are (regrettably) absent from other unices. This port has been best tested on AIX, Cygwin, HP-UX, Linux, MacOS/X, FreeBSD, NetBSD, OpenBSD, OpenServer, Solaris and UnixWare. This version actively tracks changes in the OpenBSD CVS repository. The PAM support is now more functional than the popular packages of commercial ssh-1.2.x. It checks "account" and "session" modules for all logins, not just when using password authentication. There is now several mailing lists for this port of OpenSSH. Please refer to https://www.openssh.com/list.html for details on how to join. Please send bug reports and patches to https://bugzilla.mindrot.org or the mailing list openssh-unix-dev@mindrot.org. To mitigate spam, the list only allows posting from subscribed addresses. Code contribution are welcomed, but please follow the OpenBSD style guidelines[1]. Please refer to the INSTALL document for information on dependencies and how to install OpenSSH on your system. Damien Miller Miscellania - This version of OpenSSH is based upon code retrieved from the OpenBSD CVS repository which in turn was based on the last free sample implementation released by Tatu Ylonen. References - [0] https://www.openssh.com/ [1] https://man.openbsd.org/style.9 diff --git a/auth2.c b/auth2.c index 34346e5731de..c628999e0bb6 100644 --- a/auth2.c +++ b/auth2.c @@ -1,844 +1,851 @@ -/* $OpenBSD: auth2.c,v 1.166 2023/03/08 04:43:12 guenther Exp $ */ +/* $OpenBSD: auth2.c,v 1.167 2023/08/28 09:48:11 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" #include #include #include #include #include #include #include #include #include #include #include "stdlib.h" #include "atomicio.h" #include "xmalloc.h" #include "ssh2.h" #include "packet.h" #include "log.h" #include "sshbuf.h" #include "misc.h" #include "servconf.h" #include "sshkey.h" #include "hostfile.h" #include "auth.h" #include "dispatch.h" #include "pathnames.h" #include "ssherr.h" #ifdef GSSAPI #include "ssh-gss.h" #endif #include "monitor_wrap.h" #include "digest.h" /* import */ extern ServerOptions options; extern struct sshbuf *loginmsg; /* methods */ extern Authmethod method_none; extern Authmethod method_pubkey; extern Authmethod method_passwd; extern Authmethod method_kbdint; extern Authmethod method_hostbased; #ifdef GSSAPI extern Authmethod method_gssapi; #endif Authmethod *authmethods[] = { &method_none, &method_pubkey, #ifdef GSSAPI &method_gssapi, #endif &method_passwd, &method_kbdint, &method_hostbased, NULL }; /* protocol */ static int input_service_request(int, u_int32_t, struct ssh *); static int input_userauth_request(int, u_int32_t, struct ssh *); /* helper */ static Authmethod *authmethod_byname(const char *); static Authmethod *authmethod_lookup(Authctxt *, const char *); static char *authmethods_get(Authctxt *authctxt); #define MATCH_NONE 0 /* method or submethod mismatch */ #define MATCH_METHOD 1 /* method matches (no submethod specified) */ #define MATCH_BOTH 2 /* method and submethod match */ #define MATCH_PARTIAL 3 /* method matches, submethod can't be checked */ static int list_starts_with(const char *, const char *, const char *); char * auth2_read_banner(void) { struct stat st; char *banner = NULL; size_t len, n; int fd; if ((fd = open(options.banner, O_RDONLY)) == -1) return (NULL); if (fstat(fd, &st) == -1) { close(fd); return (NULL); } if (st.st_size <= 0 || st.st_size > 1*1024*1024) { close(fd); return (NULL); } len = (size_t)st.st_size; /* truncate */ banner = xmalloc(len + 1); n = atomicio(read, fd, banner, len); close(fd); if (n != len) { free(banner); return (NULL); } banner[n] = '\0'; return (banner); } static void userauth_send_banner(struct ssh *ssh, const char *msg) { int r; if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_BANNER)) != 0 || (r = sshpkt_put_cstring(ssh, msg)) != 0 || (r = sshpkt_put_cstring(ssh, "")) != 0 || /* language, unused */ (r = sshpkt_send(ssh)) != 0) fatal_fr(r, "send packet"); debug("%s: sent", __func__); } static void userauth_banner(struct ssh *ssh) { char *banner = NULL; if (options.banner == NULL) return; if ((banner = PRIVSEP(auth2_read_banner())) == NULL) goto done; userauth_send_banner(ssh, banner); done: free(banner); } /* * loop until authctxt->success == TRUE */ void do_authentication2(struct ssh *ssh) { Authctxt *authctxt = ssh->authctxt; ssh_dispatch_init(ssh, &dispatch_protocol_error); ssh_dispatch_set(ssh, SSH2_MSG_SERVICE_REQUEST, &input_service_request); ssh_dispatch_run_fatal(ssh, DISPATCH_BLOCK, &authctxt->success); ssh->authctxt = NULL; } static int input_service_request(int type, u_int32_t seq, struct ssh *ssh) { Authctxt *authctxt = ssh->authctxt; char *service = NULL; int r, acceptit = 0; if ((r = sshpkt_get_cstring(ssh, &service, NULL)) != 0 || (r = sshpkt_get_end(ssh)) != 0) goto out; if (authctxt == NULL) fatal("input_service_request: no authctxt"); if (strcmp(service, "ssh-userauth") == 0) { if (!authctxt->success) { acceptit = 1; /* now we can handle user-auth requests */ ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_REQUEST, &input_userauth_request); } } /* XXX all other service requests are denied */ if (acceptit) { if ((r = sshpkt_start(ssh, SSH2_MSG_SERVICE_ACCEPT)) != 0 || (r = sshpkt_put_cstring(ssh, service)) != 0 || (r = sshpkt_send(ssh)) != 0 || (r = ssh_packet_write_wait(ssh)) != 0) goto out; } else { debug("bad service request %s", service); ssh_packet_disconnect(ssh, "bad service request %s", service); } r = 0; out: free(service); return r; } #define MIN_FAIL_DELAY_SECONDS 0.005 +#define MAX_FAIL_DELAY_SECONDS 5.0 static double user_specific_delay(const char *user) { char b[512]; size_t len = ssh_digest_bytes(SSH_DIGEST_SHA512); u_char *hash = xmalloc(len); double delay; (void)snprintf(b, sizeof b, "%llu%s", (unsigned long long)options.timing_secret, user); if (ssh_digest_memory(SSH_DIGEST_SHA512, b, strlen(b), hash, len) != 0) fatal_f("ssh_digest_memory"); /* 0-4.2 ms of delay */ delay = (double)PEEK_U32(hash) / 1000 / 1000 / 1000 / 1000; freezero(hash, len); debug3_f("user specific delay %0.3lfms", delay/1000); return MIN_FAIL_DELAY_SECONDS + delay; } static void ensure_minimum_time_since(double start, double seconds) { struct timespec ts; double elapsed = monotime_double() - start, req = seconds, remain; + if (elapsed > MAX_FAIL_DELAY_SECONDS) { + debug3_f("elapsed %0.3lfms exceeded the max delay " + "requested %0.3lfms)", elapsed*1000, req*1000); + return; + } + /* if we've already passed the requested time, scale up */ while ((remain = seconds - elapsed) < 0.0) seconds *= 2; ts.tv_sec = remain; ts.tv_nsec = (remain - ts.tv_sec) * 1000000000; debug3_f("elapsed %0.3lfms, delaying %0.3lfms (requested %0.3lfms)", elapsed*1000, remain*1000, req*1000); nanosleep(&ts, NULL); } static int input_userauth_request(int type, u_int32_t seq, struct ssh *ssh) { Authctxt *authctxt = ssh->authctxt; Authmethod *m = NULL; char *user = NULL, *service = NULL, *method = NULL, *style = NULL; int r, authenticated = 0; double tstart = monotime_double(); if (authctxt == NULL) fatal("input_userauth_request: no authctxt"); if ((r = sshpkt_get_cstring(ssh, &user, NULL)) != 0 || (r = sshpkt_get_cstring(ssh, &service, NULL)) != 0 || (r = sshpkt_get_cstring(ssh, &method, NULL)) != 0) goto out; debug("userauth-request for user %s service %s method %s", user, service, method); debug("attempt %d failures %d", authctxt->attempt, authctxt->failures); if ((style = strchr(user, ':')) != NULL) *style++ = 0; if (authctxt->attempt >= 1024) auth_maxtries_exceeded(ssh); if (authctxt->attempt++ == 0) { /* setup auth context */ authctxt->pw = PRIVSEP(getpwnamallow(ssh, user)); authctxt->user = xstrdup(user); if (authctxt->pw && strcmp(service, "ssh-connection")==0) { authctxt->valid = 1; debug2_f("setting up authctxt for %s", user); } else { authctxt->valid = 0; /* Invalid user, fake password information */ authctxt->pw = fakepw(); #ifdef SSH_AUDIT_EVENTS PRIVSEP(audit_event(ssh, SSH_INVALID_USER)); #endif } #ifdef USE_PAM if (options.use_pam) PRIVSEP(start_pam(ssh)); #endif ssh_packet_set_log_preamble(ssh, "%suser %s", authctxt->valid ? "authenticating " : "invalid ", user); setproctitle("%s%s", authctxt->valid ? user : "unknown", use_privsep ? " [net]" : ""); authctxt->service = xstrdup(service); authctxt->style = style ? xstrdup(style) : NULL; if (use_privsep) mm_inform_authserv(service, style); userauth_banner(ssh); if (auth2_setup_methods_lists(authctxt) != 0) ssh_packet_disconnect(ssh, "no authentication methods enabled"); } else if (strcmp(user, authctxt->user) != 0 || strcmp(service, authctxt->service) != 0) { ssh_packet_disconnect(ssh, "Change of username or service " "not allowed: (%s,%s) -> (%s,%s)", authctxt->user, authctxt->service, user, service); } /* reset state */ auth2_challenge_stop(ssh); #ifdef GSSAPI /* XXX move to auth2_gssapi_stop() */ ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL); #endif auth2_authctxt_reset_info(authctxt); authctxt->postponed = 0; authctxt->server_caused_failure = 0; /* try to authenticate user */ m = authmethod_lookup(authctxt, method); if (m != NULL && authctxt->failures < options.max_authtries) { debug2("input_userauth_request: try method %s", method); authenticated = m->userauth(ssh, method); } - if (!authctxt->authenticated) + if (!authctxt->authenticated && strcmp(method, "none") != 0) ensure_minimum_time_since(tstart, user_specific_delay(authctxt->user)); userauth_finish(ssh, authenticated, method, NULL); r = 0; out: free(service); free(user); free(method); return r; } void userauth_finish(struct ssh *ssh, int authenticated, const char *packet_method, const char *submethod) { Authctxt *authctxt = ssh->authctxt; Authmethod *m = NULL; const char *method = packet_method; char *methods; int r, partial = 0; if (authenticated) { if (!authctxt->valid) { fatal("INTERNAL ERROR: authenticated invalid user %s", authctxt->user); } if (authctxt->postponed) fatal("INTERNAL ERROR: authenticated and postponed"); /* prefer primary authmethod name to possible synonym */ if ((m = authmethod_byname(method)) == NULL) fatal("INTERNAL ERROR: bad method %s", method); method = m->name; } /* Special handling for root */ if (authenticated && authctxt->pw->pw_uid == 0 && !auth_root_allowed(ssh, method)) { authenticated = 0; #ifdef SSH_AUDIT_EVENTS PRIVSEP(audit_event(ssh, SSH_LOGIN_ROOT_DENIED)); #endif } if (authenticated && options.num_auth_methods != 0) { if (!auth2_update_methods_lists(authctxt, method, submethod)) { authenticated = 0; partial = 1; } } /* Log before sending the reply */ auth_log(ssh, authenticated, partial, method, submethod); /* Update information exposed to session */ if (authenticated || partial) auth2_update_session_info(authctxt, method, submethod); if (authctxt->postponed) return; #ifdef USE_PAM if (options.use_pam && authenticated) { int r, success = PRIVSEP(do_pam_account()); /* If PAM returned a message, send it to the user. */ if (sshbuf_len(loginmsg) > 0) { if ((r = sshbuf_put(loginmsg, "\0", 1)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); userauth_send_banner(ssh, sshbuf_ptr(loginmsg)); if ((r = ssh_packet_write_wait(ssh)) != 0) { sshpkt_fatal(ssh, r, "%s: send PAM banner", __func__); } } if (!success) { fatal("Access denied for user %s by PAM account " "configuration", authctxt->user); } } #endif if (authenticated == 1) { /* turn off userauth */ ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_REQUEST, &dispatch_protocol_ignore); if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_SUCCESS)) != 0 || (r = sshpkt_send(ssh)) != 0 || (r = ssh_packet_write_wait(ssh)) != 0) fatal_fr(r, "send success packet"); /* now we can break out */ authctxt->success = 1; ssh_packet_set_log_preamble(ssh, "user %s", authctxt->user); } else { /* Allow initial try of "none" auth without failure penalty */ if (!partial && !authctxt->server_caused_failure && (authctxt->attempt > 1 || strcmp(method, "none") != 0)) authctxt->failures++; if (authctxt->failures >= options.max_authtries) { #ifdef SSH_AUDIT_EVENTS PRIVSEP(audit_event(ssh, SSH_LOGIN_EXCEED_MAXTRIES)); #endif auth_maxtries_exceeded(ssh); } methods = authmethods_get(authctxt); debug3_f("failure partial=%d next methods=\"%s\"", partial, methods); if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_FAILURE)) != 0 || (r = sshpkt_put_cstring(ssh, methods)) != 0 || (r = sshpkt_put_u8(ssh, partial)) != 0 || (r = sshpkt_send(ssh)) != 0 || (r = ssh_packet_write_wait(ssh)) != 0) fatal_fr(r, "send failure packet"); free(methods); } } /* * Checks whether method is allowed by at least one AuthenticationMethods * methods list. Returns 1 if allowed, or no methods lists configured. * 0 otherwise. */ int auth2_method_allowed(Authctxt *authctxt, const char *method, const char *submethod) { u_int i; /* * NB. authctxt->num_auth_methods might be zero as a result of * auth2_setup_methods_lists(), so check the configuration. */ if (options.num_auth_methods == 0) return 1; for (i = 0; i < authctxt->num_auth_methods; i++) { if (list_starts_with(authctxt->auth_methods[i], method, submethod) != MATCH_NONE) return 1; } return 0; } static char * authmethods_get(Authctxt *authctxt) { struct sshbuf *b; char *list; int i, r; if ((b = sshbuf_new()) == NULL) fatal_f("sshbuf_new failed"); for (i = 0; authmethods[i] != NULL; i++) { if (strcmp(authmethods[i]->name, "none") == 0) continue; if (authmethods[i]->enabled == NULL || *(authmethods[i]->enabled) == 0) continue; if (!auth2_method_allowed(authctxt, authmethods[i]->name, NULL)) continue; if ((r = sshbuf_putf(b, "%s%s", sshbuf_len(b) ? "," : "", authmethods[i]->name)) != 0) fatal_fr(r, "buffer error"); } if ((list = sshbuf_dup_string(b)) == NULL) fatal_f("sshbuf_dup_string failed"); sshbuf_free(b); return list; } static Authmethod * authmethod_byname(const char *name) { int i; if (name == NULL) fatal_f("NULL authentication method name"); for (i = 0; authmethods[i] != NULL; i++) { if (strcmp(name, authmethods[i]->name) == 0 || (authmethods[i]->synonym != NULL && strcmp(name, authmethods[i]->synonym) == 0)) return authmethods[i]; } debug_f("unrecognized authentication method name: %s", name); return NULL; } static Authmethod * authmethod_lookup(Authctxt *authctxt, const char *name) { Authmethod *method; if ((method = authmethod_byname(name)) == NULL) return NULL; if (method->enabled == NULL || *(method->enabled) == 0) { debug3_f("method %s not enabled", name); return NULL; } if (!auth2_method_allowed(authctxt, method->name, NULL)) { debug3_f("method %s not allowed " "by AuthenticationMethods", name); return NULL; } return method; } /* * Check a comma-separated list of methods for validity. Is need_enable is * non-zero, then also require that the methods are enabled. * Returns 0 on success or -1 if the methods list is invalid. */ int auth2_methods_valid(const char *_methods, int need_enable) { char *methods, *omethods, *method, *p; u_int i, found; int ret = -1; if (*_methods == '\0') { error("empty authentication method list"); return -1; } omethods = methods = xstrdup(_methods); while ((method = strsep(&methods, ",")) != NULL) { for (found = i = 0; !found && authmethods[i] != NULL; i++) { if ((p = strchr(method, ':')) != NULL) *p = '\0'; if (strcmp(method, authmethods[i]->name) != 0) continue; if (need_enable) { if (authmethods[i]->enabled == NULL || *(authmethods[i]->enabled) == 0) { error("Disabled method \"%s\" in " "AuthenticationMethods list \"%s\"", method, _methods); goto out; } } found = 1; break; } if (!found) { error("Unknown authentication method \"%s\" in list", method); goto out; } } ret = 0; out: free(omethods); return ret; } /* * Prune the AuthenticationMethods supplied in the configuration, removing * any methods lists that include disabled methods. Note that this might * leave authctxt->num_auth_methods == 0, even when multiple required auth * has been requested. For this reason, all tests for whether multiple is * enabled should consult options.num_auth_methods directly. */ int auth2_setup_methods_lists(Authctxt *authctxt) { u_int i; /* First, normalise away the "any" pseudo-method */ if (options.num_auth_methods == 1 && strcmp(options.auth_methods[0], "any") == 0) { free(options.auth_methods[0]); options.auth_methods[0] = NULL; options.num_auth_methods = 0; } if (options.num_auth_methods == 0) return 0; debug3_f("checking methods"); authctxt->auth_methods = xcalloc(options.num_auth_methods, sizeof(*authctxt->auth_methods)); authctxt->num_auth_methods = 0; for (i = 0; i < options.num_auth_methods; i++) { if (auth2_methods_valid(options.auth_methods[i], 1) != 0) { logit("Authentication methods list \"%s\" contains " "disabled method, skipping", options.auth_methods[i]); continue; } debug("authentication methods list %d: %s", authctxt->num_auth_methods, options.auth_methods[i]); authctxt->auth_methods[authctxt->num_auth_methods++] = xstrdup(options.auth_methods[i]); } if (authctxt->num_auth_methods == 0) { error("No AuthenticationMethods left after eliminating " "disabled methods"); return -1; } return 0; } static int list_starts_with(const char *methods, const char *method, const char *submethod) { size_t l = strlen(method); int match; const char *p; if (strncmp(methods, method, l) != 0) return MATCH_NONE; p = methods + l; match = MATCH_METHOD; if (*p == ':') { if (!submethod) return MATCH_PARTIAL; l = strlen(submethod); p += 1; if (strncmp(submethod, p, l)) return MATCH_NONE; p += l; match = MATCH_BOTH; } if (*p != ',' && *p != '\0') return MATCH_NONE; return match; } /* * Remove method from the start of a comma-separated list of methods. * Returns 0 if the list of methods did not start with that method or 1 * if it did. */ static int remove_method(char **methods, const char *method, const char *submethod) { char *omethods = *methods, *p; size_t l = strlen(method); int match; match = list_starts_with(omethods, method, submethod); if (match != MATCH_METHOD && match != MATCH_BOTH) return 0; p = omethods + l; if (submethod && match == MATCH_BOTH) p += 1 + strlen(submethod); /* include colon */ if (*p == ',') p++; *methods = xstrdup(p); free(omethods); return 1; } /* * Called after successful authentication. Will remove the successful method * from the start of each list in which it occurs. If it was the last method * in any list, then authentication is deemed successful. * Returns 1 if the method completed any authentication list or 0 otherwise. */ int auth2_update_methods_lists(Authctxt *authctxt, const char *method, const char *submethod) { u_int i, found = 0; debug3_f("updating methods list after \"%s\"", method); for (i = 0; i < authctxt->num_auth_methods; i++) { if (!remove_method(&(authctxt->auth_methods[i]), method, submethod)) continue; found = 1; if (*authctxt->auth_methods[i] == '\0') { debug2("authentication methods list %d complete", i); return 1; } debug3("authentication methods list %d remaining: \"%s\"", i, authctxt->auth_methods[i]); } /* This should not happen, but would be bad if it did */ if (!found) fatal_f("method not in AuthenticationMethods"); return 0; } /* Reset method-specific information */ void auth2_authctxt_reset_info(Authctxt *authctxt) { sshkey_free(authctxt->auth_method_key); free(authctxt->auth_method_info); authctxt->auth_method_key = NULL; authctxt->auth_method_info = NULL; } /* Record auth method-specific information for logs */ void auth2_record_info(Authctxt *authctxt, const char *fmt, ...) { va_list ap; int i; free(authctxt->auth_method_info); authctxt->auth_method_info = NULL; va_start(ap, fmt); i = vasprintf(&authctxt->auth_method_info, fmt, ap); va_end(ap); if (i == -1) fatal_f("vasprintf failed"); } /* * Records a public key used in authentication. This is used for logging * and to ensure that the same key is not subsequently accepted again for * multiple authentication. */ void auth2_record_key(Authctxt *authctxt, int authenticated, const struct sshkey *key) { struct sshkey **tmp, *dup; int r; if ((r = sshkey_from_private(key, &dup)) != 0) fatal_fr(r, "copy key"); sshkey_free(authctxt->auth_method_key); authctxt->auth_method_key = dup; if (!authenticated) return; /* If authenticated, make sure we don't accept this key again */ if ((r = sshkey_from_private(key, &dup)) != 0) fatal_fr(r, "copy key"); if (authctxt->nprev_keys >= INT_MAX || (tmp = recallocarray(authctxt->prev_keys, authctxt->nprev_keys, authctxt->nprev_keys + 1, sizeof(*authctxt->prev_keys))) == NULL) fatal_f("reallocarray failed"); authctxt->prev_keys = tmp; authctxt->prev_keys[authctxt->nprev_keys] = dup; authctxt->nprev_keys++; } /* Checks whether a key has already been previously used for authentication */ int auth2_key_already_used(Authctxt *authctxt, const struct sshkey *key) { u_int i; char *fp; for (i = 0; i < authctxt->nprev_keys; i++) { if (sshkey_equal_public(key, authctxt->prev_keys[i])) { fp = sshkey_fingerprint(authctxt->prev_keys[i], options.fingerprint_hash, SSH_FP_DEFAULT); debug3_f("key already used: %s %s", sshkey_type(authctxt->prev_keys[i]), fp == NULL ? "UNKNOWN" : fp); free(fp); return 1; } } return 0; } /* * Updates authctxt->session_info with details of authentication. Should be * whenever an authentication method succeeds. */ void auth2_update_session_info(Authctxt *authctxt, const char *method, const char *submethod) { int r; if (authctxt->session_info == NULL) { if ((authctxt->session_info = sshbuf_new()) == NULL) fatal_f("sshbuf_new"); } /* Append method[/submethod] */ if ((r = sshbuf_putf(authctxt->session_info, "%s%s%s", method, submethod == NULL ? "" : "/", submethod == NULL ? "" : submethod)) != 0) fatal_fr(r, "append method"); /* Append key if present */ if (authctxt->auth_method_key != NULL) { if ((r = sshbuf_put_u8(authctxt->session_info, ' ')) != 0 || (r = sshkey_format_text(authctxt->auth_method_key, authctxt->session_info)) != 0) fatal_fr(r, "append key"); } if (authctxt->auth_method_info != NULL) { /* Ensure no ambiguity here */ if (strchr(authctxt->auth_method_info, '\n') != NULL) fatal_f("auth_method_info contains \\n"); if ((r = sshbuf_put_u8(authctxt->session_info, ' ')) != 0 || (r = sshbuf_putf(authctxt->session_info, "%s", authctxt->auth_method_info)) != 0) { fatal_fr(r, "append method info"); } } if ((r = sshbuf_put_u8(authctxt->session_info, '\n')) != 0) fatal_fr(r, "append"); } diff --git a/channels.c b/channels.c index da66b7b3494c..598ff322a175 100644 --- a/channels.c +++ b/channels.c @@ -1,5282 +1,5291 @@ -/* $OpenBSD: channels.c,v 1.432 2023/07/04 03:59:21 dlg Exp $ */ +/* $OpenBSD: channels.c,v 1.433 2023/09/04 00:01:46 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * This file contains functions for generic socket connection forwarding. * There is also code for initiating connection forwarding for X11 connections, * arbitrary tcp/ip connections, and the authentication agent connection. * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". * * SSH2 support added by Markus Friedl. * Copyright (c) 1999, 2000, 2001, 2002 Markus Friedl. All rights reserved. * Copyright (c) 1999 Dug Song. All rights reserved. * Copyright (c) 1999 Theo de Raadt. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" #include #include #include #include #include #ifdef HAVE_SYS_TIME_H # include #endif #include #include #include #include #include #include #ifdef HAVE_POLL_H #include #endif #include #ifdef HAVE_STDINT_H # include #endif #include #include #include #include #include #include "openbsd-compat/sys-queue.h" #include "xmalloc.h" #include "ssh.h" #include "ssh2.h" #include "ssherr.h" #include "sshbuf.h" #include "packet.h" #include "log.h" #include "misc.h" #include "channels.h" #include "compat.h" #include "canohost.h" #include "sshkey.h" #include "authfd.h" #include "pathnames.h" #include "match.h" /* XXX remove once we're satisfied there's no lurking bugs */ /* #define DEBUG_CHANNEL_POLL 1 */ /* -- agent forwarding */ #define NUM_SOCKS 10 /* -- tcp forwarding */ /* special-case port number meaning allow any port */ #define FWD_PERMIT_ANY_PORT 0 /* special-case wildcard meaning allow any host */ #define FWD_PERMIT_ANY_HOST "*" /* -- X11 forwarding */ /* Maximum number of fake X11 displays to try. */ #define MAX_DISPLAYS 1000 /* Per-channel callback for pre/post IO actions */ typedef void chan_fn(struct ssh *, Channel *c); /* * Data structure for storing which hosts are permitted for forward requests. * The local sides of any remote forwards are stored in this array to prevent * a corrupt remote server from accessing arbitrary TCP/IP ports on our local * network (which might be behind a firewall). */ /* XXX: streamlocal wants a path instead of host:port */ /* Overload host_to_connect; we could just make this match Forward */ /* XXX - can we use listen_host instead of listen_path? */ struct permission { char *host_to_connect; /* Connect to 'host'. */ int port_to_connect; /* Connect to 'port'. */ char *listen_host; /* Remote side should listen address. */ char *listen_path; /* Remote side should listen path. */ int listen_port; /* Remote side should listen port. */ Channel *downstream; /* Downstream mux*/ }; /* * Stores the forwarding permission state for a single direction (local or * remote). */ struct permission_set { /* * List of all local permitted host/port pairs to allow for the * user. */ u_int num_permitted_user; struct permission *permitted_user; /* * List of all permitted host/port pairs to allow for the admin. */ u_int num_permitted_admin; struct permission *permitted_admin; /* * If this is true, all opens/listens are permitted. This is the * case on the server on which we have to trust the client anyway, * and the user could do anything after logging in. */ int all_permitted; }; /* Used to record timeouts per channel type */ struct ssh_channel_timeout { char *type_pattern; int timeout_secs; }; /* Master structure for channels state */ struct ssh_channels { /* * Pointer to an array containing all allocated channels. The array * is dynamically extended as needed. */ Channel **channels; /* * Size of the channel array. All slots of the array must always be * initialized (at least the type field); unused slots set to NULL */ u_int channels_alloc; /* * 'channel_pre*' are called just before IO to add any bits * relevant to channels in the c->io_want bitmasks. * * 'channel_post*': perform any appropriate operations for * channels which have c->io_ready events pending. */ chan_fn **channel_pre; chan_fn **channel_post; /* -- tcp forwarding */ struct permission_set local_perms; struct permission_set remote_perms; /* -- X11 forwarding */ /* Saved X11 local (client) display. */ char *x11_saved_display; /* Saved X11 authentication protocol name. */ char *x11_saved_proto; /* Saved X11 authentication data. This is the real data. */ char *x11_saved_data; u_int x11_saved_data_len; /* Deadline after which all X11 connections are refused */ time_t x11_refuse_time; /* * Fake X11 authentication data. This is what the server will be * sending us; we should replace any occurrences of this by the * real data. */ u_char *x11_fake_data; u_int x11_fake_data_len; /* AF_UNSPEC or AF_INET or AF_INET6 */ int IPv4or6; /* Channel timeouts by type */ struct ssh_channel_timeout *timeouts; size_t ntimeouts; }; /* helper */ static void port_open_helper(struct ssh *ssh, Channel *c, char *rtype); static const char *channel_rfwd_bind_host(const char *listen_host); /* non-blocking connect helpers */ static int connect_next(struct channel_connect *); static void channel_connect_ctx_free(struct channel_connect *); static Channel *rdynamic_connect_prepare(struct ssh *, char *, char *); static int rdynamic_connect_finish(struct ssh *, Channel *); /* Setup helper */ static void channel_handler_init(struct ssh_channels *sc); /* -- channel core */ void channel_init_channels(struct ssh *ssh) { struct ssh_channels *sc; if ((sc = calloc(1, sizeof(*sc))) == NULL) fatal_f("allocation failed"); sc->channels_alloc = 10; sc->channels = xcalloc(sc->channels_alloc, sizeof(*sc->channels)); sc->IPv4or6 = AF_UNSPEC; channel_handler_init(sc); ssh->chanctxt = sc; } Channel * channel_by_id(struct ssh *ssh, int id) { Channel *c; if (id < 0 || (u_int)id >= ssh->chanctxt->channels_alloc) { logit_f("%d: bad id", id); return NULL; } c = ssh->chanctxt->channels[id]; if (c == NULL) { logit_f("%d: bad id: channel free", id); return NULL; } return c; } Channel * channel_by_remote_id(struct ssh *ssh, u_int remote_id) { Channel *c; u_int i; for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { c = ssh->chanctxt->channels[i]; if (c != NULL && c->have_remote_id && c->remote_id == remote_id) return c; } return NULL; } /* * Returns the channel if it is allowed to receive protocol messages. * Private channels, like listening sockets, may not receive messages. */ Channel * channel_lookup(struct ssh *ssh, int id) { Channel *c; if ((c = channel_by_id(ssh, id)) == NULL) return NULL; switch (c->type) { case SSH_CHANNEL_X11_OPEN: case SSH_CHANNEL_LARVAL: case SSH_CHANNEL_CONNECTING: case SSH_CHANNEL_DYNAMIC: case SSH_CHANNEL_RDYNAMIC_OPEN: case SSH_CHANNEL_RDYNAMIC_FINISH: case SSH_CHANNEL_OPENING: case SSH_CHANNEL_OPEN: case SSH_CHANNEL_ABANDONED: case SSH_CHANNEL_MUX_PROXY: return c; } logit("Non-public channel %d, type %d.", id, c->type); return NULL; } /* * Add a timeout for open channels whose c->ctype (or c->xctype if it is set) * match type_pattern. */ void channel_add_timeout(struct ssh *ssh, const char *type_pattern, int timeout_secs) { struct ssh_channels *sc = ssh->chanctxt; debug2_f("channel type \"%s\" timeout %d seconds", type_pattern, timeout_secs); sc->timeouts = xrecallocarray(sc->timeouts, sc->ntimeouts, sc->ntimeouts + 1, sizeof(*sc->timeouts)); sc->timeouts[sc->ntimeouts].type_pattern = xstrdup(type_pattern); sc->timeouts[sc->ntimeouts].timeout_secs = timeout_secs; sc->ntimeouts++; } /* Clears all previously-added channel timeouts */ void channel_clear_timeouts(struct ssh *ssh) { struct ssh_channels *sc = ssh->chanctxt; size_t i; debug3_f("clearing"); for (i = 0; i < sc->ntimeouts; i++) free(sc->timeouts[i].type_pattern); free(sc->timeouts); sc->timeouts = NULL; sc->ntimeouts = 0; } static int lookup_timeout(struct ssh *ssh, const char *type) { struct ssh_channels *sc = ssh->chanctxt; size_t i; for (i = 0; i < sc->ntimeouts; i++) { if (match_pattern(type, sc->timeouts[i].type_pattern)) return sc->timeouts[i].timeout_secs; } return 0; } /* * Sets "extended type" of a channel; used by session layer to add additional * information about channel types (e.g. shell, login, subsystem) that can then * be used to select timeouts. * Will reset c->inactive_deadline as a side-effect. */ void channel_set_xtype(struct ssh *ssh, int id, const char *xctype) { Channel *c; if ((c = channel_by_id(ssh, id)) == NULL) fatal_f("missing channel %d", id); if (c->xctype != NULL) free(c->xctype); c->xctype = xstrdup(xctype); /* Type has changed, so look up inactivity deadline again */ c->inactive_deadline = lookup_timeout(ssh, c->xctype); debug2_f("labeled channel %d as %s (inactive timeout %u)", id, xctype, c->inactive_deadline); } /* * Register filedescriptors for a channel, used when allocating a channel or * when the channel consumer/producer is ready, e.g. shell exec'd */ static void channel_register_fds(struct ssh *ssh, Channel *c, int rfd, int wfd, int efd, int extusage, int nonblock, int is_tty) { int val; if (rfd != -1) (void)fcntl(rfd, F_SETFD, FD_CLOEXEC); if (wfd != -1 && wfd != rfd) (void)fcntl(wfd, F_SETFD, FD_CLOEXEC); if (efd != -1 && efd != rfd && efd != wfd) (void)fcntl(efd, F_SETFD, FD_CLOEXEC); c->rfd = rfd; c->wfd = wfd; c->sock = (rfd == wfd) ? rfd : -1; c->efd = efd; c->extended_usage = extusage; if ((c->isatty = is_tty) != 0) debug2("channel %d: rfd %d isatty", c->self, c->rfd); #ifdef _AIX /* XXX: Later AIX versions can't push as much data to tty */ c->wfd_isatty = is_tty || isatty(c->wfd); #endif /* enable nonblocking mode */ c->restore_block = 0; if (nonblock == CHANNEL_NONBLOCK_STDIO) { /* * Special handling for stdio file descriptors: do not set * non-blocking mode if they are TTYs. Otherwise prepare to * restore their blocking state on exit to avoid interfering * with other programs that follow. */ if (rfd != -1 && !isatty(rfd) && (val = fcntl(rfd, F_GETFL)) != -1 && !(val & O_NONBLOCK)) { c->restore_flags[0] = val; c->restore_block |= CHANNEL_RESTORE_RFD; set_nonblock(rfd); } if (wfd != -1 && !isatty(wfd) && (val = fcntl(wfd, F_GETFL)) != -1 && !(val & O_NONBLOCK)) { c->restore_flags[1] = val; c->restore_block |= CHANNEL_RESTORE_WFD; set_nonblock(wfd); } if (efd != -1 && !isatty(efd) && (val = fcntl(efd, F_GETFL)) != -1 && !(val & O_NONBLOCK)) { c->restore_flags[2] = val; c->restore_block |= CHANNEL_RESTORE_EFD; set_nonblock(efd); } } else if (nonblock) { if (rfd != -1) set_nonblock(rfd); if (wfd != -1) set_nonblock(wfd); if (efd != -1) set_nonblock(efd); } } /* * Allocate a new channel object and set its type and socket. */ Channel * channel_new(struct ssh *ssh, char *ctype, int type, int rfd, int wfd, int efd, u_int window, u_int maxpack, int extusage, const char *remote_name, int nonblock) { struct ssh_channels *sc = ssh->chanctxt; u_int i, found = 0; Channel *c; int r; /* Try to find a free slot where to put the new channel. */ for (i = 0; i < sc->channels_alloc; i++) { if (sc->channels[i] == NULL) { /* Found a free slot. */ found = i; break; } } if (i >= sc->channels_alloc) { /* * There are no free slots. Take last+1 slot and expand * the array. */ found = sc->channels_alloc; if (sc->channels_alloc > CHANNELS_MAX_CHANNELS) fatal_f("internal error: channels_alloc %d too big", sc->channels_alloc); sc->channels = xrecallocarray(sc->channels, sc->channels_alloc, sc->channels_alloc + 10, sizeof(*sc->channels)); sc->channels_alloc += 10; debug2("channel: expanding %d", sc->channels_alloc); } /* Initialize and return new channel. */ c = sc->channels[found] = xcalloc(1, sizeof(Channel)); if ((c->input = sshbuf_new()) == NULL || (c->output = sshbuf_new()) == NULL || (c->extended = sshbuf_new()) == NULL) fatal_f("sshbuf_new failed"); if ((r = sshbuf_set_max_size(c->input, CHAN_INPUT_MAX)) != 0) fatal_fr(r, "sshbuf_set_max_size"); c->ostate = CHAN_OUTPUT_OPEN; c->istate = CHAN_INPUT_OPEN; channel_register_fds(ssh, c, rfd, wfd, efd, extusage, nonblock, 0); c->self = found; c->type = type; c->ctype = ctype; c->local_window = window; c->local_window_max = window; c->local_maxpacket = maxpack; c->remote_name = xstrdup(remote_name); c->ctl_chan = -1; c->delayed = 1; /* prevent call to channel_post handler */ c->inactive_deadline = lookup_timeout(ssh, c->ctype); TAILQ_INIT(&c->status_confirms); debug("channel %d: new %s [%s] (inactive timeout: %u)", found, c->ctype, remote_name, c->inactive_deadline); return c; } int channel_close_fd(struct ssh *ssh, Channel *c, int *fdp) { int ret, fd = *fdp; if (fd == -1) return 0; /* restore blocking */ if (*fdp == c->rfd && (c->restore_block & CHANNEL_RESTORE_RFD) != 0) (void)fcntl(*fdp, F_SETFL, c->restore_flags[0]); else if (*fdp == c->wfd && (c->restore_block & CHANNEL_RESTORE_WFD) != 0) (void)fcntl(*fdp, F_SETFL, c->restore_flags[1]); else if (*fdp == c->efd && (c->restore_block & CHANNEL_RESTORE_EFD) != 0) (void)fcntl(*fdp, F_SETFL, c->restore_flags[2]); if (*fdp == c->rfd) { c->io_want &= ~SSH_CHAN_IO_RFD; c->io_ready &= ~SSH_CHAN_IO_RFD; c->rfd = -1; c->pfds[0] = -1; } if (*fdp == c->wfd) { c->io_want &= ~SSH_CHAN_IO_WFD; c->io_ready &= ~SSH_CHAN_IO_WFD; c->wfd = -1; c->pfds[1] = -1; } if (*fdp == c->efd) { c->io_want &= ~SSH_CHAN_IO_EFD; c->io_ready &= ~SSH_CHAN_IO_EFD; c->efd = -1; c->pfds[2] = -1; } if (*fdp == c->sock) { c->io_want &= ~SSH_CHAN_IO_SOCK; c->io_ready &= ~SSH_CHAN_IO_SOCK; c->sock = -1; c->pfds[3] = -1; } ret = close(fd); *fdp = -1; /* probably redundant */ return ret; } /* Close all channel fd/socket. */ static void channel_close_fds(struct ssh *ssh, Channel *c) { int sock = c->sock, rfd = c->rfd, wfd = c->wfd, efd = c->efd; channel_close_fd(ssh, c, &c->sock); if (rfd != sock) channel_close_fd(ssh, c, &c->rfd); if (wfd != sock && wfd != rfd) channel_close_fd(ssh, c, &c->wfd); if (efd != sock && efd != rfd && efd != wfd) channel_close_fd(ssh, c, &c->efd); } static void fwd_perm_clear(struct permission *perm) { free(perm->host_to_connect); free(perm->listen_host); free(perm->listen_path); memset(perm, 0, sizeof(*perm)); } /* Returns an printable name for the specified forwarding permission list */ static const char * fwd_ident(int who, int where) { if (who == FORWARD_ADM) { if (where == FORWARD_LOCAL) return "admin local"; else if (where == FORWARD_REMOTE) return "admin remote"; } else if (who == FORWARD_USER) { if (where == FORWARD_LOCAL) return "user local"; else if (where == FORWARD_REMOTE) return "user remote"; } fatal("Unknown forward permission list %d/%d", who, where); } /* Returns the forwarding permission list for the specified direction */ static struct permission_set * permission_set_get(struct ssh *ssh, int where) { struct ssh_channels *sc = ssh->chanctxt; switch (where) { case FORWARD_LOCAL: return &sc->local_perms; break; case FORWARD_REMOTE: return &sc->remote_perms; break; default: fatal_f("invalid forwarding direction %d", where); } } /* Returns pointers to the specified forwarding list and its element count */ static void permission_set_get_array(struct ssh *ssh, int who, int where, struct permission ***permpp, u_int **npermpp) { struct permission_set *pset = permission_set_get(ssh, where); switch (who) { case FORWARD_USER: *permpp = &pset->permitted_user; *npermpp = &pset->num_permitted_user; break; case FORWARD_ADM: *permpp = &pset->permitted_admin; *npermpp = &pset->num_permitted_admin; break; default: fatal_f("invalid forwarding client %d", who); } } /* Adds an entry to the specified forwarding list */ static int permission_set_add(struct ssh *ssh, int who, int where, const char *host_to_connect, int port_to_connect, const char *listen_host, const char *listen_path, int listen_port, Channel *downstream) { struct permission **permp; u_int n, *npermp; permission_set_get_array(ssh, who, where, &permp, &npermp); if (*npermp >= INT_MAX) fatal_f("%s overflow", fwd_ident(who, where)); *permp = xrecallocarray(*permp, *npermp, *npermp + 1, sizeof(**permp)); n = (*npermp)++; #define MAYBE_DUP(s) ((s == NULL) ? NULL : xstrdup(s)) (*permp)[n].host_to_connect = MAYBE_DUP(host_to_connect); (*permp)[n].port_to_connect = port_to_connect; (*permp)[n].listen_host = MAYBE_DUP(listen_host); (*permp)[n].listen_path = MAYBE_DUP(listen_path); (*permp)[n].listen_port = listen_port; (*permp)[n].downstream = downstream; #undef MAYBE_DUP return (int)n; } static void mux_remove_remote_forwardings(struct ssh *ssh, Channel *c) { struct ssh_channels *sc = ssh->chanctxt; struct permission_set *pset = &sc->local_perms; struct permission *perm; int r; u_int i; for (i = 0; i < pset->num_permitted_user; i++) { perm = &pset->permitted_user[i]; if (perm->downstream != c) continue; /* cancel on the server, since mux client is gone */ debug("channel %d: cleanup remote forward for %s:%u", c->self, perm->listen_host, perm->listen_port); if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || (r = sshpkt_put_cstring(ssh, "cancel-tcpip-forward")) != 0 || (r = sshpkt_put_u8(ssh, 0)) != 0 || (r = sshpkt_put_cstring(ssh, channel_rfwd_bind_host(perm->listen_host))) != 0 || (r = sshpkt_put_u32(ssh, perm->listen_port)) != 0 || (r = sshpkt_send(ssh)) != 0) { fatal_fr(r, "channel %i", c->self); } fwd_perm_clear(perm); /* unregister */ } } /* Free the channel and close its fd/socket. */ void channel_free(struct ssh *ssh, Channel *c) { struct ssh_channels *sc = ssh->chanctxt; char *s; u_int i, n; Channel *other; struct channel_confirm *cc; for (n = 0, i = 0; i < sc->channels_alloc; i++) { if ((other = sc->channels[i]) == NULL) continue; n++; /* detach from mux client and prepare for closing */ if (c->type == SSH_CHANNEL_MUX_CLIENT && other->type == SSH_CHANNEL_MUX_PROXY && other->mux_ctx == c) { other->mux_ctx = NULL; other->type = SSH_CHANNEL_OPEN; other->istate = CHAN_INPUT_CLOSED; other->ostate = CHAN_OUTPUT_CLOSED; } } debug("channel %d: free: %s, nchannels %u", c->self, c->remote_name ? c->remote_name : "???", n); if (c->type == SSH_CHANNEL_MUX_CLIENT) { mux_remove_remote_forwardings(ssh, c); free(c->mux_ctx); c->mux_ctx = NULL; } else if (c->type == SSH_CHANNEL_MUX_LISTENER) { free(c->mux_ctx); c->mux_ctx = NULL; } if (log_level_get() >= SYSLOG_LEVEL_DEBUG3) { s = channel_open_message(ssh); debug3("channel %d: status: %s", c->self, s); free(s); } channel_close_fds(ssh, c); sshbuf_free(c->input); sshbuf_free(c->output); sshbuf_free(c->extended); c->input = c->output = c->extended = NULL; free(c->remote_name); c->remote_name = NULL; free(c->path); c->path = NULL; free(c->listening_addr); c->listening_addr = NULL; free(c->xctype); c->xctype = NULL; while ((cc = TAILQ_FIRST(&c->status_confirms)) != NULL) { if (cc->abandon_cb != NULL) cc->abandon_cb(ssh, c, cc->ctx); TAILQ_REMOVE(&c->status_confirms, cc, entry); freezero(cc, sizeof(*cc)); } if (c->filter_cleanup != NULL && c->filter_ctx != NULL) c->filter_cleanup(ssh, c->self, c->filter_ctx); sc->channels[c->self] = NULL; freezero(c, sizeof(*c)); } void channel_free_all(struct ssh *ssh) { u_int i; struct ssh_channels *sc = ssh->chanctxt; for (i = 0; i < sc->channels_alloc; i++) if (sc->channels[i] != NULL) channel_free(ssh, sc->channels[i]); free(sc->channels); sc->channels = NULL; sc->channels_alloc = 0; free(sc->x11_saved_display); sc->x11_saved_display = NULL; free(sc->x11_saved_proto); sc->x11_saved_proto = NULL; free(sc->x11_saved_data); sc->x11_saved_data = NULL; sc->x11_saved_data_len = 0; free(sc->x11_fake_data); sc->x11_fake_data = NULL; sc->x11_fake_data_len = 0; } /* * Closes the sockets/fds of all channels. This is used to close extra file * descriptors after a fork. */ void channel_close_all(struct ssh *ssh) { u_int i; for (i = 0; i < ssh->chanctxt->channels_alloc; i++) if (ssh->chanctxt->channels[i] != NULL) channel_close_fds(ssh, ssh->chanctxt->channels[i]); } /* * Stop listening to channels. */ void channel_stop_listening(struct ssh *ssh) { u_int i; Channel *c; for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { c = ssh->chanctxt->channels[i]; if (c != NULL) { switch (c->type) { case SSH_CHANNEL_AUTH_SOCKET: case SSH_CHANNEL_PORT_LISTENER: case SSH_CHANNEL_RPORT_LISTENER: case SSH_CHANNEL_X11_LISTENER: case SSH_CHANNEL_UNIX_LISTENER: case SSH_CHANNEL_RUNIX_LISTENER: channel_close_fd(ssh, c, &c->sock); channel_free(ssh, c); break; } } } } /* * Returns true if no channel has too much buffered data, and false if one or * more channel is overfull. */ int channel_not_very_much_buffered_data(struct ssh *ssh) { u_int i; u_int maxsize = ssh_packet_get_maxsize(ssh); Channel *c; for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { c = ssh->chanctxt->channels[i]; if (c == NULL || c->type != SSH_CHANNEL_OPEN) continue; if (sshbuf_len(c->output) > maxsize) { debug2("channel %d: big output buffer %zu > %u", c->self, sshbuf_len(c->output), maxsize); return 0; } } return 1; } /* Returns true if any channel is still open. */ int channel_still_open(struct ssh *ssh) { u_int i; Channel *c; for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { c = ssh->chanctxt->channels[i]; if (c == NULL) continue; switch (c->type) { case SSH_CHANNEL_X11_LISTENER: case SSH_CHANNEL_PORT_LISTENER: case SSH_CHANNEL_RPORT_LISTENER: case SSH_CHANNEL_MUX_LISTENER: case SSH_CHANNEL_CLOSED: case SSH_CHANNEL_AUTH_SOCKET: case SSH_CHANNEL_DYNAMIC: case SSH_CHANNEL_RDYNAMIC_OPEN: case SSH_CHANNEL_CONNECTING: case SSH_CHANNEL_ZOMBIE: case SSH_CHANNEL_ABANDONED: case SSH_CHANNEL_UNIX_LISTENER: case SSH_CHANNEL_RUNIX_LISTENER: continue; case SSH_CHANNEL_LARVAL: continue; case SSH_CHANNEL_OPENING: case SSH_CHANNEL_OPEN: case SSH_CHANNEL_RDYNAMIC_FINISH: case SSH_CHANNEL_X11_OPEN: case SSH_CHANNEL_MUX_CLIENT: case SSH_CHANNEL_MUX_PROXY: return 1; default: fatal_f("bad channel type %d", c->type); /* NOTREACHED */ } } return 0; } /* Returns the id of an open channel suitable for keepaliving */ int channel_find_open(struct ssh *ssh) { u_int i; Channel *c; for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { c = ssh->chanctxt->channels[i]; if (c == NULL || !c->have_remote_id) continue; switch (c->type) { case SSH_CHANNEL_CLOSED: case SSH_CHANNEL_DYNAMIC: case SSH_CHANNEL_RDYNAMIC_OPEN: case SSH_CHANNEL_RDYNAMIC_FINISH: case SSH_CHANNEL_X11_LISTENER: case SSH_CHANNEL_PORT_LISTENER: case SSH_CHANNEL_RPORT_LISTENER: case SSH_CHANNEL_MUX_LISTENER: case SSH_CHANNEL_MUX_CLIENT: case SSH_CHANNEL_MUX_PROXY: case SSH_CHANNEL_OPENING: case SSH_CHANNEL_CONNECTING: case SSH_CHANNEL_ZOMBIE: case SSH_CHANNEL_ABANDONED: case SSH_CHANNEL_UNIX_LISTENER: case SSH_CHANNEL_RUNIX_LISTENER: continue; case SSH_CHANNEL_LARVAL: case SSH_CHANNEL_AUTH_SOCKET: case SSH_CHANNEL_OPEN: case SSH_CHANNEL_X11_OPEN: return i; default: fatal_f("bad channel type %d", c->type); /* NOTREACHED */ } } return -1; } /* Returns the state of the channel's extended usage flag */ const char * channel_format_extended_usage(const Channel *c) { if (c->efd == -1) return "closed"; switch (c->extended_usage) { case CHAN_EXTENDED_WRITE: return "write"; case CHAN_EXTENDED_READ: return "read"; case CHAN_EXTENDED_IGNORE: return "ignore"; default: return "UNKNOWN"; } } static char * channel_format_status(const Channel *c) { char *ret = NULL; xasprintf(&ret, "t%d [%s] %s%u i%u/%zu o%u/%zu e[%s]/%zu " "fd %d/%d/%d sock %d cc %d io 0x%02x/0x%02x", c->type, c->xctype != NULL ? c->xctype : c->ctype, c->have_remote_id ? "r" : "nr", c->remote_id, c->istate, sshbuf_len(c->input), c->ostate, sshbuf_len(c->output), channel_format_extended_usage(c), sshbuf_len(c->extended), c->rfd, c->wfd, c->efd, c->sock, c->ctl_chan, c->io_want, c->io_ready); return ret; } /* * Returns a message describing the currently open forwarded connections, * suitable for sending to the client. The message contains crlf pairs for * newlines. */ char * channel_open_message(struct ssh *ssh) { struct sshbuf *buf; Channel *c; u_int i; int r; char *cp, *ret; if ((buf = sshbuf_new()) == NULL) fatal_f("sshbuf_new"); if ((r = sshbuf_putf(buf, "The following connections are open:\r\n")) != 0) fatal_fr(r, "sshbuf_putf"); for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { c = ssh->chanctxt->channels[i]; if (c == NULL) continue; switch (c->type) { case SSH_CHANNEL_X11_LISTENER: case SSH_CHANNEL_PORT_LISTENER: case SSH_CHANNEL_RPORT_LISTENER: case SSH_CHANNEL_CLOSED: case SSH_CHANNEL_AUTH_SOCKET: case SSH_CHANNEL_ZOMBIE: case SSH_CHANNEL_ABANDONED: case SSH_CHANNEL_MUX_LISTENER: case SSH_CHANNEL_UNIX_LISTENER: case SSH_CHANNEL_RUNIX_LISTENER: continue; case SSH_CHANNEL_LARVAL: case SSH_CHANNEL_OPENING: case SSH_CHANNEL_CONNECTING: case SSH_CHANNEL_DYNAMIC: case SSH_CHANNEL_RDYNAMIC_OPEN: case SSH_CHANNEL_RDYNAMIC_FINISH: case SSH_CHANNEL_OPEN: case SSH_CHANNEL_X11_OPEN: case SSH_CHANNEL_MUX_PROXY: case SSH_CHANNEL_MUX_CLIENT: cp = channel_format_status(c); if ((r = sshbuf_putf(buf, " #%d %.300s (%s)\r\n", c->self, c->remote_name, cp)) != 0) { free(cp); fatal_fr(r, "sshbuf_putf"); } free(cp); continue; default: fatal_f("bad channel type %d", c->type); /* NOTREACHED */ } } if ((ret = sshbuf_dup_string(buf)) == NULL) fatal_f("sshbuf_dup_string"); sshbuf_free(buf); return ret; } static void open_preamble(struct ssh *ssh, const char *where, Channel *c, const char *type) { int r; if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_OPEN)) != 0 || (r = sshpkt_put_cstring(ssh, type)) != 0 || (r = sshpkt_put_u32(ssh, c->self)) != 0 || (r = sshpkt_put_u32(ssh, c->local_window)) != 0 || (r = sshpkt_put_u32(ssh, c->local_maxpacket)) != 0) { fatal_r(r, "%s: channel %i: open", where, c->self); } } void channel_send_open(struct ssh *ssh, int id) { Channel *c = channel_lookup(ssh, id); int r; if (c == NULL) { logit("channel_send_open: %d: bad id", id); return; } debug2("channel %d: send open", id); open_preamble(ssh, __func__, c, c->ctype); if ((r = sshpkt_send(ssh)) != 0) fatal_fr(r, "channel %i", c->self); } void channel_request_start(struct ssh *ssh, int id, char *service, int wantconfirm) { Channel *c = channel_lookup(ssh, id); int r; if (c == NULL) { logit_f("%d: unknown channel id", id); return; } if (!c->have_remote_id) fatal_f("channel %d: no remote id", c->self); debug2("channel %d: request %s confirm %d", id, service, wantconfirm); if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_REQUEST)) != 0 || (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || (r = sshpkt_put_cstring(ssh, service)) != 0 || (r = sshpkt_put_u8(ssh, wantconfirm)) != 0) { fatal_fr(r, "channel %i", c->self); } } void channel_register_status_confirm(struct ssh *ssh, int id, channel_confirm_cb *cb, channel_confirm_abandon_cb *abandon_cb, void *ctx) { struct channel_confirm *cc; Channel *c; if ((c = channel_lookup(ssh, id)) == NULL) fatal_f("%d: bad id", id); cc = xcalloc(1, sizeof(*cc)); cc->cb = cb; cc->abandon_cb = abandon_cb; cc->ctx = ctx; TAILQ_INSERT_TAIL(&c->status_confirms, cc, entry); } void channel_register_open_confirm(struct ssh *ssh, int id, channel_open_fn *fn, void *ctx) { Channel *c = channel_lookup(ssh, id); if (c == NULL) { logit_f("%d: bad id", id); return; } c->open_confirm = fn; c->open_confirm_ctx = ctx; } void channel_register_cleanup(struct ssh *ssh, int id, channel_callback_fn *fn, int do_close) { Channel *c = channel_by_id(ssh, id); if (c == NULL) { logit_f("%d: bad id", id); return; } c->detach_user = fn; c->detach_close = do_close; } void channel_cancel_cleanup(struct ssh *ssh, int id) { Channel *c = channel_by_id(ssh, id); if (c == NULL) { logit_f("%d: bad id", id); return; } c->detach_user = NULL; c->detach_close = 0; } void channel_register_filter(struct ssh *ssh, int id, channel_infilter_fn *ifn, channel_outfilter_fn *ofn, channel_filter_cleanup_fn *cfn, void *ctx) { Channel *c = channel_lookup(ssh, id); if (c == NULL) { logit_f("%d: bad id", id); return; } c->input_filter = ifn; c->output_filter = ofn; c->filter_ctx = ctx; c->filter_cleanup = cfn; } void channel_set_fds(struct ssh *ssh, int id, int rfd, int wfd, int efd, int extusage, int nonblock, int is_tty, u_int window_max) { Channel *c = channel_lookup(ssh, id); int r; if (c == NULL || c->type != SSH_CHANNEL_LARVAL) fatal("channel_activate for non-larval channel %d.", id); if (!c->have_remote_id) fatal_f("channel %d: no remote id", c->self); channel_register_fds(ssh, c, rfd, wfd, efd, extusage, nonblock, is_tty); c->type = SSH_CHANNEL_OPEN; c->lastused = monotime(); c->local_window = c->local_window_max = window_max; if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_WINDOW_ADJUST)) != 0 || (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || (r = sshpkt_put_u32(ssh, c->local_window)) != 0 || (r = sshpkt_send(ssh)) != 0) fatal_fr(r, "channel %i", c->self); } static void channel_pre_listener(struct ssh *ssh, Channel *c) { c->io_want = SSH_CHAN_IO_SOCK_R; } static void channel_pre_connecting(struct ssh *ssh, Channel *c) { debug3("channel %d: waiting for connection", c->self); c->io_want = SSH_CHAN_IO_SOCK_W; } static void channel_pre_open(struct ssh *ssh, Channel *c) { c->io_want = 0; if (c->istate == CHAN_INPUT_OPEN && c->remote_window > 0 && sshbuf_len(c->input) < c->remote_window && sshbuf_check_reserve(c->input, CHAN_RBUF) == 0) c->io_want |= SSH_CHAN_IO_RFD; if (c->ostate == CHAN_OUTPUT_OPEN || c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { if (sshbuf_len(c->output) > 0) { c->io_want |= SSH_CHAN_IO_WFD; } else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { if (CHANNEL_EFD_OUTPUT_ACTIVE(c)) debug2("channel %d: " "obuf_empty delayed efd %d/(%zu)", c->self, c->efd, sshbuf_len(c->extended)); else chan_obuf_empty(ssh, c); } } /** XXX check close conditions, too */ if (c->efd != -1 && !(c->istate == CHAN_INPUT_CLOSED && c->ostate == CHAN_OUTPUT_CLOSED)) { if (c->extended_usage == CHAN_EXTENDED_WRITE && sshbuf_len(c->extended) > 0) c->io_want |= SSH_CHAN_IO_EFD_W; else if (c->efd != -1 && !(c->flags & CHAN_EOF_SENT) && (c->extended_usage == CHAN_EXTENDED_READ || c->extended_usage == CHAN_EXTENDED_IGNORE) && sshbuf_len(c->extended) < c->remote_window) c->io_want |= SSH_CHAN_IO_EFD_R; } /* XXX: What about efd? races? */ } /* * This is a special state for X11 authentication spoofing. An opened X11 * connection (when authentication spoofing is being done) remains in this * state until the first packet has been completely read. The authentication * data in that packet is then substituted by the real data if it matches the * fake data, and the channel is put into normal mode. * XXX All this happens at the client side. * Returns: 0 = need more data, -1 = wrong cookie, 1 = ok */ static int x11_open_helper(struct ssh *ssh, struct sshbuf *b) { struct ssh_channels *sc = ssh->chanctxt; u_char *ucp; u_int proto_len, data_len; /* Is this being called after the refusal deadline? */ if (sc->x11_refuse_time != 0 && monotime() >= sc->x11_refuse_time) { verbose("Rejected X11 connection after ForwardX11Timeout " "expired"); return -1; } /* Check if the fixed size part of the packet is in buffer. */ if (sshbuf_len(b) < 12) return 0; /* Parse the lengths of variable-length fields. */ ucp = sshbuf_mutable_ptr(b); if (ucp[0] == 0x42) { /* Byte order MSB first. */ proto_len = 256 * ucp[6] + ucp[7]; data_len = 256 * ucp[8] + ucp[9]; } else if (ucp[0] == 0x6c) { /* Byte order LSB first. */ proto_len = ucp[6] + 256 * ucp[7]; data_len = ucp[8] + 256 * ucp[9]; } else { debug2("Initial X11 packet contains bad byte order byte: 0x%x", ucp[0]); return -1; } /* Check if the whole packet is in buffer. */ if (sshbuf_len(b) < 12 + ((proto_len + 3) & ~3) + ((data_len + 3) & ~3)) return 0; /* Check if authentication protocol matches. */ if (proto_len != strlen(sc->x11_saved_proto) || memcmp(ucp + 12, sc->x11_saved_proto, proto_len) != 0) { debug2("X11 connection uses different authentication protocol."); return -1; } /* Check if authentication data matches our fake data. */ if (data_len != sc->x11_fake_data_len || timingsafe_bcmp(ucp + 12 + ((proto_len + 3) & ~3), sc->x11_fake_data, sc->x11_fake_data_len) != 0) { debug2("X11 auth data does not match fake data."); return -1; } /* Check fake data length */ if (sc->x11_fake_data_len != sc->x11_saved_data_len) { error("X11 fake_data_len %d != saved_data_len %d", sc->x11_fake_data_len, sc->x11_saved_data_len); return -1; } /* * Received authentication protocol and data match * our fake data. Substitute the fake data with real * data. */ memcpy(ucp + 12 + ((proto_len + 3) & ~3), sc->x11_saved_data, sc->x11_saved_data_len); return 1; } void channel_force_close(struct ssh *ssh, Channel *c, int abandon) { debug3_f("channel %d: forcibly closing", c->self); if (c->istate == CHAN_INPUT_OPEN) chan_read_failed(ssh, c); if (c->istate == CHAN_INPUT_WAIT_DRAIN) { sshbuf_reset(c->input); chan_ibuf_empty(ssh, c); } if (c->ostate == CHAN_OUTPUT_OPEN || c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { sshbuf_reset(c->output); chan_write_failed(ssh, c); } if (c->detach_user) c->detach_user(ssh, c->self, 1, NULL); if (c->efd != -1) channel_close_fd(ssh, c, &c->efd); if (abandon) c->type = SSH_CHANNEL_ABANDONED; /* exempt from inactivity timeouts */ c->inactive_deadline = 0; c->lastused = 0; } static void channel_pre_x11_open(struct ssh *ssh, Channel *c) { int ret = x11_open_helper(ssh, c->output); /* c->force_drain = 1; */ if (ret == 1) { c->type = SSH_CHANNEL_OPEN; c->lastused = monotime(); channel_pre_open(ssh, c); } else if (ret == -1) { logit("X11 connection rejected because of wrong " "authentication."); debug2("X11 rejected %d i%d/o%d", c->self, c->istate, c->ostate); channel_force_close(ssh, c, 0); } } static void channel_pre_mux_client(struct ssh *ssh, Channel *c) { c->io_want = 0; if (c->istate == CHAN_INPUT_OPEN && !c->mux_pause && sshbuf_check_reserve(c->input, CHAN_RBUF) == 0) c->io_want |= SSH_CHAN_IO_RFD; if (c->istate == CHAN_INPUT_WAIT_DRAIN) { /* clear buffer immediately (discard any partial packet) */ sshbuf_reset(c->input); chan_ibuf_empty(ssh, c); /* Start output drain. XXX just kill chan? */ chan_rcvd_oclose(ssh, c); } if (c->ostate == CHAN_OUTPUT_OPEN || c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { if (sshbuf_len(c->output) > 0) c->io_want |= SSH_CHAN_IO_WFD; else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) chan_obuf_empty(ssh, c); } } /* try to decode a socks4 header */ static int channel_decode_socks4(Channel *c, struct sshbuf *input, struct sshbuf *output) { const u_char *p; char *host; u_int len, have, i, found, need; char username[256]; struct { u_int8_t version; u_int8_t command; u_int16_t dest_port; struct in_addr dest_addr; } s4_req, s4_rsp; int r; debug2("channel %d: decode socks4", c->self); have = sshbuf_len(input); len = sizeof(s4_req); if (have < len) return 0; p = sshbuf_ptr(input); need = 1; /* SOCKS4A uses an invalid IP address 0.0.0.x */ if (p[4] == 0 && p[5] == 0 && p[6] == 0 && p[7] != 0) { debug2("channel %d: socks4a request", c->self); /* ... and needs an extra string (the hostname) */ need = 2; } /* Check for terminating NUL on the string(s) */ for (found = 0, i = len; i < have; i++) { if (p[i] == '\0') { found++; if (found == need) break; } if (i > 1024) { /* the peer is probably sending garbage */ debug("channel %d: decode socks4: too long", c->self); return -1; } } if (found < need) return 0; if ((r = sshbuf_get(input, &s4_req.version, 1)) != 0 || (r = sshbuf_get(input, &s4_req.command, 1)) != 0 || (r = sshbuf_get(input, &s4_req.dest_port, 2)) != 0 || (r = sshbuf_get(input, &s4_req.dest_addr, 4)) != 0) { debug_r(r, "channels %d: decode socks4", c->self); return -1; } have = sshbuf_len(input); p = sshbuf_ptr(input); if (memchr(p, '\0', have) == NULL) { error("channel %d: decode socks4: unterminated user", c->self); return -1; } len = strlen(p); debug2("channel %d: decode socks4: user %s/%d", c->self, p, len); len++; /* trailing '\0' */ strlcpy(username, p, sizeof(username)); if ((r = sshbuf_consume(input, len)) != 0) fatal_fr(r, "channel %d: consume", c->self); free(c->path); c->path = NULL; if (need == 1) { /* SOCKS4: one string */ host = inet_ntoa(s4_req.dest_addr); c->path = xstrdup(host); } else { /* SOCKS4A: two strings */ have = sshbuf_len(input); p = sshbuf_ptr(input); if (memchr(p, '\0', have) == NULL) { error("channel %d: decode socks4a: host not nul " "terminated", c->self); return -1; } len = strlen(p); debug2("channel %d: decode socks4a: host %s/%d", c->self, p, len); len++; /* trailing '\0' */ if (len > NI_MAXHOST) { error("channel %d: hostname \"%.100s\" too long", c->self, p); return -1; } c->path = xstrdup(p); if ((r = sshbuf_consume(input, len)) != 0) fatal_fr(r, "channel %d: consume", c->self); } c->host_port = ntohs(s4_req.dest_port); debug2("channel %d: dynamic request: socks4 host %s port %u command %u", c->self, c->path, c->host_port, s4_req.command); if (s4_req.command != 1) { debug("channel %d: cannot handle: %s cn %d", c->self, need == 1 ? "SOCKS4" : "SOCKS4A", s4_req.command); return -1; } s4_rsp.version = 0; /* vn: 0 for reply */ s4_rsp.command = 90; /* cd: req granted */ s4_rsp.dest_port = 0; /* ignored */ s4_rsp.dest_addr.s_addr = INADDR_ANY; /* ignored */ if ((r = sshbuf_put(output, &s4_rsp, sizeof(s4_rsp))) != 0) fatal_fr(r, "channel %d: append reply", c->self); return 1; } /* try to decode a socks5 header */ #define SSH_SOCKS5_AUTHDONE 0x1000 #define SSH_SOCKS5_NOAUTH 0x00 #define SSH_SOCKS5_IPV4 0x01 #define SSH_SOCKS5_DOMAIN 0x03 #define SSH_SOCKS5_IPV6 0x04 #define SSH_SOCKS5_CONNECT 0x01 #define SSH_SOCKS5_SUCCESS 0x00 static int channel_decode_socks5(Channel *c, struct sshbuf *input, struct sshbuf *output) { /* XXX use get/put_u8 instead of trusting struct padding */ struct { u_int8_t version; u_int8_t command; u_int8_t reserved; u_int8_t atyp; } s5_req, s5_rsp; u_int16_t dest_port; char dest_addr[255+1], ntop[INET6_ADDRSTRLEN]; const u_char *p; u_int have, need, i, found, nmethods, addrlen, af; int r; debug2("channel %d: decode socks5", c->self); p = sshbuf_ptr(input); if (p[0] != 0x05) return -1; have = sshbuf_len(input); if (!(c->flags & SSH_SOCKS5_AUTHDONE)) { /* format: ver | nmethods | methods */ if (have < 2) return 0; nmethods = p[1]; if (have < nmethods + 2) return 0; /* look for method: "NO AUTHENTICATION REQUIRED" */ for (found = 0, i = 2; i < nmethods + 2; i++) { if (p[i] == SSH_SOCKS5_NOAUTH) { found = 1; break; } } if (!found) { debug("channel %d: method SSH_SOCKS5_NOAUTH not found", c->self); return -1; } if ((r = sshbuf_consume(input, nmethods + 2)) != 0) fatal_fr(r, "channel %d: consume", c->self); /* version, method */ if ((r = sshbuf_put_u8(output, 0x05)) != 0 || (r = sshbuf_put_u8(output, SSH_SOCKS5_NOAUTH)) != 0) fatal_fr(r, "channel %d: append reply", c->self); c->flags |= SSH_SOCKS5_AUTHDONE; debug2("channel %d: socks5 auth done", c->self); return 0; /* need more */ } debug2("channel %d: socks5 post auth", c->self); if (have < sizeof(s5_req)+1) return 0; /* need more */ memcpy(&s5_req, p, sizeof(s5_req)); if (s5_req.version != 0x05 || s5_req.command != SSH_SOCKS5_CONNECT || s5_req.reserved != 0x00) { debug2("channel %d: only socks5 connect supported", c->self); return -1; } switch (s5_req.atyp){ case SSH_SOCKS5_IPV4: addrlen = 4; af = AF_INET; break; case SSH_SOCKS5_DOMAIN: addrlen = p[sizeof(s5_req)]; af = -1; break; case SSH_SOCKS5_IPV6: addrlen = 16; af = AF_INET6; break; default: debug2("channel %d: bad socks5 atyp %d", c->self, s5_req.atyp); return -1; } need = sizeof(s5_req) + addrlen + 2; if (s5_req.atyp == SSH_SOCKS5_DOMAIN) need++; if (have < need) return 0; if ((r = sshbuf_consume(input, sizeof(s5_req))) != 0) fatal_fr(r, "channel %d: consume", c->self); if (s5_req.atyp == SSH_SOCKS5_DOMAIN) { /* host string length */ if ((r = sshbuf_consume(input, 1)) != 0) fatal_fr(r, "channel %d: consume", c->self); } if ((r = sshbuf_get(input, &dest_addr, addrlen)) != 0 || (r = sshbuf_get(input, &dest_port, 2)) != 0) { debug_r(r, "channel %d: parse addr/port", c->self); return -1; } dest_addr[addrlen] = '\0'; free(c->path); c->path = NULL; if (s5_req.atyp == SSH_SOCKS5_DOMAIN) { if (addrlen >= NI_MAXHOST) { error("channel %d: dynamic request: socks5 hostname " "\"%.100s\" too long", c->self, dest_addr); return -1; } c->path = xstrdup(dest_addr); } else { if (inet_ntop(af, dest_addr, ntop, sizeof(ntop)) == NULL) return -1; c->path = xstrdup(ntop); } c->host_port = ntohs(dest_port); debug2("channel %d: dynamic request: socks5 host %s port %u command %u", c->self, c->path, c->host_port, s5_req.command); s5_rsp.version = 0x05; s5_rsp.command = SSH_SOCKS5_SUCCESS; s5_rsp.reserved = 0; /* ignored */ s5_rsp.atyp = SSH_SOCKS5_IPV4; dest_port = 0; /* ignored */ if ((r = sshbuf_put(output, &s5_rsp, sizeof(s5_rsp))) != 0 || (r = sshbuf_put_u32(output, ntohl(INADDR_ANY))) != 0 || (r = sshbuf_put(output, &dest_port, sizeof(dest_port))) != 0) fatal_fr(r, "channel %d: append reply", c->self); return 1; } Channel * channel_connect_stdio_fwd(struct ssh *ssh, const char *host_to_connect, int port_to_connect, int in, int out, int nonblock) { Channel *c; debug_f("%s:%d", host_to_connect, port_to_connect); c = channel_new(ssh, "stdio-forward", SSH_CHANNEL_OPENING, in, out, -1, CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, "stdio-forward", nonblock); c->path = xstrdup(host_to_connect); c->host_port = port_to_connect; c->listening_port = 0; c->force_drain = 1; channel_register_fds(ssh, c, in, out, -1, 0, 1, 0); port_open_helper(ssh, c, port_to_connect == PORT_STREAMLOCAL ? "direct-streamlocal@openssh.com" : "direct-tcpip"); return c; } /* dynamic port forwarding */ static void channel_pre_dynamic(struct ssh *ssh, Channel *c) { const u_char *p; u_int have; int ret; c->io_want = 0; have = sshbuf_len(c->input); debug2("channel %d: pre_dynamic: have %d", c->self, have); /* sshbuf_dump(c->input, stderr); */ /* check if the fixed size part of the packet is in buffer. */ if (have < 3) { /* need more */ c->io_want |= SSH_CHAN_IO_RFD; return; } /* try to guess the protocol */ p = sshbuf_ptr(c->input); /* XXX sshbuf_peek_u8? */ switch (p[0]) { case 0x04: ret = channel_decode_socks4(c, c->input, c->output); break; case 0x05: ret = channel_decode_socks5(c, c->input, c->output); break; default: ret = -1; break; } if (ret < 0) { chan_mark_dead(ssh, c); } else if (ret == 0) { debug2("channel %d: pre_dynamic: need more", c->self); /* need more */ c->io_want |= SSH_CHAN_IO_RFD; if (sshbuf_len(c->output)) c->io_want |= SSH_CHAN_IO_WFD; } else { /* switch to the next state */ c->type = SSH_CHANNEL_OPENING; port_open_helper(ssh, c, "direct-tcpip"); } } /* simulate read-error */ static void rdynamic_close(struct ssh *ssh, Channel *c) { c->type = SSH_CHANNEL_OPEN; channel_force_close(ssh, c, 0); } /* reverse dynamic port forwarding */ static void channel_before_prepare_io_rdynamic(struct ssh *ssh, Channel *c) { const u_char *p; u_int have, len; int r, ret; have = sshbuf_len(c->output); debug2("channel %d: pre_rdynamic: have %d", c->self, have); /* sshbuf_dump(c->output, stderr); */ /* EOF received */ if (c->flags & CHAN_EOF_RCVD) { if ((r = sshbuf_consume(c->output, have)) != 0) fatal_fr(r, "channel %d: consume", c->self); rdynamic_close(ssh, c); return; } /* check if the fixed size part of the packet is in buffer. */ if (have < 3) return; /* try to guess the protocol */ p = sshbuf_ptr(c->output); switch (p[0]) { case 0x04: /* switch input/output for reverse forwarding */ ret = channel_decode_socks4(c, c->output, c->input); break; case 0x05: ret = channel_decode_socks5(c, c->output, c->input); break; default: ret = -1; break; } if (ret < 0) { rdynamic_close(ssh, c); } else if (ret == 0) { debug2("channel %d: pre_rdynamic: need more", c->self); /* send socks request to peer */ len = sshbuf_len(c->input); if (len > 0 && len < c->remote_window) { if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_DATA)) != 0 || (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || (r = sshpkt_put_stringb(ssh, c->input)) != 0 || (r = sshpkt_send(ssh)) != 0) { fatal_fr(r, "channel %i: rdynamic", c->self); } if ((r = sshbuf_consume(c->input, len)) != 0) fatal_fr(r, "channel %d: consume", c->self); c->remote_window -= len; } } else if (rdynamic_connect_finish(ssh, c) < 0) { /* the connect failed */ rdynamic_close(ssh, c); } } /* This is our fake X11 server socket. */ static void channel_post_x11_listener(struct ssh *ssh, Channel *c) { Channel *nc; struct sockaddr_storage addr; int r, newsock, oerrno, remote_port; socklen_t addrlen; char buf[16384], *remote_ipaddr; if ((c->io_ready & SSH_CHAN_IO_SOCK_R) == 0) return; debug("X11 connection requested."); addrlen = sizeof(addr); newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen); if (c->single_connection) { oerrno = errno; debug2("single_connection: closing X11 listener."); channel_close_fd(ssh, c, &c->sock); chan_mark_dead(ssh, c); errno = oerrno; } if (newsock == -1) { if (errno != EINTR && errno != EWOULDBLOCK && errno != ECONNABORTED) error("accept: %.100s", strerror(errno)); if (errno == EMFILE || errno == ENFILE) c->notbefore = monotime() + 1; return; } set_nodelay(newsock); remote_ipaddr = get_peer_ipaddr(newsock); remote_port = get_peer_port(newsock); snprintf(buf, sizeof buf, "X11 connection from %.200s port %d", remote_ipaddr, remote_port); nc = channel_new(ssh, "x11-connection", SSH_CHANNEL_OPENING, newsock, newsock, -1, c->local_window_max, c->local_maxpacket, 0, buf, 1); open_preamble(ssh, __func__, nc, "x11"); if ((r = sshpkt_put_cstring(ssh, remote_ipaddr)) != 0 || (r = sshpkt_put_u32(ssh, remote_port)) != 0) { fatal_fr(r, "channel %i: reply", c->self); } if ((r = sshpkt_send(ssh)) != 0) fatal_fr(r, "channel %i: send", c->self); free(remote_ipaddr); } static void port_open_helper(struct ssh *ssh, Channel *c, char *rtype) { char *local_ipaddr = get_local_ipaddr(c->sock); int local_port = c->sock == -1 ? 65536 : get_local_port(c->sock); char *remote_ipaddr = get_peer_ipaddr(c->sock); int remote_port = get_peer_port(c->sock); int r; if (remote_port == -1) { /* Fake addr/port to appease peers that validate it (Tectia) */ free(remote_ipaddr); remote_ipaddr = xstrdup("127.0.0.1"); remote_port = 65535; } free(c->remote_name); xasprintf(&c->remote_name, "%s: listening port %d for %.100s port %d, " "connect from %.200s port %d to %.100s port %d", rtype, c->listening_port, c->path, c->host_port, remote_ipaddr, remote_port, local_ipaddr, local_port); open_preamble(ssh, __func__, c, rtype); if (strcmp(rtype, "direct-tcpip") == 0) { /* target host, port */ if ((r = sshpkt_put_cstring(ssh, c->path)) != 0 || (r = sshpkt_put_u32(ssh, c->host_port)) != 0) fatal_fr(r, "channel %i: reply", c->self); } else if (strcmp(rtype, "direct-streamlocal@openssh.com") == 0) { /* target path */ if ((r = sshpkt_put_cstring(ssh, c->path)) != 0) fatal_fr(r, "channel %i: reply", c->self); } else if (strcmp(rtype, "forwarded-streamlocal@openssh.com") == 0) { /* listen path */ if ((r = sshpkt_put_cstring(ssh, c->path)) != 0) fatal_fr(r, "channel %i: reply", c->self); } else { /* listen address, port */ if ((r = sshpkt_put_cstring(ssh, c->path)) != 0 || (r = sshpkt_put_u32(ssh, local_port)) != 0) fatal_fr(r, "channel %i: reply", c->self); } if (strcmp(rtype, "forwarded-streamlocal@openssh.com") == 0) { /* reserved for future owner/mode info */ if ((r = sshpkt_put_cstring(ssh, "")) != 0) fatal_fr(r, "channel %i: reply", c->self); } else { /* originator host and port */ if ((r = sshpkt_put_cstring(ssh, remote_ipaddr)) != 0 || (r = sshpkt_put_u32(ssh, (u_int)remote_port)) != 0) fatal_fr(r, "channel %i: reply", c->self); } if ((r = sshpkt_send(ssh)) != 0) fatal_fr(r, "channel %i: send", c->self); free(remote_ipaddr); free(local_ipaddr); } void channel_set_x11_refuse_time(struct ssh *ssh, time_t refuse_time) { ssh->chanctxt->x11_refuse_time = refuse_time; } /* * This socket is listening for connections to a forwarded TCP/IP port. */ static void channel_post_port_listener(struct ssh *ssh, Channel *c) { Channel *nc; struct sockaddr_storage addr; int newsock, nextstate; socklen_t addrlen; char *rtype; if ((c->io_ready & SSH_CHAN_IO_SOCK_R) == 0) return; debug("Connection to port %d forwarding to %.100s port %d requested.", c->listening_port, c->path, c->host_port); if (c->type == SSH_CHANNEL_RPORT_LISTENER) { nextstate = SSH_CHANNEL_OPENING; rtype = "forwarded-tcpip"; } else if (c->type == SSH_CHANNEL_RUNIX_LISTENER) { nextstate = SSH_CHANNEL_OPENING; rtype = "forwarded-streamlocal@openssh.com"; } else if (c->host_port == PORT_STREAMLOCAL) { nextstate = SSH_CHANNEL_OPENING; rtype = "direct-streamlocal@openssh.com"; } else if (c->host_port == 0) { nextstate = SSH_CHANNEL_DYNAMIC; rtype = "dynamic-tcpip"; } else { nextstate = SSH_CHANNEL_OPENING; rtype = "direct-tcpip"; } addrlen = sizeof(addr); newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen); if (newsock == -1) { if (errno != EINTR && errno != EWOULDBLOCK && errno != ECONNABORTED) error("accept: %.100s", strerror(errno)); if (errno == EMFILE || errno == ENFILE) c->notbefore = monotime() + 1; return; } if (c->host_port != PORT_STREAMLOCAL) set_nodelay(newsock); nc = channel_new(ssh, rtype, nextstate, newsock, newsock, -1, c->local_window_max, c->local_maxpacket, 0, rtype, 1); nc->listening_port = c->listening_port; nc->host_port = c->host_port; if (c->path != NULL) nc->path = xstrdup(c->path); if (nextstate != SSH_CHANNEL_DYNAMIC) port_open_helper(ssh, nc, rtype); } /* * This is the authentication agent socket listening for connections from * clients. */ static void channel_post_auth_listener(struct ssh *ssh, Channel *c) { Channel *nc; int r, newsock; struct sockaddr_storage addr; socklen_t addrlen; if ((c->io_ready & SSH_CHAN_IO_SOCK_R) == 0) return; addrlen = sizeof(addr); newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen); if (newsock == -1) { error("accept from auth socket: %.100s", strerror(errno)); if (errno == EMFILE || errno == ENFILE) c->notbefore = monotime() + 1; return; } nc = channel_new(ssh, "agent-connection", SSH_CHANNEL_OPENING, newsock, newsock, -1, c->local_window_max, c->local_maxpacket, 0, "accepted auth socket", 1); open_preamble(ssh, __func__, nc, "auth-agent@openssh.com"); if ((r = sshpkt_send(ssh)) != 0) fatal_fr(r, "channel %i", c->self); } static void channel_post_connecting(struct ssh *ssh, Channel *c) { int err = 0, sock, isopen, r; socklen_t sz = sizeof(err); if ((c->io_ready & SSH_CHAN_IO_SOCK_W) == 0) return; if (!c->have_remote_id) fatal_f("channel %d: no remote id", c->self); /* for rdynamic the OPEN_CONFIRMATION has been sent already */ isopen = (c->type == SSH_CHANNEL_RDYNAMIC_FINISH); if (getsockopt(c->sock, SOL_SOCKET, SO_ERROR, &err, &sz) == -1) { err = errno; error("getsockopt SO_ERROR failed"); } if (err == 0) { /* Non-blocking connection completed */ debug("channel %d: connected to %s port %d", c->self, c->connect_ctx.host, c->connect_ctx.port); channel_connect_ctx_free(&c->connect_ctx); c->type = SSH_CHANNEL_OPEN; c->lastused = monotime(); if (isopen) { /* no message necessary */ } else { if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_OPEN_CONFIRMATION)) != 0 || (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || (r = sshpkt_put_u32(ssh, c->self)) != 0 || (r = sshpkt_put_u32(ssh, c->local_window)) != 0 || (r = sshpkt_put_u32(ssh, c->local_maxpacket)) != 0 || (r = sshpkt_send(ssh)) != 0) fatal_fr(r, "channel %i open confirm", c->self); } return; } if (err == EINTR || err == EAGAIN || err == EINPROGRESS) return; /* Non-blocking connection failed */ debug("channel %d: connection failed: %s", c->self, strerror(err)); /* Try next address, if any */ if ((sock = connect_next(&c->connect_ctx)) == -1) { /* Exhausted all addresses for this destination */ error("connect_to %.100s port %d: failed.", c->connect_ctx.host, c->connect_ctx.port); channel_connect_ctx_free(&c->connect_ctx); if (isopen) { rdynamic_close(ssh, c); } else { if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_OPEN_FAILURE)) != 0 || (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || (r = sshpkt_put_u32(ssh, SSH2_OPEN_CONNECT_FAILED)) != 0 || (r = sshpkt_put_cstring(ssh, strerror(err))) != 0 || (r = sshpkt_put_cstring(ssh, "")) != 0 || (r = sshpkt_send(ssh)) != 0) fatal_fr(r, "channel %i: failure", c->self); chan_mark_dead(ssh, c); } } /* New non-blocking connection in progress */ close(c->sock); c->sock = c->rfd = c->wfd = sock; } static int channel_handle_rfd(struct ssh *ssh, Channel *c) { char buf[CHAN_RBUF]; ssize_t len; int r, force; size_t nr = 0, have, avail, maxlen = CHANNEL_MAX_READ; int pty_zeroread = 0; #ifdef PTY_ZEROREAD /* Bug on AIX: read(1) can return 0 for a non-closed fd */ pty_zeroread = c->isatty; #endif force = c->isatty && c->detach_close && c->istate != CHAN_INPUT_CLOSED; if (!force && (c->io_ready & SSH_CHAN_IO_RFD) == 0) return 1; if ((avail = sshbuf_avail(c->input)) == 0) return 1; /* Shouldn't happen */ /* * For "simple" channels (i.e. not datagram or filtered), we can * read directly to the channel buffer. */ if (!pty_zeroread && c->input_filter == NULL && !c->datagram) { /* Only OPEN channels have valid rwin */ if (c->type == SSH_CHANNEL_OPEN) { if ((have = sshbuf_len(c->input)) >= c->remote_window) return 1; /* shouldn't happen */ if (maxlen > c->remote_window - have) maxlen = c->remote_window - have; } if (maxlen > avail) maxlen = avail; if ((r = sshbuf_read(c->rfd, c->input, maxlen, &nr)) != 0) { if (errno == EINTR || (!force && (errno == EAGAIN || errno == EWOULDBLOCK))) return 1; debug2("channel %d: read failed rfd %d maxlen %zu: %s", c->self, c->rfd, maxlen, ssh_err(r)); goto rfail; } if (nr != 0) c->lastused = monotime(); return 1; } errno = 0; len = read(c->rfd, buf, sizeof(buf)); /* fixup AIX zero-length read with errno set to look more like errors */ if (pty_zeroread && len == 0 && errno != 0) len = -1; if (len == -1 && (errno == EINTR || ((errno == EAGAIN || errno == EWOULDBLOCK) && !force))) return 1; if (len < 0 || (!pty_zeroread && len == 0)) { debug2("channel %d: read<=0 rfd %d len %zd: %s", c->self, c->rfd, len, len == 0 ? "closed" : strerror(errno)); rfail: if (c->type != SSH_CHANNEL_OPEN) { debug2("channel %d: not open", c->self); chan_mark_dead(ssh, c); return -1; } else { chan_read_failed(ssh, c); } return -1; } c->lastused = monotime(); if (c->input_filter != NULL) { if (c->input_filter(ssh, c, buf, len) == -1) { debug2("channel %d: filter stops", c->self); chan_read_failed(ssh, c); } } else if (c->datagram) { if ((r = sshbuf_put_string(c->input, buf, len)) != 0) fatal_fr(r, "channel %i: put datagram", c->self); } else if ((r = sshbuf_put(c->input, buf, len)) != 0) fatal_fr(r, "channel %i: put data", c->self); return 1; } static int channel_handle_wfd(struct ssh *ssh, Channel *c) { struct termios tio; u_char *data = NULL, *buf; /* XXX const; need filter API change */ size_t dlen, olen = 0; int r, len; if ((c->io_ready & SSH_CHAN_IO_WFD) == 0) return 1; if (sshbuf_len(c->output) == 0) return 1; /* Send buffered output data to the socket. */ olen = sshbuf_len(c->output); if (c->output_filter != NULL) { if ((buf = c->output_filter(ssh, c, &data, &dlen)) == NULL) { debug2("channel %d: filter stops", c->self); if (c->type != SSH_CHANNEL_OPEN) chan_mark_dead(ssh, c); else chan_write_failed(ssh, c); return -1; } } else if (c->datagram) { if ((r = sshbuf_get_string(c->output, &data, &dlen)) != 0) fatal_fr(r, "channel %i: get datagram", c->self); buf = data; } else { buf = data = sshbuf_mutable_ptr(c->output); dlen = sshbuf_len(c->output); } if (c->datagram) { /* ignore truncated writes, datagrams might get lost */ len = write(c->wfd, buf, dlen); free(data); if (len == -1 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)) return 1; if (len <= 0) goto write_fail; goto out; } #ifdef _AIX /* XXX: Later AIX versions can't push as much data to tty */ if (c->wfd_isatty) dlen = MINIMUM(dlen, 8*1024); #endif len = write(c->wfd, buf, dlen); if (len == -1 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)) return 1; if (len <= 0) { write_fail: if (c->type != SSH_CHANNEL_OPEN) { debug2("channel %d: not open", c->self); chan_mark_dead(ssh, c); return -1; } else { chan_write_failed(ssh, c); } return -1; } c->lastused = monotime(); #ifndef BROKEN_TCGETATTR_ICANON if (c->isatty && dlen >= 1 && buf[0] != '\r') { if (tcgetattr(c->wfd, &tio) == 0 && !(tio.c_lflag & ECHO) && (tio.c_lflag & ICANON)) { /* * Simulate echo to reduce the impact of * traffic analysis. We need to match the * size of a SSH2_MSG_CHANNEL_DATA message * (4 byte channel id + buf) */ if ((r = sshpkt_msg_ignore(ssh, 4+len)) != 0 || (r = sshpkt_send(ssh)) != 0) fatal_fr(r, "channel %i: ignore", c->self); } } #endif /* BROKEN_TCGETATTR_ICANON */ if ((r = sshbuf_consume(c->output, len)) != 0) fatal_fr(r, "channel %i: consume", c->self); out: c->local_consumed += olen - sshbuf_len(c->output); return 1; } static int channel_handle_efd_write(struct ssh *ssh, Channel *c) { int r; ssize_t len; if ((c->io_ready & SSH_CHAN_IO_EFD_W) == 0) return 1; if (sshbuf_len(c->extended) == 0) return 1; len = write(c->efd, sshbuf_ptr(c->extended), sshbuf_len(c->extended)); debug2("channel %d: written %zd to efd %d", c->self, len, c->efd); if (len == -1 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)) return 1; if (len <= 0) { debug2("channel %d: closing write-efd %d", c->self, c->efd); channel_close_fd(ssh, c, &c->efd); } else { if ((r = sshbuf_consume(c->extended, len)) != 0) fatal_fr(r, "channel %i: consume", c->self); c->local_consumed += len; c->lastused = monotime(); } return 1; } static int channel_handle_efd_read(struct ssh *ssh, Channel *c) { char buf[CHAN_RBUF]; ssize_t len; int r, force; force = c->isatty && c->detach_close && c->istate != CHAN_INPUT_CLOSED; if (!force && (c->io_ready & SSH_CHAN_IO_EFD_R) == 0) return 1; len = read(c->efd, buf, sizeof(buf)); debug2("channel %d: read %zd from efd %d", c->self, len, c->efd); if (len == -1 && (errno == EINTR || ((errno == EAGAIN || errno == EWOULDBLOCK) && !force))) return 1; if (len <= 0) { debug2("channel %d: closing read-efd %d", c->self, c->efd); channel_close_fd(ssh, c, &c->efd); return 1; } c->lastused = monotime(); if (c->extended_usage == CHAN_EXTENDED_IGNORE) debug3("channel %d: discard efd", c->self); else if ((r = sshbuf_put(c->extended, buf, len)) != 0) fatal_fr(r, "channel %i: append", c->self); return 1; } static int channel_handle_efd(struct ssh *ssh, Channel *c) { if (c->efd == -1) return 1; /** XXX handle drain efd, too */ if (c->extended_usage == CHAN_EXTENDED_WRITE) return channel_handle_efd_write(ssh, c); else if (c->extended_usage == CHAN_EXTENDED_READ || c->extended_usage == CHAN_EXTENDED_IGNORE) return channel_handle_efd_read(ssh, c); return 1; } static int channel_check_window(struct ssh *ssh, Channel *c) { int r; if (c->type == SSH_CHANNEL_OPEN && !(c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD)) && ((c->local_window_max - c->local_window > c->local_maxpacket*3) || c->local_window < c->local_window_max/2) && c->local_consumed > 0) { if (!c->have_remote_id) fatal_f("channel %d: no remote id", c->self); if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_WINDOW_ADJUST)) != 0 || (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || (r = sshpkt_put_u32(ssh, c->local_consumed)) != 0 || (r = sshpkt_send(ssh)) != 0) { fatal_fr(r, "channel %i", c->self); } debug2("channel %d: window %d sent adjust %d", c->self, c->local_window, c->local_consumed); c->local_window += c->local_consumed; c->local_consumed = 0; } return 1; } static void channel_post_open(struct ssh *ssh, Channel *c) { channel_handle_rfd(ssh, c); channel_handle_wfd(ssh, c); channel_handle_efd(ssh, c); channel_check_window(ssh, c); } static u_int read_mux(struct ssh *ssh, Channel *c, u_int need) { char buf[CHAN_RBUF]; ssize_t len; u_int rlen; int r; if (sshbuf_len(c->input) < need) { rlen = need - sshbuf_len(c->input); len = read(c->rfd, buf, MINIMUM(rlen, CHAN_RBUF)); if (len == -1 && (errno == EINTR || errno == EAGAIN)) return sshbuf_len(c->input); if (len <= 0) { debug2("channel %d: ctl read<=0 rfd %d len %zd", c->self, c->rfd, len); chan_read_failed(ssh, c); return 0; } else if ((r = sshbuf_put(c->input, buf, len)) != 0) fatal_fr(r, "channel %i: append", c->self); } return sshbuf_len(c->input); } static void channel_post_mux_client_read(struct ssh *ssh, Channel *c) { u_int need; if ((c->io_ready & SSH_CHAN_IO_RFD) == 0) return; if (c->istate != CHAN_INPUT_OPEN && c->istate != CHAN_INPUT_WAIT_DRAIN) return; if (c->mux_pause) return; /* * Don't not read past the precise end of packets to * avoid disrupting fd passing. */ if (read_mux(ssh, c, 4) < 4) /* read header */ return; /* XXX sshbuf_peek_u32 */ need = PEEK_U32(sshbuf_ptr(c->input)); #define CHANNEL_MUX_MAX_PACKET (256 * 1024) if (need > CHANNEL_MUX_MAX_PACKET) { debug2("channel %d: packet too big %u > %u", c->self, CHANNEL_MUX_MAX_PACKET, need); chan_rcvd_oclose(ssh, c); return; } if (read_mux(ssh, c, need + 4) < need + 4) /* read body */ return; if (c->mux_rcb(ssh, c) != 0) { debug("channel %d: mux_rcb failed", c->self); chan_mark_dead(ssh, c); return; } } static void channel_post_mux_client_write(struct ssh *ssh, Channel *c) { ssize_t len; int r; if ((c->io_ready & SSH_CHAN_IO_WFD) == 0) return; if (sshbuf_len(c->output) == 0) return; len = write(c->wfd, sshbuf_ptr(c->output), sshbuf_len(c->output)); if (len == -1 && (errno == EINTR || errno == EAGAIN)) return; if (len <= 0) { chan_mark_dead(ssh, c); return; } if ((r = sshbuf_consume(c->output, len)) != 0) fatal_fr(r, "channel %i: consume", c->self); } static void channel_post_mux_client(struct ssh *ssh, Channel *c) { channel_post_mux_client_read(ssh, c); channel_post_mux_client_write(ssh, c); } static void channel_post_mux_listener(struct ssh *ssh, Channel *c) { Channel *nc; struct sockaddr_storage addr; socklen_t addrlen; int newsock; uid_t euid; gid_t egid; if ((c->io_ready & SSH_CHAN_IO_SOCK_R) == 0) return; debug("multiplexing control connection"); /* * Accept connection on control socket */ memset(&addr, 0, sizeof(addr)); addrlen = sizeof(addr); if ((newsock = accept(c->sock, (struct sockaddr*)&addr, &addrlen)) == -1) { error_f("accept: %s", strerror(errno)); if (errno == EMFILE || errno == ENFILE) c->notbefore = monotime() + 1; return; } if (getpeereid(newsock, &euid, &egid) == -1) { error_f("getpeereid failed: %s", strerror(errno)); close(newsock); return; } if ((euid != 0) && (getuid() != euid)) { error("multiplex uid mismatch: peer euid %u != uid %u", (u_int)euid, (u_int)getuid()); close(newsock); return; } nc = channel_new(ssh, "mux-control", SSH_CHANNEL_MUX_CLIENT, newsock, newsock, -1, c->local_window_max, c->local_maxpacket, 0, "mux-control", 1); nc->mux_rcb = c->mux_rcb; debug3_f("new mux channel %d fd %d", nc->self, nc->sock); /* establish state */ nc->mux_rcb(ssh, nc); /* mux state transitions must not elicit protocol messages */ nc->flags |= CHAN_LOCAL; } static void channel_handler_init(struct ssh_channels *sc) { chan_fn **pre, **post; if ((pre = calloc(SSH_CHANNEL_MAX_TYPE, sizeof(*pre))) == NULL || (post = calloc(SSH_CHANNEL_MAX_TYPE, sizeof(*post))) == NULL) fatal_f("allocation failed"); pre[SSH_CHANNEL_OPEN] = &channel_pre_open; pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open; pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; pre[SSH_CHANNEL_RPORT_LISTENER] = &channel_pre_listener; pre[SSH_CHANNEL_UNIX_LISTENER] = &channel_pre_listener; pre[SSH_CHANNEL_RUNIX_LISTENER] = &channel_pre_listener; pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting; pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic; pre[SSH_CHANNEL_RDYNAMIC_FINISH] = &channel_pre_connecting; pre[SSH_CHANNEL_MUX_LISTENER] = &channel_pre_listener; pre[SSH_CHANNEL_MUX_CLIENT] = &channel_pre_mux_client; post[SSH_CHANNEL_OPEN] = &channel_post_open; post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; post[SSH_CHANNEL_RPORT_LISTENER] = &channel_post_port_listener; post[SSH_CHANNEL_UNIX_LISTENER] = &channel_post_port_listener; post[SSH_CHANNEL_RUNIX_LISTENER] = &channel_post_port_listener; post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; post[SSH_CHANNEL_DYNAMIC] = &channel_post_open; post[SSH_CHANNEL_RDYNAMIC_FINISH] = &channel_post_connecting; post[SSH_CHANNEL_MUX_LISTENER] = &channel_post_mux_listener; post[SSH_CHANNEL_MUX_CLIENT] = &channel_post_mux_client; sc->channel_pre = pre; sc->channel_post = post; } /* gc dead channels */ static void channel_garbage_collect(struct ssh *ssh, Channel *c) { if (c == NULL) return; if (c->detach_user != NULL) { if (!chan_is_dead(ssh, c, c->detach_close)) return; debug2("channel %d: gc: notify user", c->self); c->detach_user(ssh, c->self, 0, NULL); /* if we still have a callback */ if (c->detach_user != NULL) return; debug2("channel %d: gc: user detached", c->self); } if (!chan_is_dead(ssh, c, 1)) return; debug2("channel %d: garbage collecting", c->self); channel_free(ssh, c); } enum channel_table { CHAN_PRE, CHAN_POST }; static void channel_handler(struct ssh *ssh, int table, struct timespec *timeout) { struct ssh_channels *sc = ssh->chanctxt; chan_fn **ftab = table == CHAN_PRE ? sc->channel_pre : sc->channel_post; u_int i, oalloc; Channel *c; time_t now; now = monotime(); for (i = 0, oalloc = sc->channels_alloc; i < oalloc; i++) { c = sc->channels[i]; if (c == NULL) continue; /* Try to keep IO going while rekeying */ if (ssh_packet_is_rekeying(ssh) && c->type != SSH_CHANNEL_OPEN) continue; if (c->delayed) { if (table == CHAN_PRE) c->delayed = 0; else continue; } if (ftab[c->type] != NULL) { if (table == CHAN_PRE && c->type == SSH_CHANNEL_OPEN && c->inactive_deadline != 0 && c->lastused != 0 && now >= c->lastused + c->inactive_deadline) { /* channel closed for inactivity */ verbose("channel %d: closing after %u seconds " "of inactivity", c->self, c->inactive_deadline); channel_force_close(ssh, c, 1); } else if (c->notbefore <= now) { /* Run handlers that are not paused. */ (*ftab[c->type])(ssh, c); /* inactivity timeouts must interrupt poll() */ if (timeout != NULL && c->type == SSH_CHANNEL_OPEN && c->lastused != 0 && c->inactive_deadline != 0) { ptimeout_deadline_monotime(timeout, c->lastused + c->inactive_deadline); } } else if (timeout != NULL) { /* * Arrange for poll() wakeup when channel pause * timer expires. */ ptimeout_deadline_monotime(timeout, c->notbefore); } } channel_garbage_collect(ssh, c); } } /* * Create sockets before preparing IO. * This is necessary for things that need to happen after reading * the network-input but need to be completed before IO event setup, e.g. * because they may create new channels. */ static void channel_before_prepare_io(struct ssh *ssh) { struct ssh_channels *sc = ssh->chanctxt; Channel *c; u_int i, oalloc; for (i = 0, oalloc = sc->channels_alloc; i < oalloc; i++) { c = sc->channels[i]; if (c == NULL) continue; if (c->type == SSH_CHANNEL_RDYNAMIC_OPEN) channel_before_prepare_io_rdynamic(ssh, c); } } static void dump_channel_poll(const char *func, const char *what, Channel *c, u_int pollfd_offset, struct pollfd *pfd) { #ifdef DEBUG_CHANNEL_POLL debug3("%s: channel %d: %s r%d w%d e%d s%d c->pfds [ %d %d %d %d ] " "io_want 0x%02x io_ready 0x%02x pfd[%u].fd=%d " "pfd.ev 0x%02x pfd.rev 0x%02x", func, c->self, what, c->rfd, c->wfd, c->efd, c->sock, c->pfds[0], c->pfds[1], c->pfds[2], c->pfds[3], c->io_want, c->io_ready, pollfd_offset, pfd->fd, pfd->events, pfd->revents); #endif } /* Prepare pollfd entries for a single channel */ static void channel_prepare_pollfd(Channel *c, u_int *next_pollfd, struct pollfd *pfd, u_int npfd) { u_int ev, p = *next_pollfd; if (c == NULL) return; if (p + 4 > npfd) { /* Shouldn't happen */ fatal_f("channel %d: bad pfd offset %u (max %u)", c->self, p, npfd); } c->pfds[0] = c->pfds[1] = c->pfds[2] = c->pfds[3] = -1; /* * prepare c->rfd * * This is a special case, since c->rfd might be the same as * c->wfd, c->efd and/or c->sock. Handle those here if they want * IO too. */ if (c->rfd != -1) { ev = 0; if ((c->io_want & SSH_CHAN_IO_RFD) != 0) ev |= POLLIN; /* rfd == wfd */ if (c->wfd == c->rfd) { if ((c->io_want & SSH_CHAN_IO_WFD) != 0) ev |= POLLOUT; } /* rfd == efd */ if (c->efd == c->rfd) { if ((c->io_want & SSH_CHAN_IO_EFD_R) != 0) ev |= POLLIN; if ((c->io_want & SSH_CHAN_IO_EFD_W) != 0) ev |= POLLOUT; } /* rfd == sock */ if (c->sock == c->rfd) { if ((c->io_want & SSH_CHAN_IO_SOCK_R) != 0) ev |= POLLIN; if ((c->io_want & SSH_CHAN_IO_SOCK_W) != 0) ev |= POLLOUT; } /* Pack a pfd entry if any event armed for this fd */ if (ev != 0) { c->pfds[0] = p; pfd[p].fd = c->rfd; pfd[p].events = ev; dump_channel_poll(__func__, "rfd", c, p, &pfd[p]); p++; } } /* prepare c->wfd if wanting IO and not already handled above */ if (c->wfd != -1 && c->rfd != c->wfd) { ev = 0; if ((c->io_want & SSH_CHAN_IO_WFD)) ev |= POLLOUT; /* Pack a pfd entry if any event armed for this fd */ if (ev != 0) { c->pfds[1] = p; pfd[p].fd = c->wfd; pfd[p].events = ev; dump_channel_poll(__func__, "wfd", c, p, &pfd[p]); p++; } } /* prepare c->efd if wanting IO and not already handled above */ if (c->efd != -1 && c->rfd != c->efd) { ev = 0; if ((c->io_want & SSH_CHAN_IO_EFD_R) != 0) ev |= POLLIN; if ((c->io_want & SSH_CHAN_IO_EFD_W) != 0) ev |= POLLOUT; /* Pack a pfd entry if any event armed for this fd */ if (ev != 0) { c->pfds[2] = p; pfd[p].fd = c->efd; pfd[p].events = ev; dump_channel_poll(__func__, "efd", c, p, &pfd[p]); p++; } } /* prepare c->sock if wanting IO and not already handled above */ if (c->sock != -1 && c->rfd != c->sock) { ev = 0; if ((c->io_want & SSH_CHAN_IO_SOCK_R) != 0) ev |= POLLIN; if ((c->io_want & SSH_CHAN_IO_SOCK_W) != 0) ev |= POLLOUT; /* Pack a pfd entry if any event armed for this fd */ if (ev != 0) { c->pfds[3] = p; pfd[p].fd = c->sock; pfd[p].events = 0; dump_channel_poll(__func__, "sock", c, p, &pfd[p]); p++; } } *next_pollfd = p; } /* * Allocate/prepare poll structure */ void channel_prepare_poll(struct ssh *ssh, struct pollfd **pfdp, u_int *npfd_allocp, u_int *npfd_activep, u_int npfd_reserved, struct timespec *timeout) { struct ssh_channels *sc = ssh->chanctxt; u_int i, oalloc, p, npfd = npfd_reserved; channel_before_prepare_io(ssh); /* might create a new channel */ /* clear out I/O flags from last poll */ for (i = 0; i < sc->channels_alloc; i++) { if (sc->channels[i] == NULL) continue; sc->channels[i]->io_want = sc->channels[i]->io_ready = 0; } /* Allocate 4x pollfd for each channel (rfd, wfd, efd, sock) */ if (sc->channels_alloc >= (INT_MAX / 4) - npfd_reserved) fatal_f("too many channels"); /* shouldn't happen */ npfd += sc->channels_alloc * 4; if (npfd > *npfd_allocp) { *pfdp = xrecallocarray(*pfdp, *npfd_allocp, npfd, sizeof(**pfdp)); *npfd_allocp = npfd; } *npfd_activep = npfd_reserved; oalloc = sc->channels_alloc; channel_handler(ssh, CHAN_PRE, timeout); if (oalloc != sc->channels_alloc) { /* shouldn't happen */ fatal_f("channels_alloc changed during CHAN_PRE " "(was %u, now %u)", oalloc, sc->channels_alloc); } /* Prepare pollfd */ p = npfd_reserved; for (i = 0; i < sc->channels_alloc; i++) channel_prepare_pollfd(sc->channels[i], &p, *pfdp, npfd); *npfd_activep = p; } static void fd_ready(Channel *c, int p, struct pollfd *pfds, u_int npfd, int fd, const char *what, u_int revents_mask, u_int ready) { struct pollfd *pfd = &pfds[p]; if (fd == -1) return; if (p == -1 || (u_int)p >= npfd) fatal_f("channel %d: bad pfd %d (max %u)", c->self, p, npfd); dump_channel_poll(__func__, what, c, p, pfd); if (pfd->fd != fd) { fatal("channel %d: inconsistent %s fd=%d pollfd[%u].fd %d " "r%d w%d e%d s%d", c->self, what, fd, p, pfd->fd, c->rfd, c->wfd, c->efd, c->sock); } if ((pfd->revents & POLLNVAL) != 0) { fatal("channel %d: invalid %s pollfd[%u].fd %d r%d w%d e%d s%d", c->self, what, p, pfd->fd, c->rfd, c->wfd, c->efd, c->sock); } if ((pfd->revents & (revents_mask|POLLHUP|POLLERR)) != 0) c->io_ready |= ready & c->io_want; } /* * After poll, perform any appropriate operations for channels which have * events pending. */ void channel_after_poll(struct ssh *ssh, struct pollfd *pfd, u_int npfd) { struct ssh_channels *sc = ssh->chanctxt; u_int i; int p; Channel *c; #ifdef DEBUG_CHANNEL_POLL for (p = 0; p < (int)npfd; p++) { if (pfd[p].revents == 0) continue; debug_f("pfd[%u].fd %d rev 0x%04x", p, pfd[p].fd, pfd[p].revents); } #endif /* Convert pollfd into c->io_ready */ for (i = 0; i < sc->channels_alloc; i++) { c = sc->channels[i]; if (c == NULL) continue; /* if rfd is shared with efd/sock then wfd should be too */ if (c->rfd != -1 && c->wfd != -1 && c->rfd != c->wfd && (c->rfd == c->efd || c->rfd == c->sock)) { /* Shouldn't happen */ fatal_f("channel %d: unexpected fds r%d w%d e%d s%d", c->self, c->rfd, c->wfd, c->efd, c->sock); } c->io_ready = 0; /* rfd, potentially shared with wfd, efd and sock */ if (c->rfd != -1 && (p = c->pfds[0]) != -1) { fd_ready(c, p, pfd, npfd, c->rfd, "rfd", POLLIN, SSH_CHAN_IO_RFD); if (c->rfd == c->wfd) { fd_ready(c, p, pfd, npfd, c->wfd, "wfd/r", POLLOUT, SSH_CHAN_IO_WFD); } if (c->rfd == c->efd) { fd_ready(c, p, pfd, npfd, c->efd, "efdr/r", POLLIN, SSH_CHAN_IO_EFD_R); fd_ready(c, p, pfd, npfd, c->efd, "efdw/r", POLLOUT, SSH_CHAN_IO_EFD_W); } if (c->rfd == c->sock) { fd_ready(c, p, pfd, npfd, c->sock, "sockr/r", POLLIN, SSH_CHAN_IO_SOCK_R); fd_ready(c, p, pfd, npfd, c->sock, "sockw/r", POLLOUT, SSH_CHAN_IO_SOCK_W); } dump_channel_poll(__func__, "rfd", c, p, pfd); } /* wfd */ if (c->wfd != -1 && c->wfd != c->rfd && (p = c->pfds[1]) != -1) { fd_ready(c, p, pfd, npfd, c->wfd, "wfd", POLLOUT, SSH_CHAN_IO_WFD); dump_channel_poll(__func__, "wfd", c, p, pfd); } /* efd */ if (c->efd != -1 && c->efd != c->rfd && (p = c->pfds[2]) != -1) { fd_ready(c, p, pfd, npfd, c->efd, "efdr", POLLIN, SSH_CHAN_IO_EFD_R); fd_ready(c, p, pfd, npfd, c->efd, "efdw", POLLOUT, SSH_CHAN_IO_EFD_W); dump_channel_poll(__func__, "efd", c, p, pfd); } /* sock */ if (c->sock != -1 && c->sock != c->rfd && (p = c->pfds[3]) != -1) { fd_ready(c, p, pfd, npfd, c->sock, "sockr", POLLIN, SSH_CHAN_IO_SOCK_R); fd_ready(c, p, pfd, npfd, c->sock, "sockw", POLLOUT, SSH_CHAN_IO_SOCK_W); dump_channel_poll(__func__, "sock", c, p, pfd); } } channel_handler(ssh, CHAN_POST, NULL); } /* * Enqueue data for channels with open or draining c->input. + * Returns non-zero if a packet was enqueued. */ -static void +static int channel_output_poll_input_open(struct ssh *ssh, Channel *c) { size_t len, plen; const u_char *pkt; int r; if ((len = sshbuf_len(c->input)) == 0) { if (c->istate == CHAN_INPUT_WAIT_DRAIN) { /* * input-buffer is empty and read-socket shutdown: * tell peer, that we will not send more data: * send IEOF. * hack for extended data: delay EOF if EFD still * in use. */ if (CHANNEL_EFD_INPUT_ACTIVE(c)) debug2("channel %d: " "ibuf_empty delayed efd %d/(%zu)", c->self, c->efd, sshbuf_len(c->extended)); else chan_ibuf_empty(ssh, c); } - return; + return 0; } if (!c->have_remote_id) fatal_f("channel %d: no remote id", c->self); if (c->datagram) { /* Check datagram will fit; drop if not */ if ((r = sshbuf_get_string_direct(c->input, &pkt, &plen)) != 0) fatal_fr(r, "channel %i: get datagram", c->self); /* * XXX this does tail-drop on the datagram queue which is * usually suboptimal compared to head-drop. Better to have * backpressure at read time? (i.e. read + discard) */ if (plen > c->remote_window || plen > c->remote_maxpacket) { debug("channel %d: datagram too big", c->self); - return; + return 0; } /* Enqueue it */ if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_DATA)) != 0 || (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || (r = sshpkt_put_string(ssh, pkt, plen)) != 0 || (r = sshpkt_send(ssh)) != 0) fatal_fr(r, "channel %i: send datagram", c->self); c->remote_window -= plen; - return; + return 1; } /* Enqueue packet for buffered data. */ if (len > c->remote_window) len = c->remote_window; if (len > c->remote_maxpacket) len = c->remote_maxpacket; if (len == 0) - return; + return 0; if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_DATA)) != 0 || (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || (r = sshpkt_put_string(ssh, sshbuf_ptr(c->input), len)) != 0 || (r = sshpkt_send(ssh)) != 0) fatal_fr(r, "channel %i: send data", c->self); if ((r = sshbuf_consume(c->input, len)) != 0) fatal_fr(r, "channel %i: consume", c->self); c->remote_window -= len; + return 1; } /* * Enqueue data for channels with open c->extended in read mode. + * Returns non-zero if a packet was enqueued. */ -static void +static int channel_output_poll_extended_read(struct ssh *ssh, Channel *c) { size_t len; int r; if ((len = sshbuf_len(c->extended)) == 0) - return; + return 0; debug2("channel %d: rwin %u elen %zu euse %d", c->self, c->remote_window, sshbuf_len(c->extended), c->extended_usage); if (len > c->remote_window) len = c->remote_window; if (len > c->remote_maxpacket) len = c->remote_maxpacket; if (len == 0) - return; + return 0; if (!c->have_remote_id) fatal_f("channel %d: no remote id", c->self); if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_EXTENDED_DATA)) != 0 || (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || (r = sshpkt_put_u32(ssh, SSH2_EXTENDED_DATA_STDERR)) != 0 || (r = sshpkt_put_string(ssh, sshbuf_ptr(c->extended), len)) != 0 || (r = sshpkt_send(ssh)) != 0) fatal_fr(r, "channel %i: data", c->self); if ((r = sshbuf_consume(c->extended, len)) != 0) fatal_fr(r, "channel %i: consume", c->self); c->remote_window -= len; debug2("channel %d: sent ext data %zu", c->self, len); + return 1; } -/* If there is data to send to the connection, enqueue some of it now. */ -void +/* + * If there is data to send to the connection, enqueue some of it now. + * Returns non-zero if data was enqueued. + */ +int channel_output_poll(struct ssh *ssh) { struct ssh_channels *sc = ssh->chanctxt; Channel *c; u_int i; + int ret = 0; for (i = 0; i < sc->channels_alloc; i++) { c = sc->channels[i]; if (c == NULL) continue; /* * We are only interested in channels that can have buffered * incoming data. */ if (c->type != SSH_CHANNEL_OPEN) continue; if ((c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD))) { /* XXX is this true? */ debug3("channel %d: will not send data after close", c->self); continue; } /* Get the amount of buffered data for this channel. */ if (c->istate == CHAN_INPUT_OPEN || c->istate == CHAN_INPUT_WAIT_DRAIN) - channel_output_poll_input_open(ssh, c); + ret |= channel_output_poll_input_open(ssh, c); /* Send extended data, i.e. stderr */ if (!(c->flags & CHAN_EOF_SENT) && c->extended_usage == CHAN_EXTENDED_READ) - channel_output_poll_extended_read(ssh, c); + ret |= channel_output_poll_extended_read(ssh, c); } + return ret; } /* -- mux proxy support */ /* * When multiplexing channel messages for mux clients we have to deal * with downstream messages from the mux client and upstream messages * from the ssh server: * 1) Handling downstream messages is straightforward and happens * in channel_proxy_downstream(): * - We forward all messages (mostly) unmodified to the server. * - However, in order to route messages from upstream to the correct * downstream client, we have to replace the channel IDs used by the * mux clients with a unique channel ID because the mux clients might * use conflicting channel IDs. * - so we inspect and change both SSH2_MSG_CHANNEL_OPEN and * SSH2_MSG_CHANNEL_OPEN_CONFIRMATION messages, create a local * SSH_CHANNEL_MUX_PROXY channel and replace the mux clients ID * with the newly allocated channel ID. * 2) Upstream messages are received by matching SSH_CHANNEL_MUX_PROXY * channels and processed by channel_proxy_upstream(). The local channel ID * is then translated back to the original mux client ID. * 3) In both cases we need to keep track of matching SSH2_MSG_CHANNEL_CLOSE * messages so we can clean up SSH_CHANNEL_MUX_PROXY channels. * 4) The SSH_CHANNEL_MUX_PROXY channels also need to closed when the * downstream mux client are removed. * 5) Handling SSH2_MSG_CHANNEL_OPEN messages from the upstream server * requires more work, because they are not addressed to a specific * channel. E.g. client_request_forwarded_tcpip() needs to figure * out whether the request is addressed to the local client or a * specific downstream client based on the listen-address/port. * 6) Agent and X11-Forwarding have a similar problem and are currently * not supported as the matching session/channel cannot be identified * easily. */ /* * receive packets from downstream mux clients: * channel callback fired on read from mux client, creates * SSH_CHANNEL_MUX_PROXY channels and translates channel IDs * on channel creation. */ int channel_proxy_downstream(struct ssh *ssh, Channel *downstream) { Channel *c = NULL; struct sshbuf *original = NULL, *modified = NULL; const u_char *cp; char *ctype = NULL, *listen_host = NULL; u_char type; size_t have; int ret = -1, r; u_int id, remote_id, listen_port; /* sshbuf_dump(downstream->input, stderr); */ if ((r = sshbuf_get_string_direct(downstream->input, &cp, &have)) != 0) { error_fr(r, "parse"); return -1; } if (have < 2) { error_f("short message"); return -1; } type = cp[1]; /* skip padlen + type */ cp += 2; have -= 2; if (ssh_packet_log_type(type)) debug3_f("channel %u: down->up: type %u", downstream->self, type); switch (type) { case SSH2_MSG_CHANNEL_OPEN: if ((original = sshbuf_from(cp, have)) == NULL || (modified = sshbuf_new()) == NULL) { error_f("alloc"); goto out; } if ((r = sshbuf_get_cstring(original, &ctype, NULL)) != 0 || (r = sshbuf_get_u32(original, &id)) != 0) { error_fr(r, "parse"); goto out; } c = channel_new(ssh, "mux-proxy", SSH_CHANNEL_MUX_PROXY, -1, -1, -1, 0, 0, 0, ctype, 1); c->mux_ctx = downstream; /* point to mux client */ c->mux_downstream_id = id; /* original downstream id */ if ((r = sshbuf_put_cstring(modified, ctype)) != 0 || (r = sshbuf_put_u32(modified, c->self)) != 0 || (r = sshbuf_putb(modified, original)) != 0) { error_fr(r, "compose"); channel_free(ssh, c); goto out; } break; case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION: /* * Almost the same as SSH2_MSG_CHANNEL_OPEN, except then we * need to parse 'remote_id' instead of 'ctype'. */ if ((original = sshbuf_from(cp, have)) == NULL || (modified = sshbuf_new()) == NULL) { error_f("alloc"); goto out; } if ((r = sshbuf_get_u32(original, &remote_id)) != 0 || (r = sshbuf_get_u32(original, &id)) != 0) { error_fr(r, "parse"); goto out; } c = channel_new(ssh, "mux-proxy", SSH_CHANNEL_MUX_PROXY, -1, -1, -1, 0, 0, 0, "mux-down-connect", 1); c->mux_ctx = downstream; /* point to mux client */ c->mux_downstream_id = id; c->remote_id = remote_id; c->have_remote_id = 1; if ((r = sshbuf_put_u32(modified, remote_id)) != 0 || (r = sshbuf_put_u32(modified, c->self)) != 0 || (r = sshbuf_putb(modified, original)) != 0) { error_fr(r, "compose"); channel_free(ssh, c); goto out; } break; case SSH2_MSG_GLOBAL_REQUEST: if ((original = sshbuf_from(cp, have)) == NULL) { error_f("alloc"); goto out; } if ((r = sshbuf_get_cstring(original, &ctype, NULL)) != 0) { error_fr(r, "parse"); goto out; } if (strcmp(ctype, "tcpip-forward") != 0) { error_f("unsupported request %s", ctype); goto out; } if ((r = sshbuf_get_u8(original, NULL)) != 0 || (r = sshbuf_get_cstring(original, &listen_host, NULL)) != 0 || (r = sshbuf_get_u32(original, &listen_port)) != 0) { error_fr(r, "parse"); goto out; } if (listen_port > 65535) { error_f("tcpip-forward for %s: bad port %u", listen_host, listen_port); goto out; } /* Record that connection to this host/port is permitted. */ permission_set_add(ssh, FORWARD_USER, FORWARD_LOCAL, "", -1, listen_host, NULL, (int)listen_port, downstream); listen_host = NULL; break; case SSH2_MSG_CHANNEL_CLOSE: if (have < 4) break; remote_id = PEEK_U32(cp); if ((c = channel_by_remote_id(ssh, remote_id)) != NULL) { if (c->flags & CHAN_CLOSE_RCVD) channel_free(ssh, c); else c->flags |= CHAN_CLOSE_SENT; } break; } if (modified) { if ((r = sshpkt_start(ssh, type)) != 0 || (r = sshpkt_putb(ssh, modified)) != 0 || (r = sshpkt_send(ssh)) != 0) { error_fr(r, "send"); goto out; } } else { if ((r = sshpkt_start(ssh, type)) != 0 || (r = sshpkt_put(ssh, cp, have)) != 0 || (r = sshpkt_send(ssh)) != 0) { error_fr(r, "send"); goto out; } } ret = 0; out: free(ctype); free(listen_host); sshbuf_free(original); sshbuf_free(modified); return ret; } /* * receive packets from upstream server and de-multiplex packets * to correct downstream: * implemented as a helper for channel input handlers, * replaces local (proxy) channel ID with downstream channel ID. */ int channel_proxy_upstream(Channel *c, int type, u_int32_t seq, struct ssh *ssh) { struct sshbuf *b = NULL; Channel *downstream; const u_char *cp = NULL; size_t len; int r; /* * When receiving packets from the peer we need to check whether we * need to forward the packets to the mux client. In this case we * restore the original channel id and keep track of CLOSE messages, * so we can cleanup the channel. */ if (c == NULL || c->type != SSH_CHANNEL_MUX_PROXY) return 0; if ((downstream = c->mux_ctx) == NULL) return 0; switch (type) { case SSH2_MSG_CHANNEL_CLOSE: case SSH2_MSG_CHANNEL_DATA: case SSH2_MSG_CHANNEL_EOF: case SSH2_MSG_CHANNEL_EXTENDED_DATA: case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION: case SSH2_MSG_CHANNEL_OPEN_FAILURE: case SSH2_MSG_CHANNEL_WINDOW_ADJUST: case SSH2_MSG_CHANNEL_SUCCESS: case SSH2_MSG_CHANNEL_FAILURE: case SSH2_MSG_CHANNEL_REQUEST: break; default: debug2_f("channel %u: unsupported type %u", c->self, type); return 0; } if ((b = sshbuf_new()) == NULL) { error_f("alloc reply"); goto out; } /* get remaining payload (after id) */ cp = sshpkt_ptr(ssh, &len); if (cp == NULL) { error_f("no packet"); goto out; } /* translate id and send to muxclient */ if ((r = sshbuf_put_u8(b, 0)) != 0 || /* padlen */ (r = sshbuf_put_u8(b, type)) != 0 || (r = sshbuf_put_u32(b, c->mux_downstream_id)) != 0 || (r = sshbuf_put(b, cp, len)) != 0 || (r = sshbuf_put_stringb(downstream->output, b)) != 0) { error_fr(r, "compose muxclient"); goto out; } /* sshbuf_dump(b, stderr); */ if (ssh_packet_log_type(type)) debug3_f("channel %u: up->down: type %u", c->self, type); out: /* update state */ switch (type) { case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION: /* record remote_id for SSH2_MSG_CHANNEL_CLOSE */ if (cp && len > 4) { c->remote_id = PEEK_U32(cp); c->have_remote_id = 1; } break; case SSH2_MSG_CHANNEL_CLOSE: if (c->flags & CHAN_CLOSE_SENT) channel_free(ssh, c); else c->flags |= CHAN_CLOSE_RCVD; break; } sshbuf_free(b); return 1; } /* -- protocol input */ /* Parse a channel ID from the current packet */ static int channel_parse_id(struct ssh *ssh, const char *where, const char *what) { u_int32_t id; int r; if ((r = sshpkt_get_u32(ssh, &id)) != 0) { error_r(r, "%s: parse id", where); ssh_packet_disconnect(ssh, "Invalid %s message", what); } if (id > INT_MAX) { error_r(r, "%s: bad channel id %u", where, id); ssh_packet_disconnect(ssh, "Invalid %s channel id", what); } return (int)id; } /* Lookup a channel from an ID in the current packet */ static Channel * channel_from_packet_id(struct ssh *ssh, const char *where, const char *what) { int id = channel_parse_id(ssh, where, what); Channel *c; if ((c = channel_lookup(ssh, id)) == NULL) { ssh_packet_disconnect(ssh, "%s packet referred to nonexistent channel %d", what, id); } return c; } int channel_input_data(int type, u_int32_t seq, struct ssh *ssh) { const u_char *data; size_t data_len, win_len; Channel *c = channel_from_packet_id(ssh, __func__, "data"); int r; if (channel_proxy_upstream(c, type, seq, ssh)) return 0; /* Ignore any data for non-open channels (might happen on close) */ if (c->type != SSH_CHANNEL_OPEN && c->type != SSH_CHANNEL_RDYNAMIC_OPEN && c->type != SSH_CHANNEL_RDYNAMIC_FINISH && c->type != SSH_CHANNEL_X11_OPEN) return 0; /* Get the data. */ if ((r = sshpkt_get_string_direct(ssh, &data, &data_len)) != 0 || (r = sshpkt_get_end(ssh)) != 0) fatal_fr(r, "channel %i: get data", c->self); win_len = data_len; if (c->datagram) win_len += 4; /* string length header */ /* * The sending side reduces its window as it sends data, so we * must 'fake' consumption of the data in order to ensure that window * updates are sent back. Otherwise the connection might deadlock. */ if (c->ostate != CHAN_OUTPUT_OPEN) { c->local_window -= win_len; c->local_consumed += win_len; return 0; } if (win_len > c->local_maxpacket) { logit("channel %d: rcvd big packet %zu, maxpack %u", c->self, win_len, c->local_maxpacket); return 0; } if (win_len > c->local_window) { logit("channel %d: rcvd too much data %zu, win %u", c->self, win_len, c->local_window); return 0; } c->local_window -= win_len; if (c->datagram) { if ((r = sshbuf_put_string(c->output, data, data_len)) != 0) fatal_fr(r, "channel %i: append datagram", c->self); } else if ((r = sshbuf_put(c->output, data, data_len)) != 0) fatal_fr(r, "channel %i: append data", c->self); return 0; } int channel_input_extended_data(int type, u_int32_t seq, struct ssh *ssh) { const u_char *data; size_t data_len; u_int32_t tcode; Channel *c = channel_from_packet_id(ssh, __func__, "extended data"); int r; if (channel_proxy_upstream(c, type, seq, ssh)) return 0; if (c->type != SSH_CHANNEL_OPEN) { logit("channel %d: ext data for non open", c->self); return 0; } if (c->flags & CHAN_EOF_RCVD) { if (ssh->compat & SSH_BUG_EXTEOF) debug("channel %d: accepting ext data after eof", c->self); else ssh_packet_disconnect(ssh, "Received extended_data " "after EOF on channel %d.", c->self); } if ((r = sshpkt_get_u32(ssh, &tcode)) != 0) { error_fr(r, "parse tcode"); ssh_packet_disconnect(ssh, "Invalid extended_data message"); } if (c->efd == -1 || c->extended_usage != CHAN_EXTENDED_WRITE || tcode != SSH2_EXTENDED_DATA_STDERR) { logit("channel %d: bad ext data", c->self); return 0; } if ((r = sshpkt_get_string_direct(ssh, &data, &data_len)) != 0 || (r = sshpkt_get_end(ssh)) != 0) { error_fr(r, "parse data"); ssh_packet_disconnect(ssh, "Invalid extended_data message"); } if (data_len > c->local_window) { logit("channel %d: rcvd too much extended_data %zu, win %u", c->self, data_len, c->local_window); return 0; } debug2("channel %d: rcvd ext data %zu", c->self, data_len); /* XXX sshpkt_getb? */ if ((r = sshbuf_put(c->extended, data, data_len)) != 0) error_fr(r, "append"); c->local_window -= data_len; return 0; } int channel_input_ieof(int type, u_int32_t seq, struct ssh *ssh) { Channel *c = channel_from_packet_id(ssh, __func__, "ieof"); int r; if ((r = sshpkt_get_end(ssh)) != 0) { error_fr(r, "parse data"); ssh_packet_disconnect(ssh, "Invalid ieof message"); } if (channel_proxy_upstream(c, type, seq, ssh)) return 0; chan_rcvd_ieof(ssh, c); /* XXX force input close */ if (c->force_drain && c->istate == CHAN_INPUT_OPEN) { debug("channel %d: FORCE input drain", c->self); c->istate = CHAN_INPUT_WAIT_DRAIN; if (sshbuf_len(c->input) == 0) chan_ibuf_empty(ssh, c); } return 0; } int channel_input_oclose(int type, u_int32_t seq, struct ssh *ssh) { Channel *c = channel_from_packet_id(ssh, __func__, "oclose"); int r; if (channel_proxy_upstream(c, type, seq, ssh)) return 0; if ((r = sshpkt_get_end(ssh)) != 0) { error_fr(r, "parse data"); ssh_packet_disconnect(ssh, "Invalid oclose message"); } chan_rcvd_oclose(ssh, c); return 0; } int channel_input_open_confirmation(int type, u_int32_t seq, struct ssh *ssh) { Channel *c = channel_from_packet_id(ssh, __func__, "open confirmation"); u_int32_t remote_window, remote_maxpacket; int r; if (channel_proxy_upstream(c, type, seq, ssh)) return 0; if (c->type != SSH_CHANNEL_OPENING) ssh_packet_disconnect(ssh, "Received open confirmation for " "non-opening channel %d.", c->self); /* * Record the remote channel number and mark that the channel * is now open. */ if ((r = sshpkt_get_u32(ssh, &c->remote_id)) != 0 || (r = sshpkt_get_u32(ssh, &remote_window)) != 0 || (r = sshpkt_get_u32(ssh, &remote_maxpacket)) != 0 || (r = sshpkt_get_end(ssh)) != 0) { error_fr(r, "window/maxpacket"); ssh_packet_disconnect(ssh, "Invalid open confirmation message"); } c->have_remote_id = 1; c->remote_window = remote_window; c->remote_maxpacket = remote_maxpacket; c->type = SSH_CHANNEL_OPEN; if (c->open_confirm) { debug2_f("channel %d: callback start", c->self); c->open_confirm(ssh, c->self, 1, c->open_confirm_ctx); debug2_f("channel %d: callback done", c->self); } c->lastused = monotime(); debug2("channel %d: open confirm rwindow %u rmax %u", c->self, c->remote_window, c->remote_maxpacket); return 0; } static char * reason2txt(int reason) { switch (reason) { case SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED: return "administratively prohibited"; case SSH2_OPEN_CONNECT_FAILED: return "connect failed"; case SSH2_OPEN_UNKNOWN_CHANNEL_TYPE: return "unknown channel type"; case SSH2_OPEN_RESOURCE_SHORTAGE: return "resource shortage"; } return "unknown reason"; } int channel_input_open_failure(int type, u_int32_t seq, struct ssh *ssh) { Channel *c = channel_from_packet_id(ssh, __func__, "open failure"); u_int32_t reason; char *msg = NULL; int r; if (channel_proxy_upstream(c, type, seq, ssh)) return 0; if (c->type != SSH_CHANNEL_OPENING) ssh_packet_disconnect(ssh, "Received open failure for " "non-opening channel %d.", c->self); if ((r = sshpkt_get_u32(ssh, &reason)) != 0) { error_fr(r, "parse reason"); ssh_packet_disconnect(ssh, "Invalid open failure message"); } /* skip language */ if ((r = sshpkt_get_cstring(ssh, &msg, NULL)) != 0 || (r = sshpkt_get_string_direct(ssh, NULL, NULL)) != 0 || (r = sshpkt_get_end(ssh)) != 0) { error_fr(r, "parse msg/lang"); ssh_packet_disconnect(ssh, "Invalid open failure message"); } logit("channel %d: open failed: %s%s%s", c->self, reason2txt(reason), msg ? ": ": "", msg ? msg : ""); free(msg); if (c->open_confirm) { debug2_f("channel %d: callback start", c->self); c->open_confirm(ssh, c->self, 0, c->open_confirm_ctx); debug2_f("channel %d: callback done", c->self); } /* Schedule the channel for cleanup/deletion. */ chan_mark_dead(ssh, c); return 0; } int channel_input_window_adjust(int type, u_int32_t seq, struct ssh *ssh) { int id = channel_parse_id(ssh, __func__, "window adjust"); Channel *c; u_int32_t adjust; u_int new_rwin; int r; if ((c = channel_lookup(ssh, id)) == NULL) { logit("Received window adjust for non-open channel %d.", id); return 0; } if (channel_proxy_upstream(c, type, seq, ssh)) return 0; if ((r = sshpkt_get_u32(ssh, &adjust)) != 0 || (r = sshpkt_get_end(ssh)) != 0) { error_fr(r, "parse adjust"); ssh_packet_disconnect(ssh, "Invalid window adjust message"); } debug2("channel %d: rcvd adjust %u", c->self, adjust); if ((new_rwin = c->remote_window + adjust) < c->remote_window) { fatal("channel %d: adjust %u overflows remote window %u", c->self, adjust, c->remote_window); } c->remote_window = new_rwin; return 0; } int channel_input_status_confirm(int type, u_int32_t seq, struct ssh *ssh) { int id = channel_parse_id(ssh, __func__, "status confirm"); Channel *c; struct channel_confirm *cc; /* Reset keepalive timeout */ ssh_packet_set_alive_timeouts(ssh, 0); debug2_f("type %d id %d", type, id); if ((c = channel_lookup(ssh, id)) == NULL) { logit_f("%d: unknown", id); return 0; } if (channel_proxy_upstream(c, type, seq, ssh)) return 0; if (sshpkt_get_end(ssh) != 0) ssh_packet_disconnect(ssh, "Invalid status confirm message"); if ((cc = TAILQ_FIRST(&c->status_confirms)) == NULL) return 0; cc->cb(ssh, type, c, cc->ctx); TAILQ_REMOVE(&c->status_confirms, cc, entry); freezero(cc, sizeof(*cc)); return 0; } /* -- tcp forwarding */ void channel_set_af(struct ssh *ssh, int af) { ssh->chanctxt->IPv4or6 = af; } /* * Determine whether or not a port forward listens to loopback, the * specified address or wildcard. On the client, a specified bind * address will always override gateway_ports. On the server, a * gateway_ports of 1 (``yes'') will override the client's specification * and force a wildcard bind, whereas a value of 2 (``clientspecified'') * will bind to whatever address the client asked for. * * Special-case listen_addrs are: * * "0.0.0.0" -> wildcard v4/v6 if SSH_OLD_FORWARD_ADDR * "" (empty string), "*" -> wildcard v4/v6 * "localhost" -> loopback v4/v6 * "127.0.0.1" / "::1" -> accepted even if gateway_ports isn't set */ static const char * channel_fwd_bind_addr(struct ssh *ssh, const char *listen_addr, int *wildcardp, int is_client, struct ForwardOptions *fwd_opts) { const char *addr = NULL; int wildcard = 0; if (listen_addr == NULL) { /* No address specified: default to gateway_ports setting */ if (fwd_opts->gateway_ports) wildcard = 1; } else if (fwd_opts->gateway_ports || is_client) { if (((ssh->compat & SSH_OLD_FORWARD_ADDR) && strcmp(listen_addr, "0.0.0.0") == 0 && is_client == 0) || *listen_addr == '\0' || strcmp(listen_addr, "*") == 0 || (!is_client && fwd_opts->gateway_ports == 1)) { wildcard = 1; /* * Notify client if they requested a specific listen * address and it was overridden. */ if (*listen_addr != '\0' && strcmp(listen_addr, "0.0.0.0") != 0 && strcmp(listen_addr, "*") != 0) { ssh_packet_send_debug(ssh, "Forwarding listen address " "\"%s\" overridden by server " "GatewayPorts", listen_addr); } } else if (strcmp(listen_addr, "localhost") != 0 || strcmp(listen_addr, "127.0.0.1") == 0 || strcmp(listen_addr, "::1") == 0) { /* * Accept explicit localhost address when * GatewayPorts=yes. The "localhost" hostname is * deliberately skipped here so it will listen on all * available local address families. */ addr = listen_addr; } } else if (strcmp(listen_addr, "127.0.0.1") == 0 || strcmp(listen_addr, "::1") == 0) { /* * If a specific IPv4/IPv6 localhost address has been * requested then accept it even if gateway_ports is in * effect. This allows the client to prefer IPv4 or IPv6. */ addr = listen_addr; } if (wildcardp != NULL) *wildcardp = wildcard; return addr; } static int channel_setup_fwd_listener_tcpip(struct ssh *ssh, int type, struct Forward *fwd, int *allocated_listen_port, struct ForwardOptions *fwd_opts) { Channel *c; int sock, r, success = 0, wildcard = 0, is_client; struct addrinfo hints, *ai, *aitop; const char *host, *addr; char ntop[NI_MAXHOST], strport[NI_MAXSERV]; in_port_t *lport_p; is_client = (type == SSH_CHANNEL_PORT_LISTENER); if (is_client && fwd->connect_path != NULL) { host = fwd->connect_path; } else { host = (type == SSH_CHANNEL_RPORT_LISTENER) ? fwd->listen_host : fwd->connect_host; if (host == NULL) { error("No forward host name."); return 0; } if (strlen(host) >= NI_MAXHOST) { error("Forward host name too long."); return 0; } } /* Determine the bind address, cf. channel_fwd_bind_addr() comment */ addr = channel_fwd_bind_addr(ssh, fwd->listen_host, &wildcard, is_client, fwd_opts); debug3_f("type %d wildcard %d addr %s", type, wildcard, (addr == NULL) ? "NULL" : addr); /* * getaddrinfo returns a loopback address if the hostname is * set to NULL and hints.ai_flags is not AI_PASSIVE */ memset(&hints, 0, sizeof(hints)); hints.ai_family = ssh->chanctxt->IPv4or6; hints.ai_flags = wildcard ? AI_PASSIVE : 0; hints.ai_socktype = SOCK_STREAM; snprintf(strport, sizeof strport, "%d", fwd->listen_port); if ((r = getaddrinfo(addr, strport, &hints, &aitop)) != 0) { if (addr == NULL) { /* This really shouldn't happen */ ssh_packet_disconnect(ssh, "getaddrinfo: fatal error: %s", ssh_gai_strerror(r)); } else { error_f("getaddrinfo(%.64s): %s", addr, ssh_gai_strerror(r)); } return 0; } if (allocated_listen_port != NULL) *allocated_listen_port = 0; for (ai = aitop; ai; ai = ai->ai_next) { switch (ai->ai_family) { case AF_INET: lport_p = &((struct sockaddr_in *)ai->ai_addr)-> sin_port; break; case AF_INET6: lport_p = &((struct sockaddr_in6 *)ai->ai_addr)-> sin6_port; break; default: continue; } /* * If allocating a port for -R forwards, then use the * same port for all address families. */ if (type == SSH_CHANNEL_RPORT_LISTENER && fwd->listen_port == 0 && allocated_listen_port != NULL && *allocated_listen_port > 0) *lport_p = htons(*allocated_listen_port); if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) { error_f("getnameinfo failed"); continue; } /* Create a port to listen for the host. */ sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (sock == -1) { /* this is no error since kernel may not support ipv6 */ verbose("socket [%s]:%s: %.100s", ntop, strport, strerror(errno)); continue; } set_reuseaddr(sock); if (ai->ai_family == AF_INET6) sock_set_v6only(sock); debug("Local forwarding listening on %s port %s.", ntop, strport); /* Bind the socket to the address. */ if (bind(sock, ai->ai_addr, ai->ai_addrlen) == -1) { /* * address can be in if use ipv6 address is * already bound */ if (!ai->ai_next) error("bind [%s]:%s: %.100s", ntop, strport, strerror(errno)); else verbose("bind [%s]:%s: %.100s", ntop, strport, strerror(errno)); close(sock); continue; } /* Start listening for connections on the socket. */ if (listen(sock, SSH_LISTEN_BACKLOG) == -1) { error("listen [%s]:%s: %.100s", ntop, strport, strerror(errno)); close(sock); continue; } /* * fwd->listen_port == 0 requests a dynamically allocated port - * record what we got. */ if (type == SSH_CHANNEL_RPORT_LISTENER && fwd->listen_port == 0 && allocated_listen_port != NULL && *allocated_listen_port == 0) { *allocated_listen_port = get_local_port(sock); debug("Allocated listen port %d", *allocated_listen_port); } /* Allocate a channel number for the socket. */ c = channel_new(ssh, "port-listener", type, sock, sock, -1, CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, "port listener", 1); c->path = xstrdup(host); c->host_port = fwd->connect_port; c->listening_addr = addr == NULL ? NULL : xstrdup(addr); if (fwd->listen_port == 0 && allocated_listen_port != NULL && !(ssh->compat & SSH_BUG_DYNAMIC_RPORT)) c->listening_port = *allocated_listen_port; else c->listening_port = fwd->listen_port; success = 1; } if (success == 0) error_f("cannot listen to port: %d", fwd->listen_port); freeaddrinfo(aitop); return success; } static int channel_setup_fwd_listener_streamlocal(struct ssh *ssh, int type, struct Forward *fwd, struct ForwardOptions *fwd_opts) { struct sockaddr_un sunaddr; const char *path; Channel *c; int port, sock; mode_t omask; switch (type) { case SSH_CHANNEL_UNIX_LISTENER: if (fwd->connect_path != NULL) { if (strlen(fwd->connect_path) > sizeof(sunaddr.sun_path)) { error("Local connecting path too long: %s", fwd->connect_path); return 0; } path = fwd->connect_path; port = PORT_STREAMLOCAL; } else { if (fwd->connect_host == NULL) { error("No forward host name."); return 0; } if (strlen(fwd->connect_host) >= NI_MAXHOST) { error("Forward host name too long."); return 0; } path = fwd->connect_host; port = fwd->connect_port; } break; case SSH_CHANNEL_RUNIX_LISTENER: path = fwd->listen_path; port = PORT_STREAMLOCAL; break; default: error_f("unexpected channel type %d", type); return 0; } if (fwd->listen_path == NULL) { error("No forward path name."); return 0; } if (strlen(fwd->listen_path) > sizeof(sunaddr.sun_path)) { error("Local listening path too long: %s", fwd->listen_path); return 0; } debug3_f("type %d path %s", type, fwd->listen_path); /* Start a Unix domain listener. */ omask = umask(fwd_opts->streamlocal_bind_mask); sock = unix_listener(fwd->listen_path, SSH_LISTEN_BACKLOG, fwd_opts->streamlocal_bind_unlink); umask(omask); if (sock < 0) return 0; debug("Local forwarding listening on path %s.", fwd->listen_path); /* Allocate a channel number for the socket. */ c = channel_new(ssh, "unix-listener", type, sock, sock, -1, CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, "unix listener", 1); c->path = xstrdup(path); c->host_port = port; c->listening_port = PORT_STREAMLOCAL; c->listening_addr = xstrdup(fwd->listen_path); return 1; } static int channel_cancel_rport_listener_tcpip(struct ssh *ssh, const char *host, u_short port) { u_int i; int found = 0; for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { Channel *c = ssh->chanctxt->channels[i]; if (c == NULL || c->type != SSH_CHANNEL_RPORT_LISTENER) continue; if (strcmp(c->path, host) == 0 && c->listening_port == port) { debug2_f("close channel %d", i); channel_free(ssh, c); found = 1; } } return found; } static int channel_cancel_rport_listener_streamlocal(struct ssh *ssh, const char *path) { u_int i; int found = 0; for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { Channel *c = ssh->chanctxt->channels[i]; if (c == NULL || c->type != SSH_CHANNEL_RUNIX_LISTENER) continue; if (c->path == NULL) continue; if (strcmp(c->path, path) == 0) { debug2_f("close channel %d", i); channel_free(ssh, c); found = 1; } } return found; } int channel_cancel_rport_listener(struct ssh *ssh, struct Forward *fwd) { if (fwd->listen_path != NULL) { return channel_cancel_rport_listener_streamlocal(ssh, fwd->listen_path); } else { return channel_cancel_rport_listener_tcpip(ssh, fwd->listen_host, fwd->listen_port); } } static int channel_cancel_lport_listener_tcpip(struct ssh *ssh, const char *lhost, u_short lport, int cport, struct ForwardOptions *fwd_opts) { u_int i; int found = 0; const char *addr = channel_fwd_bind_addr(ssh, lhost, NULL, 1, fwd_opts); for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { Channel *c = ssh->chanctxt->channels[i]; if (c == NULL || c->type != SSH_CHANNEL_PORT_LISTENER) continue; if (c->listening_port != lport) continue; if (cport == CHANNEL_CANCEL_PORT_STATIC) { /* skip dynamic forwardings */ if (c->host_port == 0) continue; } else { if (c->host_port != cport) continue; } if ((c->listening_addr == NULL && addr != NULL) || (c->listening_addr != NULL && addr == NULL)) continue; if (addr == NULL || strcmp(c->listening_addr, addr) == 0) { debug2_f("close channel %d", i); channel_free(ssh, c); found = 1; } } return found; } static int channel_cancel_lport_listener_streamlocal(struct ssh *ssh, const char *path) { u_int i; int found = 0; if (path == NULL) { error_f("no path specified."); return 0; } for (i = 0; i < ssh->chanctxt->channels_alloc; i++) { Channel *c = ssh->chanctxt->channels[i]; if (c == NULL || c->type != SSH_CHANNEL_UNIX_LISTENER) continue; if (c->listening_addr == NULL) continue; if (strcmp(c->listening_addr, path) == 0) { debug2_f("close channel %d", i); channel_free(ssh, c); found = 1; } } return found; } int channel_cancel_lport_listener(struct ssh *ssh, struct Forward *fwd, int cport, struct ForwardOptions *fwd_opts) { if (fwd->listen_path != NULL) { return channel_cancel_lport_listener_streamlocal(ssh, fwd->listen_path); } else { return channel_cancel_lport_listener_tcpip(ssh, fwd->listen_host, fwd->listen_port, cport, fwd_opts); } } /* protocol local port fwd, used by ssh */ int channel_setup_local_fwd_listener(struct ssh *ssh, struct Forward *fwd, struct ForwardOptions *fwd_opts) { if (fwd->listen_path != NULL) { return channel_setup_fwd_listener_streamlocal(ssh, SSH_CHANNEL_UNIX_LISTENER, fwd, fwd_opts); } else { return channel_setup_fwd_listener_tcpip(ssh, SSH_CHANNEL_PORT_LISTENER, fwd, NULL, fwd_opts); } } /* Matches a remote forwarding permission against a requested forwarding */ static int remote_open_match(struct permission *allowed_open, struct Forward *fwd) { int ret; char *lhost; /* XXX add ACLs for streamlocal */ if (fwd->listen_path != NULL) return 1; if (fwd->listen_host == NULL || allowed_open->listen_host == NULL) return 0; if (allowed_open->listen_port != FWD_PERMIT_ANY_PORT && allowed_open->listen_port != fwd->listen_port) return 0; /* Match hostnames case-insensitively */ lhost = xstrdup(fwd->listen_host); lowercase(lhost); ret = match_pattern(lhost, allowed_open->listen_host); free(lhost); return ret; } /* Checks whether a requested remote forwarding is permitted */ static int check_rfwd_permission(struct ssh *ssh, struct Forward *fwd) { struct ssh_channels *sc = ssh->chanctxt; struct permission_set *pset = &sc->remote_perms; u_int i, permit, permit_adm = 1; struct permission *perm; /* XXX apply GatewayPorts override before checking? */ permit = pset->all_permitted; if (!permit) { for (i = 0; i < pset->num_permitted_user; i++) { perm = &pset->permitted_user[i]; if (remote_open_match(perm, fwd)) { permit = 1; break; } } } if (pset->num_permitted_admin > 0) { permit_adm = 0; for (i = 0; i < pset->num_permitted_admin; i++) { perm = &pset->permitted_admin[i]; if (remote_open_match(perm, fwd)) { permit_adm = 1; break; } } } return permit && permit_adm; } /* protocol v2 remote port fwd, used by sshd */ int channel_setup_remote_fwd_listener(struct ssh *ssh, struct Forward *fwd, int *allocated_listen_port, struct ForwardOptions *fwd_opts) { if (!check_rfwd_permission(ssh, fwd)) { ssh_packet_send_debug(ssh, "port forwarding refused"); if (fwd->listen_path != NULL) /* XXX always allowed, see remote_open_match() */ logit("Received request from %.100s port %d to " "remote forward to path \"%.100s\", " "but the request was denied.", ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), fwd->listen_path); else if(fwd->listen_host != NULL) logit("Received request from %.100s port %d to " "remote forward to host %.100s port %d, " "but the request was denied.", ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), fwd->listen_host, fwd->listen_port ); else logit("Received request from %.100s port %d to remote " "forward, but the request was denied.", ssh_remote_ipaddr(ssh), ssh_remote_port(ssh)); return 0; } if (fwd->listen_path != NULL) { return channel_setup_fwd_listener_streamlocal(ssh, SSH_CHANNEL_RUNIX_LISTENER, fwd, fwd_opts); } else { return channel_setup_fwd_listener_tcpip(ssh, SSH_CHANNEL_RPORT_LISTENER, fwd, allocated_listen_port, fwd_opts); } } /* * Translate the requested rfwd listen host to something usable for * this server. */ static const char * channel_rfwd_bind_host(const char *listen_host) { if (listen_host == NULL) { return "localhost"; } else if (*listen_host == '\0' || strcmp(listen_host, "*") == 0) { return ""; } else return listen_host; } /* * Initiate forwarding of connections to port "port" on remote host through * the secure channel to host:port from local side. * Returns handle (index) for updating the dynamic listen port with * channel_update_permission(). */ int channel_request_remote_forwarding(struct ssh *ssh, struct Forward *fwd) { int r, success = 0, idx = -1; const char *host_to_connect, *listen_host, *listen_path; int port_to_connect, listen_port; /* Send the forward request to the remote side. */ if (fwd->listen_path != NULL) { if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || (r = sshpkt_put_cstring(ssh, "streamlocal-forward@openssh.com")) != 0 || (r = sshpkt_put_u8(ssh, 1)) != 0 || /* want reply */ (r = sshpkt_put_cstring(ssh, fwd->listen_path)) != 0 || (r = sshpkt_send(ssh)) != 0 || (r = ssh_packet_write_wait(ssh)) != 0) fatal_fr(r, "request streamlocal"); } else { if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || (r = sshpkt_put_cstring(ssh, "tcpip-forward")) != 0 || (r = sshpkt_put_u8(ssh, 1)) != 0 || /* want reply */ (r = sshpkt_put_cstring(ssh, channel_rfwd_bind_host(fwd->listen_host))) != 0 || (r = sshpkt_put_u32(ssh, fwd->listen_port)) != 0 || (r = sshpkt_send(ssh)) != 0 || (r = ssh_packet_write_wait(ssh)) != 0) fatal_fr(r, "request tcpip-forward"); } /* Assume that server accepts the request */ success = 1; if (success) { /* Record that connection to this host/port is permitted. */ host_to_connect = listen_host = listen_path = NULL; port_to_connect = listen_port = 0; if (fwd->connect_path != NULL) { host_to_connect = fwd->connect_path; port_to_connect = PORT_STREAMLOCAL; } else { host_to_connect = fwd->connect_host; port_to_connect = fwd->connect_port; } if (fwd->listen_path != NULL) { listen_path = fwd->listen_path; listen_port = PORT_STREAMLOCAL; } else { listen_host = fwd->listen_host; listen_port = fwd->listen_port; } idx = permission_set_add(ssh, FORWARD_USER, FORWARD_LOCAL, host_to_connect, port_to_connect, listen_host, listen_path, listen_port, NULL); } return idx; } static int open_match(struct permission *allowed_open, const char *requestedhost, int requestedport) { if (allowed_open->host_to_connect == NULL) return 0; if (allowed_open->port_to_connect != FWD_PERMIT_ANY_PORT && allowed_open->port_to_connect != requestedport) return 0; if (strcmp(allowed_open->host_to_connect, FWD_PERMIT_ANY_HOST) != 0 && strcmp(allowed_open->host_to_connect, requestedhost) != 0) return 0; return 1; } /* * Note that in the listen host/port case * we don't support FWD_PERMIT_ANY_PORT and * need to translate between the configured-host (listen_host) * and what we've sent to the remote server (channel_rfwd_bind_host) */ static int open_listen_match_tcpip(struct permission *allowed_open, const char *requestedhost, u_short requestedport, int translate) { const char *allowed_host; if (allowed_open->host_to_connect == NULL) return 0; if (allowed_open->listen_port != requestedport) return 0; if (!translate && allowed_open->listen_host == NULL && requestedhost == NULL) return 1; allowed_host = translate ? channel_rfwd_bind_host(allowed_open->listen_host) : allowed_open->listen_host; if (allowed_host == NULL || requestedhost == NULL || strcmp(allowed_host, requestedhost) != 0) return 0; return 1; } static int open_listen_match_streamlocal(struct permission *allowed_open, const char *requestedpath) { if (allowed_open->host_to_connect == NULL) return 0; if (allowed_open->listen_port != PORT_STREAMLOCAL) return 0; if (allowed_open->listen_path == NULL || strcmp(allowed_open->listen_path, requestedpath) != 0) return 0; return 1; } /* * Request cancellation of remote forwarding of connection host:port from * local side. */ static int channel_request_rforward_cancel_tcpip(struct ssh *ssh, const char *host, u_short port) { struct ssh_channels *sc = ssh->chanctxt; struct permission_set *pset = &sc->local_perms; int r; u_int i; struct permission *perm = NULL; for (i = 0; i < pset->num_permitted_user; i++) { perm = &pset->permitted_user[i]; if (open_listen_match_tcpip(perm, host, port, 0)) break; perm = NULL; } if (perm == NULL) { debug_f("requested forward not found"); return -1; } if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || (r = sshpkt_put_cstring(ssh, "cancel-tcpip-forward")) != 0 || (r = sshpkt_put_u8(ssh, 0)) != 0 || /* want reply */ (r = sshpkt_put_cstring(ssh, channel_rfwd_bind_host(host))) != 0 || (r = sshpkt_put_u32(ssh, port)) != 0 || (r = sshpkt_send(ssh)) != 0) fatal_fr(r, "send cancel"); fwd_perm_clear(perm); /* unregister */ return 0; } /* * Request cancellation of remote forwarding of Unix domain socket * path from local side. */ static int channel_request_rforward_cancel_streamlocal(struct ssh *ssh, const char *path) { struct ssh_channels *sc = ssh->chanctxt; struct permission_set *pset = &sc->local_perms; int r; u_int i; struct permission *perm = NULL; for (i = 0; i < pset->num_permitted_user; i++) { perm = &pset->permitted_user[i]; if (open_listen_match_streamlocal(perm, path)) break; perm = NULL; } if (perm == NULL) { debug_f("requested forward not found"); return -1; } if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || (r = sshpkt_put_cstring(ssh, "cancel-streamlocal-forward@openssh.com")) != 0 || (r = sshpkt_put_u8(ssh, 0)) != 0 || /* want reply */ (r = sshpkt_put_cstring(ssh, path)) != 0 || (r = sshpkt_send(ssh)) != 0) fatal_fr(r, "send cancel"); fwd_perm_clear(perm); /* unregister */ return 0; } /* * Request cancellation of remote forwarding of a connection from local side. */ int channel_request_rforward_cancel(struct ssh *ssh, struct Forward *fwd) { if (fwd->listen_path != NULL) { return channel_request_rforward_cancel_streamlocal(ssh, fwd->listen_path); } else { return channel_request_rforward_cancel_tcpip(ssh, fwd->listen_host, fwd->listen_port ? fwd->listen_port : fwd->allocated_port); } } /* * Permits opening to any host/port if permitted_user[] is empty. This is * usually called by the server, because the user could connect to any port * anyway, and the server has no way to know but to trust the client anyway. */ void channel_permit_all(struct ssh *ssh, int where) { struct permission_set *pset = permission_set_get(ssh, where); if (pset->num_permitted_user == 0) pset->all_permitted = 1; } /* * Permit the specified host/port for forwarding. */ void channel_add_permission(struct ssh *ssh, int who, int where, char *host, int port) { int local = where == FORWARD_LOCAL; struct permission_set *pset = permission_set_get(ssh, where); debug("allow %s forwarding to host %s port %d", fwd_ident(who, where), host, port); /* * Remote forwards set listen_host/port, local forwards set * host/port_to_connect. */ permission_set_add(ssh, who, where, local ? host : 0, local ? port : 0, local ? NULL : host, NULL, local ? 0 : port, NULL); pset->all_permitted = 0; } /* * Administratively disable forwarding. */ void channel_disable_admin(struct ssh *ssh, int where) { channel_clear_permission(ssh, FORWARD_ADM, where); permission_set_add(ssh, FORWARD_ADM, where, NULL, 0, NULL, NULL, 0, NULL); } /* * Clear a list of permitted opens. */ void channel_clear_permission(struct ssh *ssh, int who, int where) { struct permission **permp; u_int *npermp; permission_set_get_array(ssh, who, where, &permp, &npermp); *permp = xrecallocarray(*permp, *npermp, 0, sizeof(**permp)); *npermp = 0; } /* * Update the listen port for a dynamic remote forward, after * the actual 'newport' has been allocated. If 'newport' < 0 is * passed then they entry will be invalidated. */ void channel_update_permission(struct ssh *ssh, int idx, int newport) { struct permission_set *pset = &ssh->chanctxt->local_perms; if (idx < 0 || (u_int)idx >= pset->num_permitted_user) { debug_f("index out of range: %d num_permitted_user %d", idx, pset->num_permitted_user); return; } debug("%s allowed port %d for forwarding to host %s port %d", newport > 0 ? "Updating" : "Removing", newport, pset->permitted_user[idx].host_to_connect, pset->permitted_user[idx].port_to_connect); if (newport <= 0) fwd_perm_clear(&pset->permitted_user[idx]); else { pset->permitted_user[idx].listen_port = (ssh->compat & SSH_BUG_DYNAMIC_RPORT) ? 0 : newport; } } /* returns port number, FWD_PERMIT_ANY_PORT or -1 on error */ int permitopen_port(const char *p) { int port; if (strcmp(p, "*") == 0) return FWD_PERMIT_ANY_PORT; if ((port = a2port(p)) > 0) return port; return -1; } /* Try to start non-blocking connect to next host in cctx list */ static int connect_next(struct channel_connect *cctx) { int sock, saved_errno; struct sockaddr_un *sunaddr; char ntop[NI_MAXHOST]; char strport[MAXIMUM(NI_MAXSERV, sizeof(sunaddr->sun_path))]; for (; cctx->ai; cctx->ai = cctx->ai->ai_next) { switch (cctx->ai->ai_family) { case AF_UNIX: /* unix:pathname instead of host:port */ sunaddr = (struct sockaddr_un *)cctx->ai->ai_addr; strlcpy(ntop, "unix", sizeof(ntop)); strlcpy(strport, sunaddr->sun_path, sizeof(strport)); break; case AF_INET: case AF_INET6: if (getnameinfo(cctx->ai->ai_addr, cctx->ai->ai_addrlen, ntop, sizeof(ntop), strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) { error_f("getnameinfo failed"); continue; } break; default: continue; } debug_f("start for host %.100s ([%.100s]:%s)", cctx->host, ntop, strport); if ((sock = socket(cctx->ai->ai_family, cctx->ai->ai_socktype, cctx->ai->ai_protocol)) == -1) { if (cctx->ai->ai_next == NULL) error("socket: %.100s", strerror(errno)); else verbose("socket: %.100s", strerror(errno)); continue; } if (set_nonblock(sock) == -1) fatal_f("set_nonblock(%d)", sock); if (connect(sock, cctx->ai->ai_addr, cctx->ai->ai_addrlen) == -1 && errno != EINPROGRESS) { debug_f("host %.100s ([%.100s]:%s): %.100s", cctx->host, ntop, strport, strerror(errno)); saved_errno = errno; close(sock); errno = saved_errno; continue; /* fail -- try next */ } if (cctx->ai->ai_family != AF_UNIX) set_nodelay(sock); debug_f("connect host %.100s ([%.100s]:%s) in progress, fd=%d", cctx->host, ntop, strport, sock); cctx->ai = cctx->ai->ai_next; return sock; } return -1; } static void channel_connect_ctx_free(struct channel_connect *cctx) { free(cctx->host); if (cctx->aitop) { if (cctx->aitop->ai_family == AF_UNIX) free(cctx->aitop); else freeaddrinfo(cctx->aitop); } memset(cctx, 0, sizeof(*cctx)); } /* * Return connecting socket to remote host:port or local socket path, * passing back the failure reason if appropriate. */ static int connect_to_helper(struct ssh *ssh, const char *name, int port, int socktype, char *ctype, char *rname, struct channel_connect *cctx, int *reason, const char **errmsg) { struct addrinfo hints; int gaierr; int sock = -1; char strport[NI_MAXSERV]; if (port == PORT_STREAMLOCAL) { struct sockaddr_un *sunaddr; struct addrinfo *ai; if (strlen(name) > sizeof(sunaddr->sun_path)) { error("%.100s: %.100s", name, strerror(ENAMETOOLONG)); return -1; } /* * Fake up a struct addrinfo for AF_UNIX connections. * channel_connect_ctx_free() must check ai_family * and use free() not freeaddirinfo() for AF_UNIX. */ ai = xmalloc(sizeof(*ai) + sizeof(*sunaddr)); memset(ai, 0, sizeof(*ai) + sizeof(*sunaddr)); ai->ai_addr = (struct sockaddr *)(ai + 1); ai->ai_addrlen = sizeof(*sunaddr); ai->ai_family = AF_UNIX; ai->ai_socktype = socktype; ai->ai_protocol = PF_UNSPEC; sunaddr = (struct sockaddr_un *)ai->ai_addr; sunaddr->sun_family = AF_UNIX; strlcpy(sunaddr->sun_path, name, sizeof(sunaddr->sun_path)); cctx->aitop = ai; } else { memset(&hints, 0, sizeof(hints)); hints.ai_family = ssh->chanctxt->IPv4or6; hints.ai_socktype = socktype; snprintf(strport, sizeof strport, "%d", port); if ((gaierr = getaddrinfo(name, strport, &hints, &cctx->aitop)) != 0) { if (errmsg != NULL) *errmsg = ssh_gai_strerror(gaierr); if (reason != NULL) *reason = SSH2_OPEN_CONNECT_FAILED; error("connect_to %.100s: unknown host (%s)", name, ssh_gai_strerror(gaierr)); return -1; } } cctx->host = xstrdup(name); cctx->port = port; cctx->ai = cctx->aitop; if ((sock = connect_next(cctx)) == -1) { error("connect to %.100s port %d failed: %s", name, port, strerror(errno)); return -1; } return sock; } /* Return CONNECTING channel to remote host:port or local socket path */ static Channel * connect_to(struct ssh *ssh, const char *host, int port, char *ctype, char *rname) { struct channel_connect cctx; Channel *c; int sock; memset(&cctx, 0, sizeof(cctx)); sock = connect_to_helper(ssh, host, port, SOCK_STREAM, ctype, rname, &cctx, NULL, NULL); if (sock == -1) { channel_connect_ctx_free(&cctx); return NULL; } c = channel_new(ssh, ctype, SSH_CHANNEL_CONNECTING, sock, sock, -1, CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, rname, 1); c->host_port = port; c->path = xstrdup(host); c->connect_ctx = cctx; return c; } /* * returns either the newly connected channel or the downstream channel * that needs to deal with this connection. */ Channel * channel_connect_by_listen_address(struct ssh *ssh, const char *listen_host, u_short listen_port, char *ctype, char *rname) { struct ssh_channels *sc = ssh->chanctxt; struct permission_set *pset = &sc->local_perms; u_int i; struct permission *perm; for (i = 0; i < pset->num_permitted_user; i++) { perm = &pset->permitted_user[i]; if (open_listen_match_tcpip(perm, listen_host, listen_port, 1)) { if (perm->downstream) return perm->downstream; if (perm->port_to_connect == 0) return rdynamic_connect_prepare(ssh, ctype, rname); return connect_to(ssh, perm->host_to_connect, perm->port_to_connect, ctype, rname); } } error("WARNING: Server requests forwarding for unknown listen_port %d", listen_port); return NULL; } Channel * channel_connect_by_listen_path(struct ssh *ssh, const char *path, char *ctype, char *rname) { struct ssh_channels *sc = ssh->chanctxt; struct permission_set *pset = &sc->local_perms; u_int i; struct permission *perm; for (i = 0; i < pset->num_permitted_user; i++) { perm = &pset->permitted_user[i]; if (open_listen_match_streamlocal(perm, path)) { return connect_to(ssh, perm->host_to_connect, perm->port_to_connect, ctype, rname); } } error("WARNING: Server requests forwarding for unknown path %.100s", path); return NULL; } /* Check if connecting to that port is permitted and connect. */ Channel * channel_connect_to_port(struct ssh *ssh, const char *host, u_short port, char *ctype, char *rname, int *reason, const char **errmsg) { struct ssh_channels *sc = ssh->chanctxt; struct permission_set *pset = &sc->local_perms; struct channel_connect cctx; Channel *c; u_int i, permit, permit_adm = 1; int sock; struct permission *perm; permit = pset->all_permitted; if (!permit) { for (i = 0; i < pset->num_permitted_user; i++) { perm = &pset->permitted_user[i]; if (open_match(perm, host, port)) { permit = 1; break; } } } if (pset->num_permitted_admin > 0) { permit_adm = 0; for (i = 0; i < pset->num_permitted_admin; i++) { perm = &pset->permitted_admin[i]; if (open_match(perm, host, port)) { permit_adm = 1; break; } } } if (!permit || !permit_adm) { logit("Received request from %.100s port %d to connect to " "host %.100s port %d, but the request was denied.", ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), host, port); if (reason != NULL) *reason = SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED; return NULL; } memset(&cctx, 0, sizeof(cctx)); sock = connect_to_helper(ssh, host, port, SOCK_STREAM, ctype, rname, &cctx, reason, errmsg); if (sock == -1) { channel_connect_ctx_free(&cctx); return NULL; } c = channel_new(ssh, ctype, SSH_CHANNEL_CONNECTING, sock, sock, -1, CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, rname, 1); c->host_port = port; c->path = xstrdup(host); c->connect_ctx = cctx; return c; } /* Check if connecting to that path is permitted and connect. */ Channel * channel_connect_to_path(struct ssh *ssh, const char *path, char *ctype, char *rname) { struct ssh_channels *sc = ssh->chanctxt; struct permission_set *pset = &sc->local_perms; u_int i, permit, permit_adm = 1; struct permission *perm; permit = pset->all_permitted; if (!permit) { for (i = 0; i < pset->num_permitted_user; i++) { perm = &pset->permitted_user[i]; if (open_match(perm, path, PORT_STREAMLOCAL)) { permit = 1; break; } } } if (pset->num_permitted_admin > 0) { permit_adm = 0; for (i = 0; i < pset->num_permitted_admin; i++) { perm = &pset->permitted_admin[i]; if (open_match(perm, path, PORT_STREAMLOCAL)) { permit_adm = 1; break; } } } if (!permit || !permit_adm) { logit("Received request to connect to path %.100s, " "but the request was denied.", path); return NULL; } return connect_to(ssh, path, PORT_STREAMLOCAL, ctype, rname); } void channel_send_window_changes(struct ssh *ssh) { struct ssh_channels *sc = ssh->chanctxt; struct winsize ws; int r; u_int i; for (i = 0; i < sc->channels_alloc; i++) { if (sc->channels[i] == NULL || !sc->channels[i]->client_tty || sc->channels[i]->type != SSH_CHANNEL_OPEN) continue; if (ioctl(sc->channels[i]->rfd, TIOCGWINSZ, &ws) == -1) continue; channel_request_start(ssh, i, "window-change", 0); if ((r = sshpkt_put_u32(ssh, (u_int)ws.ws_col)) != 0 || (r = sshpkt_put_u32(ssh, (u_int)ws.ws_row)) != 0 || (r = sshpkt_put_u32(ssh, (u_int)ws.ws_xpixel)) != 0 || (r = sshpkt_put_u32(ssh, (u_int)ws.ws_ypixel)) != 0 || (r = sshpkt_send(ssh)) != 0) fatal_fr(r, "channel %u; send window-change", i); } } /* Return RDYNAMIC_OPEN channel: channel allows SOCKS, but is not connected */ static Channel * rdynamic_connect_prepare(struct ssh *ssh, char *ctype, char *rname) { Channel *c; int r; c = channel_new(ssh, ctype, SSH_CHANNEL_RDYNAMIC_OPEN, -1, -1, -1, CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, rname, 1); c->host_port = 0; c->path = NULL; /* * We need to open the channel before we have a FD, * so that we can get SOCKS header from peer. */ if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_OPEN_CONFIRMATION)) != 0 || (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || (r = sshpkt_put_u32(ssh, c->self)) != 0 || (r = sshpkt_put_u32(ssh, c->local_window)) != 0 || (r = sshpkt_put_u32(ssh, c->local_maxpacket)) != 0) fatal_fr(r, "channel %i; confirm", c->self); return c; } /* Return CONNECTING socket to remote host:port or local socket path */ static int rdynamic_connect_finish(struct ssh *ssh, Channel *c) { struct ssh_channels *sc = ssh->chanctxt; struct permission_set *pset = &sc->local_perms; struct permission *perm; struct channel_connect cctx; u_int i, permit_adm = 1; int sock; if (pset->num_permitted_admin > 0) { permit_adm = 0; for (i = 0; i < pset->num_permitted_admin; i++) { perm = &pset->permitted_admin[i]; if (open_match(perm, c->path, c->host_port)) { permit_adm = 1; break; } } } if (!permit_adm) { debug_f("requested forward not permitted"); return -1; } memset(&cctx, 0, sizeof(cctx)); sock = connect_to_helper(ssh, c->path, c->host_port, SOCK_STREAM, NULL, NULL, &cctx, NULL, NULL); if (sock == -1) channel_connect_ctx_free(&cctx); else { /* similar to SSH_CHANNEL_CONNECTING but we've already sent the open */ c->type = SSH_CHANNEL_RDYNAMIC_FINISH; c->connect_ctx = cctx; channel_register_fds(ssh, c, sock, sock, -1, 0, 1, 0); } return sock; } /* -- X11 forwarding */ /* * Creates an internet domain socket for listening for X11 connections. * Returns 0 and a suitable display number for the DISPLAY variable * stored in display_numberp , or -1 if an error occurs. */ int x11_create_display_inet(struct ssh *ssh, int x11_display_offset, int x11_use_localhost, int single_connection, u_int *display_numberp, int **chanids) { Channel *nc = NULL; int display_number, sock; u_short port; struct addrinfo hints, *ai, *aitop; char strport[NI_MAXSERV]; int gaierr, n, num_socks = 0, socks[NUM_SOCKS]; if (chanids == NULL) return -1; for (display_number = x11_display_offset; display_number < MAX_DISPLAYS; display_number++) { port = 6000 + display_number; memset(&hints, 0, sizeof(hints)); hints.ai_family = ssh->chanctxt->IPv4or6; hints.ai_flags = x11_use_localhost ? 0: AI_PASSIVE; hints.ai_socktype = SOCK_STREAM; snprintf(strport, sizeof strport, "%d", port); if ((gaierr = getaddrinfo(NULL, strport, &hints, &aitop)) != 0) { error("getaddrinfo: %.100s", ssh_gai_strerror(gaierr)); return -1; } for (ai = aitop; ai; ai = ai->ai_next) { if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) continue; sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (sock == -1) { if ((errno != EINVAL) && (errno != EAFNOSUPPORT) #ifdef EPFNOSUPPORT && (errno != EPFNOSUPPORT) #endif ) { error("socket: %.100s", strerror(errno)); freeaddrinfo(aitop); return -1; } else { debug("x11_create_display_inet: Socket family %d not supported", ai->ai_family); continue; } } if (ai->ai_family == AF_INET6) sock_set_v6only(sock); if (x11_use_localhost) set_reuseaddr(sock); if (bind(sock, ai->ai_addr, ai->ai_addrlen) == -1) { debug2_f("bind port %d: %.100s", port, strerror(errno)); close(sock); for (n = 0; n < num_socks; n++) close(socks[n]); num_socks = 0; break; } socks[num_socks++] = sock; if (num_socks == NUM_SOCKS) break; } freeaddrinfo(aitop); if (num_socks > 0) break; } if (display_number >= MAX_DISPLAYS) { error("Failed to allocate internet-domain X11 display socket."); return -1; } /* Start listening for connections on the socket. */ for (n = 0; n < num_socks; n++) { sock = socks[n]; if (listen(sock, SSH_LISTEN_BACKLOG) == -1) { error("listen: %.100s", strerror(errno)); close(sock); return -1; } } /* Allocate a channel for each socket. */ *chanids = xcalloc(num_socks + 1, sizeof(**chanids)); for (n = 0; n < num_socks; n++) { sock = socks[n]; nc = channel_new(ssh, "x11-listener", SSH_CHANNEL_X11_LISTENER, sock, sock, -1, CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 0, "X11 inet listener", 1); nc->single_connection = single_connection; (*chanids)[n] = nc->self; } (*chanids)[n] = -1; /* Return the display number for the DISPLAY environment variable. */ *display_numberp = display_number; return 0; } static int connect_local_xsocket_path(const char *pathname) { int sock; struct sockaddr_un addr; sock = socket(AF_UNIX, SOCK_STREAM, 0); if (sock == -1) { error("socket: %.100s", strerror(errno)); return -1; } memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; strlcpy(addr.sun_path, pathname, sizeof addr.sun_path); if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == 0) return sock; close(sock); error("connect %.100s: %.100s", addr.sun_path, strerror(errno)); return -1; } static int connect_local_xsocket(u_int dnr) { char buf[1024]; snprintf(buf, sizeof buf, _PATH_UNIX_X, dnr); return connect_local_xsocket_path(buf); } #ifdef __APPLE__ static int is_path_to_xsocket(const char *display, char *path, size_t pathlen) { struct stat sbuf; if (strlcpy(path, display, pathlen) >= pathlen) { error("%s: display path too long", __func__); return 0; } if (display[0] != '/') return 0; if (stat(path, &sbuf) == 0) { return 1; } else { char *dot = strrchr(path, '.'); if (dot != NULL) { *dot = '\0'; if (stat(path, &sbuf) == 0) { return 1; } } } return 0; } #endif int x11_connect_display(struct ssh *ssh) { u_int display_number; const char *display; char buf[1024], *cp; struct addrinfo hints, *ai, *aitop; char strport[NI_MAXSERV]; int gaierr, sock = 0; /* Try to open a socket for the local X server. */ display = getenv("DISPLAY"); if (!display) { error("DISPLAY not set."); return -1; } /* * Now we decode the value of the DISPLAY variable and make a * connection to the real X server. */ #ifdef __APPLE__ /* Check if display is a path to a socket (as set by launchd). */ { char path[PATH_MAX]; if (is_path_to_xsocket(display, path, sizeof(path))) { debug("x11_connect_display: $DISPLAY is launchd"); /* Create a socket. */ sock = connect_local_xsocket_path(path); if (sock < 0) return -1; /* OK, we now have a connection to the display. */ return sock; } } #endif /* * Check if it is a unix domain socket. Unix domain displays are in * one of the following formats: unix:d[.s], :d[.s], ::d[.s] */ if (strncmp(display, "unix:", 5) == 0 || display[0] == ':') { /* Connect to the unix domain socket. */ if (sscanf(strrchr(display, ':') + 1, "%u", &display_number) != 1) { error("Could not parse display number from DISPLAY: " "%.100s", display); return -1; } /* Create a socket. */ sock = connect_local_xsocket(display_number); if (sock < 0) return -1; /* OK, we now have a connection to the display. */ return sock; } /* * Connect to an inet socket. The DISPLAY value is supposedly * hostname:d[.s], where hostname may also be numeric IP address. */ strlcpy(buf, display, sizeof(buf)); cp = strchr(buf, ':'); if (!cp) { error("Could not find ':' in DISPLAY: %.100s", display); return -1; } *cp = 0; /* * buf now contains the host name. But first we parse the * display number. */ if (sscanf(cp + 1, "%u", &display_number) != 1) { error("Could not parse display number from DISPLAY: %.100s", display); return -1; } /* Look up the host address */ memset(&hints, 0, sizeof(hints)); hints.ai_family = ssh->chanctxt->IPv4or6; hints.ai_socktype = SOCK_STREAM; snprintf(strport, sizeof strport, "%u", 6000 + display_number); if ((gaierr = getaddrinfo(buf, strport, &hints, &aitop)) != 0) { error("%.100s: unknown host. (%s)", buf, ssh_gai_strerror(gaierr)); return -1; } for (ai = aitop; ai; ai = ai->ai_next) { /* Create a socket. */ sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (sock == -1) { debug2("socket: %.100s", strerror(errno)); continue; } /* Connect it to the display. */ if (connect(sock, ai->ai_addr, ai->ai_addrlen) == -1) { debug2("connect %.100s port %u: %.100s", buf, 6000 + display_number, strerror(errno)); close(sock); continue; } /* Success */ break; } freeaddrinfo(aitop); if (!ai) { error("connect %.100s port %u: %.100s", buf, 6000 + display_number, strerror(errno)); return -1; } set_nodelay(sock); return sock; } /* * Requests forwarding of X11 connections, generates fake authentication * data, and enables authentication spoofing. * This should be called in the client only. */ void x11_request_forwarding_with_spoofing(struct ssh *ssh, int client_session_id, const char *disp, const char *proto, const char *data, int want_reply) { struct ssh_channels *sc = ssh->chanctxt; u_int data_len = (u_int) strlen(data) / 2; u_int i, value; const char *cp; char *new_data; int r, screen_number; if (sc->x11_saved_display == NULL) sc->x11_saved_display = xstrdup(disp); else if (strcmp(disp, sc->x11_saved_display) != 0) { error("x11_request_forwarding_with_spoofing: different " "$DISPLAY already forwarded"); return; } cp = strchr(disp, ':'); if (cp) cp = strchr(cp, '.'); if (cp) screen_number = (u_int)strtonum(cp + 1, 0, 400, NULL); else screen_number = 0; if (sc->x11_saved_proto == NULL) { /* Save protocol name. */ sc->x11_saved_proto = xstrdup(proto); /* Extract real authentication data. */ sc->x11_saved_data = xmalloc(data_len); for (i = 0; i < data_len; i++) { if (sscanf(data + 2 * i, "%2x", &value) != 1) { fatal("x11_request_forwarding: bad " "authentication data: %.100s", data); } sc->x11_saved_data[i] = value; } sc->x11_saved_data_len = data_len; /* Generate fake data of the same length. */ sc->x11_fake_data = xmalloc(data_len); arc4random_buf(sc->x11_fake_data, data_len); sc->x11_fake_data_len = data_len; } /* Convert the fake data into hex. */ new_data = tohex(sc->x11_fake_data, data_len); /* Send the request packet. */ channel_request_start(ssh, client_session_id, "x11-req", want_reply); if ((r = sshpkt_put_u8(ssh, 0)) != 0 || /* bool: single connection */ (r = sshpkt_put_cstring(ssh, proto)) != 0 || (r = sshpkt_put_cstring(ssh, new_data)) != 0 || (r = sshpkt_put_u32(ssh, screen_number)) != 0 || (r = sshpkt_send(ssh)) != 0 || (r = ssh_packet_write_wait(ssh)) != 0) fatal_fr(r, "send x11-req"); free(new_data); } diff --git a/channels.h b/channels.h index 7afba78377e0..58019a84e71c 100644 --- a/channels.h +++ b/channels.h @@ -1,399 +1,399 @@ -/* $OpenBSD: channels.h,v 1.151 2023/07/04 03:59:21 dlg Exp $ */ +/* $OpenBSD: channels.h,v 1.152 2023/09/04 00:01:46 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". */ /* * Copyright (c) 1999, 2000, 2001, 2002 Markus Friedl. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef CHANNEL_H #define CHANNEL_H /* Definitions for channel types. */ #define SSH_CHANNEL_X11_LISTENER 1 /* Listening for inet X11 conn. */ #define SSH_CHANNEL_PORT_LISTENER 2 /* Listening on a port. */ #define SSH_CHANNEL_OPENING 3 /* waiting for confirmation */ #define SSH_CHANNEL_OPEN 4 /* normal open two-way channel */ #define SSH_CHANNEL_CLOSED 5 /* waiting for close confirmation */ #define SSH_CHANNEL_AUTH_SOCKET 6 /* authentication socket */ #define SSH_CHANNEL_X11_OPEN 7 /* reading first X11 packet */ #define SSH_CHANNEL_LARVAL 10 /* larval session */ #define SSH_CHANNEL_RPORT_LISTENER 11 /* Listening to a R-style port */ #define SSH_CHANNEL_CONNECTING 12 #define SSH_CHANNEL_DYNAMIC 13 #define SSH_CHANNEL_ZOMBIE 14 /* Almost dead. */ #define SSH_CHANNEL_MUX_LISTENER 15 /* Listener for mux conn. */ #define SSH_CHANNEL_MUX_CLIENT 16 /* Conn. to mux client */ #define SSH_CHANNEL_ABANDONED 17 /* Abandoned session, eg mux */ #define SSH_CHANNEL_UNIX_LISTENER 18 /* Listening on a domain socket. */ #define SSH_CHANNEL_RUNIX_LISTENER 19 /* Listening to a R-style domain socket. */ #define SSH_CHANNEL_MUX_PROXY 20 /* proxy channel for mux-client */ #define SSH_CHANNEL_RDYNAMIC_OPEN 21 /* reverse SOCKS, parsing request */ #define SSH_CHANNEL_RDYNAMIC_FINISH 22 /* reverse SOCKS, finishing connect */ #define SSH_CHANNEL_MAX_TYPE 23 #define CHANNEL_CANCEL_PORT_STATIC -1 /* nonblocking flags for channel_new */ #define CHANNEL_NONBLOCK_LEAVE 0 /* don't modify non-blocking state */ #define CHANNEL_NONBLOCK_SET 1 /* set non-blocking state */ #define CHANNEL_NONBLOCK_STDIO 2 /* set non-blocking and restore on close */ /* c->restore_block mask flags */ #define CHANNEL_RESTORE_RFD 0x01 #define CHANNEL_RESTORE_WFD 0x02 #define CHANNEL_RESTORE_EFD 0x04 /* TCP forwarding */ #define FORWARD_DENY 0 #define FORWARD_REMOTE (1) #define FORWARD_LOCAL (1<<1) #define FORWARD_ALLOW (FORWARD_REMOTE|FORWARD_LOCAL) #define FORWARD_ADM 0x100 #define FORWARD_USER 0x101 struct ssh; struct Channel; typedef struct Channel Channel; struct fwd_perm_list; typedef void channel_open_fn(struct ssh *, int, int, void *); typedef void channel_callback_fn(struct ssh *, int, int, void *); typedef int channel_infilter_fn(struct ssh *, struct Channel *, char *, int); typedef void channel_filter_cleanup_fn(struct ssh *, int, void *); typedef u_char *channel_outfilter_fn(struct ssh *, struct Channel *, u_char **, size_t *); /* Channel success/failure callbacks */ typedef void channel_confirm_cb(struct ssh *, int, struct Channel *, void *); typedef void channel_confirm_abandon_cb(struct ssh *, struct Channel *, void *); struct channel_confirm { TAILQ_ENTRY(channel_confirm) entry; channel_confirm_cb *cb; channel_confirm_abandon_cb *abandon_cb; void *ctx; }; TAILQ_HEAD(channel_confirms, channel_confirm); /* Context for non-blocking connects */ struct channel_connect { char *host; int port; struct addrinfo *ai, *aitop; }; /* Callbacks for mux channels back into client-specific code */ typedef int mux_callback_fn(struct ssh *, struct Channel *); /* * NB. channel IDs on the wire and in c->remote_id are uint32, but local * channel IDs (e.g. c->self) only ever use the int32 subset of this range, * because we use local channel ID -1 for housekeeping. Remote channels have * a dedicated "have_remote_id" flag to indicate their validity. */ struct Channel { int type; /* channel type/state */ int self; /* my own channel identifier */ uint32_t remote_id; /* channel identifier for remote peer */ int have_remote_id; /* non-zero if remote_id is valid */ u_int istate; /* input from channel (state of receive half) */ u_int ostate; /* output to channel (state of transmit half) */ int flags; /* close sent/rcvd */ int rfd; /* read fd */ int wfd; /* write fd */ int efd; /* extended fd */ int sock; /* sock fd */ u_int io_want; /* bitmask of SSH_CHAN_IO_* */ u_int io_ready; /* bitmask of SSH_CHAN_IO_* */ int pfds[4]; /* pollfd entries for rfd/wfd/efd/sock */ int ctl_chan; /* control channel (multiplexed connections) */ int isatty; /* rfd is a tty */ #ifdef _AIX int wfd_isatty; /* wfd is a tty */ #endif int client_tty; /* (client) TTY has been requested */ int force_drain; /* force close on iEOF */ time_t notbefore; /* Pause IO until deadline (time_t) */ int delayed; /* post-IO handlers for newly created * channels are delayed until the first call * to a matching pre-IO handler. * this way post-IO handlers are not * accidentally called if a FD gets reused */ int restore_block; /* fd mask to restore blocking status */ int restore_flags[3];/* flags to restore */ struct sshbuf *input; /* data read from socket, to be sent over * encrypted connection */ struct sshbuf *output; /* data received over encrypted connection for * send on socket */ struct sshbuf *extended; char *path; /* path for unix domain sockets, or host name for forwards */ int listening_port; /* port being listened for forwards */ char *listening_addr; /* addr being listened for forwards */ int host_port; /* remote port to connect for forwards */ char *remote_name; /* remote hostname */ u_int remote_window; u_int remote_maxpacket; u_int local_window; u_int local_window_max; u_int local_consumed; u_int local_maxpacket; int extended_usage; int single_connection; char *ctype; /* const type - NB. not freed on channel_free */ char *xctype; /* extended type */ /* callback */ channel_open_fn *open_confirm; void *open_confirm_ctx; channel_callback_fn *detach_user; int detach_close; struct channel_confirms status_confirms; /* filter */ channel_infilter_fn *input_filter; channel_outfilter_fn *output_filter; void *filter_ctx; channel_filter_cleanup_fn *filter_cleanup; /* keep boundaries */ int datagram; /* non-blocking connect */ /* XXX make this a pointer so the structure can be opaque */ struct channel_connect connect_ctx; /* multiplexing protocol hook, called for each packet received */ mux_callback_fn *mux_rcb; void *mux_ctx; int mux_pause; int mux_downstream_id; /* Inactivity timeouts */ /* Last traffic seen for OPEN channels */ time_t lastused; /* Inactivity timeout deadline in seconds (0 = no timeout) */ int inactive_deadline; }; #define CHAN_EXTENDED_IGNORE 0 #define CHAN_EXTENDED_READ 1 #define CHAN_EXTENDED_WRITE 2 /* default window/packet sizes for tcp/x11-fwd-channel */ #define CHAN_SES_PACKET_DEFAULT (32*1024) #define CHAN_SES_WINDOW_DEFAULT (64*CHAN_SES_PACKET_DEFAULT) #define CHAN_TCP_PACKET_DEFAULT (32*1024) #define CHAN_TCP_WINDOW_DEFAULT (64*CHAN_TCP_PACKET_DEFAULT) #define CHAN_X11_PACKET_DEFAULT (16*1024) #define CHAN_X11_WINDOW_DEFAULT (4*CHAN_X11_PACKET_DEFAULT) /* possible input states */ #define CHAN_INPUT_OPEN 0 #define CHAN_INPUT_WAIT_DRAIN 1 #define CHAN_INPUT_WAIT_OCLOSE 2 #define CHAN_INPUT_CLOSED 3 /* possible output states */ #define CHAN_OUTPUT_OPEN 0 #define CHAN_OUTPUT_WAIT_DRAIN 1 #define CHAN_OUTPUT_WAIT_IEOF 2 #define CHAN_OUTPUT_CLOSED 3 #define CHAN_CLOSE_SENT 0x01 #define CHAN_CLOSE_RCVD 0x02 #define CHAN_EOF_SENT 0x04 #define CHAN_EOF_RCVD 0x08 #define CHAN_LOCAL 0x10 /* file descriptor events */ #define SSH_CHAN_IO_RFD 0x01 #define SSH_CHAN_IO_WFD 0x02 #define SSH_CHAN_IO_EFD_R 0x04 #define SSH_CHAN_IO_EFD_W 0x08 #define SSH_CHAN_IO_EFD (SSH_CHAN_IO_EFD_R|SSH_CHAN_IO_EFD_W) #define SSH_CHAN_IO_SOCK_R 0x10 #define SSH_CHAN_IO_SOCK_W 0x20 #define SSH_CHAN_IO_SOCK (SSH_CHAN_IO_SOCK_R|SSH_CHAN_IO_SOCK_W) /* Read buffer size */ #define CHAN_RBUF (16*1024) /* Maximum size for direct reads to buffers */ #define CHANNEL_MAX_READ CHAN_SES_PACKET_DEFAULT /* Maximum channel input buffer size */ #define CHAN_INPUT_MAX (16*1024*1024) /* Hard limit on number of channels */ #define CHANNELS_MAX_CHANNELS (16*1024) /* check whether 'efd' is still in use */ #define CHANNEL_EFD_INPUT_ACTIVE(c) \ (c->extended_usage == CHAN_EXTENDED_READ && \ (c->efd != -1 || \ sshbuf_len(c->extended) > 0)) #define CHANNEL_EFD_OUTPUT_ACTIVE(c) \ (c->extended_usage == CHAN_EXTENDED_WRITE && \ c->efd != -1 && (!(c->flags & (CHAN_EOF_RCVD|CHAN_CLOSE_RCVD)) || \ sshbuf_len(c->extended) > 0)) /* Add channel management structures to SSH transport instance */ void channel_init_channels(struct ssh *ssh); /* channel management */ Channel *channel_by_id(struct ssh *, int); Channel *channel_by_remote_id(struct ssh *, u_int); Channel *channel_lookup(struct ssh *, int); Channel *channel_new(struct ssh *, char *, int, int, int, int, u_int, u_int, int, const char *, int); void channel_set_fds(struct ssh *, int, int, int, int, int, int, int, u_int); void channel_free(struct ssh *, Channel *); void channel_free_all(struct ssh *); void channel_stop_listening(struct ssh *); void channel_force_close(struct ssh *, Channel *, int); void channel_set_xtype(struct ssh *, int, const char *); void channel_send_open(struct ssh *, int); void channel_request_start(struct ssh *, int, char *, int); void channel_register_cleanup(struct ssh *, int, channel_callback_fn *, int); void channel_register_open_confirm(struct ssh *, int, channel_open_fn *, void *); void channel_register_filter(struct ssh *, int, channel_infilter_fn *, channel_outfilter_fn *, channel_filter_cleanup_fn *, void *); void channel_register_status_confirm(struct ssh *, int, channel_confirm_cb *, channel_confirm_abandon_cb *, void *); void channel_cancel_cleanup(struct ssh *, int); int channel_close_fd(struct ssh *, Channel *, int *); void channel_send_window_changes(struct ssh *); /* channel inactivity timeouts */ void channel_add_timeout(struct ssh *, const char *, int); void channel_clear_timeouts(struct ssh *); /* mux proxy support */ int channel_proxy_downstream(struct ssh *, Channel *mc); int channel_proxy_upstream(Channel *, int, u_int32_t, struct ssh *); /* protocol handler */ int channel_input_data(int, u_int32_t, struct ssh *); int channel_input_extended_data(int, u_int32_t, struct ssh *); int channel_input_ieof(int, u_int32_t, struct ssh *); int channel_input_oclose(int, u_int32_t, struct ssh *); int channel_input_open_confirmation(int, u_int32_t, struct ssh *); int channel_input_open_failure(int, u_int32_t, struct ssh *); int channel_input_port_open(int, u_int32_t, struct ssh *); int channel_input_window_adjust(int, u_int32_t, struct ssh *); int channel_input_status_confirm(int, u_int32_t, struct ssh *); /* file descriptor handling (read/write) */ struct pollfd; struct timespec; void channel_prepare_poll(struct ssh *, struct pollfd **, u_int *, u_int *, u_int, struct timespec *); void channel_after_poll(struct ssh *, struct pollfd *, u_int); -void channel_output_poll(struct ssh *); +int channel_output_poll(struct ssh *); int channel_not_very_much_buffered_data(struct ssh *); void channel_close_all(struct ssh *); int channel_still_open(struct ssh *); const char *channel_format_extended_usage(const Channel *); char *channel_open_message(struct ssh *); int channel_find_open(struct ssh *); /* tcp forwarding */ struct Forward; struct ForwardOptions; void channel_set_af(struct ssh *, int af); void channel_permit_all(struct ssh *, int); void channel_add_permission(struct ssh *, int, int, char *, int); void channel_clear_permission(struct ssh *, int, int); void channel_disable_admin(struct ssh *, int); void channel_update_permission(struct ssh *, int, int); Channel *channel_connect_to_port(struct ssh *, const char *, u_short, char *, char *, int *, const char **); Channel *channel_connect_to_path(struct ssh *, const char *, char *, char *); Channel *channel_connect_stdio_fwd(struct ssh *, const char*, int, int, int, int); Channel *channel_connect_by_listen_address(struct ssh *, const char *, u_short, char *, char *); Channel *channel_connect_by_listen_path(struct ssh *, const char *, char *, char *); int channel_request_remote_forwarding(struct ssh *, struct Forward *); int channel_setup_local_fwd_listener(struct ssh *, struct Forward *, struct ForwardOptions *); int channel_request_rforward_cancel(struct ssh *, struct Forward *); int channel_setup_remote_fwd_listener(struct ssh *, struct Forward *, int *, struct ForwardOptions *); int channel_cancel_rport_listener(struct ssh *, struct Forward *); int channel_cancel_lport_listener(struct ssh *, struct Forward *, int, struct ForwardOptions *); int permitopen_port(const char *); /* x11 forwarding */ void channel_set_x11_refuse_time(struct ssh *, time_t); int x11_connect_display(struct ssh *); int x11_create_display_inet(struct ssh *, int, int, int, u_int *, int **); void x11_request_forwarding_with_spoofing(struct ssh *, int, const char *, const char *, const char *, int); /* channel close */ int chan_is_dead(struct ssh *, Channel *, int); void chan_mark_dead(struct ssh *, Channel *); /* channel events */ void chan_rcvd_oclose(struct ssh *, Channel *); void chan_rcvd_eow(struct ssh *, Channel *); void chan_read_failed(struct ssh *, Channel *); void chan_ibuf_empty(struct ssh *, Channel *); void chan_rcvd_ieof(struct ssh *, Channel *); void chan_write_failed(struct ssh *, Channel *); void chan_obuf_empty(struct ssh *, Channel *); #endif diff --git a/clientloop.c b/clientloop.c index 99846a978397..3e9fa3220b7d 100644 --- a/clientloop.c +++ b/clientloop.c @@ -1,2694 +1,2865 @@ -/* $OpenBSD: clientloop.c,v 1.392 2023/04/03 08:10:54 dtucker Exp $ */ +/* $OpenBSD: clientloop.c,v 1.398 2023/09/10 03:51:55 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * The main loop for the interactive session (client side). * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". * * * Copyright (c) 1999 Theo de Raadt. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * * SSH2 support added by Markus Friedl. * Copyright (c) 1999, 2000, 2001 Markus Friedl. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" #include #include #ifdef HAVE_SYS_STAT_H # include #endif #ifdef HAVE_SYS_TIME_H # include #endif #include #include #include #ifdef HAVE_PATHS_H #include #endif #ifdef HAVE_POLL_H #include #endif #include #include #include #include #include #include #include #include #include #include "openbsd-compat/sys-queue.h" #include "xmalloc.h" #include "ssh.h" #include "ssh2.h" #include "packet.h" #include "sshbuf.h" #include "compat.h" #include "channels.h" #include "dispatch.h" #include "sshkey.h" #include "cipher.h" #include "kex.h" #include "myproposal.h" #include "log.h" #include "misc.h" #include "readconf.h" #include "clientloop.h" #include "sshconnect.h" #include "authfd.h" #include "atomicio.h" #include "sshpty.h" #include "match.h" #include "msg.h" #include "ssherr.h" #include "hostfile.h" /* Permitted RSA signature algorithms for UpdateHostkeys proofs */ #define HOSTKEY_PROOF_RSA_ALGS "rsa-sha2-512,rsa-sha2-256" +/* Uncertainty (in percent) of keystroke timing intervals */ +#define SSH_KEYSTROKE_TIMING_FUZZ 10 + /* import options */ extern Options options; /* Control socket */ extern int muxserver_sock; /* XXX use mux_client_cleanup() instead */ /* * Name of the host we are connecting to. This is the name given on the * command line, or the Hostname specified for the user-supplied name in a * configuration file. */ extern char *host; /* * If this field is not NULL, the ForwardAgent socket is this path and different * instead of SSH_AUTH_SOCK. */ extern char *forward_agent_sock_path; /* * Flag to indicate that we have received a window change signal which has * not yet been processed. This will cause a message indicating the new * window size to be sent to the server a little later. This is volatile * because this is updated in a signal handler. */ static volatile sig_atomic_t received_window_change_signal = 0; static volatile sig_atomic_t received_signal = 0; /* Time when backgrounded control master using ControlPersist should exit */ static time_t control_persist_exit_time = 0; /* Common data for the client loop code. */ volatile sig_atomic_t quit_pending; /* Set non-zero to quit the loop. */ static int last_was_cr; /* Last character was a newline. */ static int exit_status; /* Used to store the command exit status. */ static struct sshbuf *stderr_buffer; /* Used for final exit message. */ static int connection_in; /* Connection to server (input). */ static int connection_out; /* Connection to server (output). */ static int need_rekeying; /* Set to non-zero if rekeying is requested. */ static int session_closed; /* In SSH2: login session closed. */ static time_t x11_refuse_time; /* If >0, refuse x11 opens after this time. */ static time_t server_alive_time; /* Time to do server_alive_check */ static int hostkeys_update_complete; static int session_setup_complete; static void client_init_dispatch(struct ssh *ssh); int session_ident = -1; /* Track escape per proto2 channel */ struct escape_filter_ctx { int escape_pending; int escape_char; }; /* Context for channel confirmation replies */ struct channel_reply_ctx { const char *request_type; int id; enum confirm_action action; }; /* Global request success/failure callbacks */ /* XXX move to struct ssh? */ struct global_confirm { TAILQ_ENTRY(global_confirm) entry; global_confirm_cb *cb; void *ctx; int ref_count; }; TAILQ_HEAD(global_confirms, global_confirm); static struct global_confirms global_confirms = TAILQ_HEAD_INITIALIZER(global_confirms); void ssh_process_session2_setup(int, int, int, struct sshbuf *); static void quit_message(const char *fmt, ...) __attribute__((__format__ (printf, 1, 2))); static void quit_message(const char *fmt, ...) { char *msg; va_list args; int r; va_start(args, fmt); xvasprintf(&msg, fmt, args); va_end(args); if ((r = sshbuf_putf(stderr_buffer, "%s\r\n", msg)) != 0) fatal_fr(r, "sshbuf_putf"); quit_pending = 1; } /* * Signal handler for the window change signal (SIGWINCH). This just sets a * flag indicating that the window has changed. */ static void window_change_handler(int sig) { received_window_change_signal = 1; } /* * Signal handler for signals that cause the program to terminate. These * signals must be trapped to restore terminal modes. */ static void signal_handler(int sig) { received_signal = sig; quit_pending = 1; } /* * Sets control_persist_exit_time to the absolute time when the * backgrounded control master should exit due to expiry of the * ControlPersist timeout. Sets it to 0 if we are not a backgrounded * control master process, or if there is no ControlPersist timeout. */ static void set_control_persist_exit_time(struct ssh *ssh) { if (muxserver_sock == -1 || !options.control_persist || options.control_persist_timeout == 0) { /* not using a ControlPersist timeout */ control_persist_exit_time = 0; } else if (channel_still_open(ssh)) { /* some client connections are still open */ if (control_persist_exit_time > 0) debug2_f("cancel scheduled exit"); control_persist_exit_time = 0; } else if (control_persist_exit_time <= 0) { /* a client connection has recently closed */ control_persist_exit_time = monotime() + (time_t)options.control_persist_timeout; debug2_f("schedule exit in %d seconds", options.control_persist_timeout); } /* else we are already counting down to the timeout */ } #define SSH_X11_VALID_DISPLAY_CHARS ":/.-_" static int client_x11_display_valid(const char *display) { size_t i, dlen; if (display == NULL) return 0; dlen = strlen(display); for (i = 0; i < dlen; i++) { if (!isalnum((u_char)display[i]) && strchr(SSH_X11_VALID_DISPLAY_CHARS, display[i]) == NULL) { debug("Invalid character '%c' in DISPLAY", display[i]); return 0; } } return 1; } #define SSH_X11_PROTO "MIT-MAGIC-COOKIE-1" #define X11_TIMEOUT_SLACK 60 int client_x11_get_proto(struct ssh *ssh, const char *display, const char *xauth_path, u_int trusted, u_int timeout, char **_proto, char **_data) { char *cmd, line[512], xdisplay[512]; char xauthfile[PATH_MAX], xauthdir[PATH_MAX]; static char proto[512], data[512]; FILE *f; int got_data = 0, generated = 0, do_unlink = 0, r; struct stat st; u_int now, x11_timeout_real; *_proto = proto; *_data = data; proto[0] = data[0] = xauthfile[0] = xauthdir[0] = '\0'; if (!client_x11_display_valid(display)) { if (display != NULL) logit("DISPLAY \"%s\" invalid; disabling X11 forwarding", display); return -1; } if (xauth_path != NULL && stat(xauth_path, &st) == -1) { debug("No xauth program."); xauth_path = NULL; } if (xauth_path != NULL) { /* * Handle FamilyLocal case where $DISPLAY does * not match an authorization entry. For this we * just try "xauth list unix:displaynum.screennum". * XXX: "localhost" match to determine FamilyLocal * is not perfect. */ if (strncmp(display, "localhost:", 10) == 0) { if ((r = snprintf(xdisplay, sizeof(xdisplay), "unix:%s", display + 10)) < 0 || (size_t)r >= sizeof(xdisplay)) { error_f("display name too long"); return -1; } display = xdisplay; } if (trusted == 0) { /* * Generate an untrusted X11 auth cookie. * * The authentication cookie should briefly outlive * ssh's willingness to forward X11 connections to * avoid nasty fail-open behaviour in the X server. */ mktemp_proto(xauthdir, sizeof(xauthdir)); if (mkdtemp(xauthdir) == NULL) { error_f("mkdtemp: %s", strerror(errno)); return -1; } do_unlink = 1; if ((r = snprintf(xauthfile, sizeof(xauthfile), "%s/xauthfile", xauthdir)) < 0 || (size_t)r >= sizeof(xauthfile)) { error_f("xauthfile path too long"); rmdir(xauthdir); return -1; } if (timeout == 0) { /* auth doesn't time out */ xasprintf(&cmd, "%s -f %s generate %s %s " "untrusted 2>%s", xauth_path, xauthfile, display, SSH_X11_PROTO, _PATH_DEVNULL); } else { /* Add some slack to requested expiry */ if (timeout < UINT_MAX - X11_TIMEOUT_SLACK) x11_timeout_real = timeout + X11_TIMEOUT_SLACK; else { /* Don't overflow on long timeouts */ x11_timeout_real = UINT_MAX; } xasprintf(&cmd, "%s -f %s generate %s %s " "untrusted timeout %u 2>%s", xauth_path, xauthfile, display, SSH_X11_PROTO, x11_timeout_real, _PATH_DEVNULL); } debug2_f("xauth command: %s", cmd); if (timeout != 0 && x11_refuse_time == 0) { now = monotime() + 1; if (SSH_TIME_T_MAX - timeout < now) x11_refuse_time = SSH_TIME_T_MAX; else x11_refuse_time = now + timeout; channel_set_x11_refuse_time(ssh, x11_refuse_time); } if (system(cmd) == 0) generated = 1; free(cmd); } /* * When in untrusted mode, we read the cookie only if it was * successfully generated as an untrusted one in the step * above. */ if (trusted || generated) { xasprintf(&cmd, "%s %s%s list %s 2>" _PATH_DEVNULL, xauth_path, generated ? "-f " : "" , generated ? xauthfile : "", display); debug2("x11_get_proto: %s", cmd); f = popen(cmd, "r"); if (f && fgets(line, sizeof(line), f) && sscanf(line, "%*s %511s %511s", proto, data) == 2) got_data = 1; if (f) pclose(f); free(cmd); } } if (do_unlink) { unlink(xauthfile); rmdir(xauthdir); } /* Don't fall back to fake X11 data for untrusted forwarding */ if (!trusted && !got_data) { error("Warning: untrusted X11 forwarding setup failed: " "xauth key data not generated"); return -1; } /* * If we didn't get authentication data, just make up some * data. The forwarding code will check the validity of the * response anyway, and substitute this data. The X11 * server, however, will ignore this fake data and use * whatever authentication mechanisms it was using otherwise * for the local connection. */ if (!got_data) { u_int8_t rnd[16]; u_int i; logit("Warning: No xauth data; " "using fake authentication data for X11 forwarding."); strlcpy(proto, SSH_X11_PROTO, sizeof proto); arc4random_buf(rnd, sizeof(rnd)); for (i = 0; i < sizeof(rnd); i++) { snprintf(data + 2 * i, sizeof data - 2 * i, "%02x", rnd[i]); } } return 0; } /* * Checks if the client window has changed, and sends a packet about it to * the server if so. The actual change is detected elsewhere (by a software * interrupt on Unix); this just checks the flag and sends a message if * appropriate. */ static void client_check_window_change(struct ssh *ssh) { if (!received_window_change_signal) return; received_window_change_signal = 0; debug2_f("changed"); channel_send_window_changes(ssh); } static int client_global_request_reply(int type, u_int32_t seq, struct ssh *ssh) { struct global_confirm *gc; if ((gc = TAILQ_FIRST(&global_confirms)) == NULL) return 0; if (gc->cb != NULL) gc->cb(ssh, type, seq, gc->ctx); if (--gc->ref_count <= 0) { TAILQ_REMOVE(&global_confirms, gc, entry); freezero(gc, sizeof(*gc)); } ssh_packet_set_alive_timeouts(ssh, 0); return 0; } static void schedule_server_alive_check(void) { if (options.server_alive_interval > 0) server_alive_time = monotime() + options.server_alive_interval; } static void server_alive_check(struct ssh *ssh) { int r; if (ssh_packet_inc_alive_timeouts(ssh) > options.server_alive_count_max) { logit("Timeout, server %s not responding.", host); cleanup_exit(255); } if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || (r = sshpkt_put_cstring(ssh, "keepalive@openssh.com")) != 0 || (r = sshpkt_put_u8(ssh, 1)) != 0 || /* boolean: want reply */ (r = sshpkt_send(ssh)) != 0) fatal_fr(r, "send packet"); /* Insert an empty placeholder to maintain ordering */ client_register_global_confirm(NULL, NULL); schedule_server_alive_check(); } +/* Try to send a dummy keystroke */ +static int +send_chaff(struct ssh *ssh) +{ + int r; + + if ((ssh->kex->flags & KEX_HAS_PING) == 0) + return 0; + /* XXX probabilistically send chaff? */ + /* + * a SSH2_MSG_CHANNEL_DATA payload is 9 bytes: + * 4 bytes channel ID + 4 bytes string length + 1 byte string data + * simulate that here. + */ + if ((r = sshpkt_start(ssh, SSH2_MSG_PING)) != 0 || + (r = sshpkt_put_cstring(ssh, "PING!")) != 0 || + (r = sshpkt_send(ssh)) != 0) + fatal_fr(r, "send packet"); + return 1; +} + +/* Sets the next interval to send a keystroke or chaff packet */ +static void +set_next_interval(const struct timespec *now, struct timespec *next_interval, + u_int interval_ms, int starting) +{ + struct timespec tmp; + long long interval_ns, fuzz_ns; + static long long rate_fuzz; + + interval_ns = interval_ms * (1000LL * 1000); + fuzz_ns = (interval_ns * SSH_KEYSTROKE_TIMING_FUZZ) / 100; + /* Center fuzz around requested interval */ + if (fuzz_ns > INT_MAX) + fuzz_ns = INT_MAX; + if (fuzz_ns > interval_ns) { + /* Shouldn't happen */ + fatal_f("internal error: fuzz %u%% %lldns > interval %lldns", + SSH_KEYSTROKE_TIMING_FUZZ, fuzz_ns, interval_ns); + } + /* + * Randomise the keystroke/chaff intervals in two ways: + * 1. Each interval has some random jitter applied to make the + * interval-to-interval time unpredictable. + * 2. The overall interval rate is also randomly perturbed for each + * chaffing session to make the average rate unpredictable. + */ + if (starting) + rate_fuzz = arc4random_uniform(fuzz_ns); + interval_ns -= fuzz_ns; + interval_ns += arc4random_uniform(fuzz_ns) + rate_fuzz; + + tmp.tv_sec = interval_ns / (1000 * 1000 * 1000); + tmp.tv_nsec = interval_ns % (1000 * 1000 * 1000); + + timespecadd(now, &tmp, next_interval); +} + +/* + * Performs keystroke timing obfuscation. Returns non-zero if the + * output fd should be polled. + */ +static int +obfuscate_keystroke_timing(struct ssh *ssh, struct timespec *timeout, + int channel_did_enqueue) +{ + static int active; + static struct timespec next_interval, chaff_until; + struct timespec now, tmp; + int just_started = 0, had_keystroke = 0; + static unsigned long long nchaff; + char *stop_reason = NULL; + long long n; + + monotime_ts(&now); + + if (options.obscure_keystroke_timing_interval <= 0) + return 1; /* disabled in config */ + + if (!channel_still_open(ssh) || quit_pending) { + /* Stop if no channels left of we're waiting for one to close */ + stop_reason = "no active channels"; + } else if (ssh_packet_is_rekeying(ssh)) { + /* Stop if we're rekeying */ + stop_reason = "rekeying started"; + } else if (!ssh_packet_interactive_data_to_write(ssh) && + ssh_packet_have_data_to_write(ssh)) { + /* Stop if the output buffer has more than a few keystrokes */ + stop_reason = "output buffer filling"; + } else if (active && channel_did_enqueue && + ssh_packet_have_data_to_write(ssh)) { + /* Still in active mode and have a keystroke queued. */ + had_keystroke = 1; + } else if (active) { + if (timespeccmp(&now, &chaff_until, >=)) { + /* Stop if there have been no keystrokes for a while */ + stop_reason = "chaff time expired"; + } else if (timespeccmp(&now, &next_interval, >=)) { + /* Otherwise if we were due to send, then send chaff */ + if (send_chaff(ssh)) + nchaff++; + } + } + + if (stop_reason != NULL) { + if (active) { + debug3_f("stopping: %s (%llu chaff packets sent)", + stop_reason, nchaff); + active = 0; + } + return 1; + } + + /* + * If we're in interactive mode, and only have a small amount + * of outbound data, then we assume that the user is typing + * interactively. In this case, start quantising outbound packets to + * fixed time intervals to hide inter-keystroke timing. + */ + if (!active && ssh_packet_interactive_data_to_write(ssh) && + channel_did_enqueue && ssh_packet_have_data_to_write(ssh)) { + debug3_f("starting: interval ~%dms", + options.obscure_keystroke_timing_interval); + just_started = had_keystroke = active = 1; + nchaff = 0; + set_next_interval(&now, &next_interval, + options.obscure_keystroke_timing_interval, 1); + } + + /* Don't hold off if obfuscation inactive */ + if (!active) + return 1; + + if (had_keystroke) { + /* + * Arrange to send chaff packets for a random interval after + * the last keystroke was sent. + */ + ms_to_timespec(&tmp, SSH_KEYSTROKE_CHAFF_MIN_MS + + arc4random_uniform(SSH_KEYSTROKE_CHAFF_RNG_MS)); + timespecadd(&now, &tmp, &chaff_until); + } + + ptimeout_deadline_monotime_tsp(timeout, &next_interval); + + if (just_started) + return 1; + + /* Don't arm output fd for poll until the timing interval has elapsed */ + if (timespeccmp(&now, &next_interval, <)) + return 0; + + /* Calculate number of intervals missed since the last check */ + n = (now.tv_sec - next_interval.tv_sec) * 1000LL * 1000 * 1000; + n += now.tv_nsec - next_interval.tv_nsec; + n /= options.obscure_keystroke_timing_interval * 1000LL * 1000; + n = (n < 0) ? 1 : n + 1; + + /* Advance to the next interval */ + set_next_interval(&now, &next_interval, + options.obscure_keystroke_timing_interval * n, 0); + return 1; +} + /* * Waits until the client can do something (some data becomes available on * one of the file descriptors). */ static void client_wait_until_can_do_something(struct ssh *ssh, struct pollfd **pfdp, - u_int *npfd_allocp, u_int *npfd_activep, int rekeying, + u_int *npfd_allocp, u_int *npfd_activep, int channel_did_enqueue, int *conn_in_readyp, int *conn_out_readyp) { struct timespec timeout; - int ret; + int ret, oready; u_int p; *conn_in_readyp = *conn_out_readyp = 0; /* Prepare channel poll. First two pollfd entries are reserved */ ptimeout_init(&timeout); channel_prepare_poll(ssh, pfdp, npfd_allocp, npfd_activep, 2, &timeout); if (*npfd_activep < 2) fatal_f("bad npfd %u", *npfd_activep); /* shouldn't happen */ /* channel_prepare_poll could have closed the last channel */ if (session_closed && !channel_still_open(ssh) && !ssh_packet_have_data_to_write(ssh)) { /* clear events since we did not call poll() */ for (p = 0; p < *npfd_activep; p++) (*pfdp)[p].revents = 0; return; } + oready = obfuscate_keystroke_timing(ssh, &timeout, channel_did_enqueue); + /* Monitor server connection on reserved pollfd entries */ (*pfdp)[0].fd = connection_in; (*pfdp)[0].events = POLLIN; (*pfdp)[1].fd = connection_out; - (*pfdp)[1].events = ssh_packet_have_data_to_write(ssh) ? POLLOUT : 0; + (*pfdp)[1].events = (oready && ssh_packet_have_data_to_write(ssh)) ? + POLLOUT : 0; /* * Wait for something to happen. This will suspend the process until * some polled descriptor can be read, written, or has some other * event pending, or a timeout expires. */ set_control_persist_exit_time(ssh); if (control_persist_exit_time > 0) ptimeout_deadline_monotime(&timeout, control_persist_exit_time); if (options.server_alive_interval > 0) ptimeout_deadline_monotime(&timeout, server_alive_time); - if (options.rekey_interval > 0 && !rekeying) { + if (options.rekey_interval > 0 && !ssh_packet_is_rekeying(ssh)) { ptimeout_deadline_sec(&timeout, ssh_packet_get_rekey_timeout(ssh)); } - ret = poll(*pfdp, *npfd_activep, ptimeout_get_ms(&timeout)); + ret = ppoll(*pfdp, *npfd_activep, ptimeout_get_tsp(&timeout), NULL); if (ret == -1) { /* * We have to clear the events because we return. * We have to return, because the mainloop checks for the flags * set by the signal handlers. */ for (p = 0; p < *npfd_activep; p++) (*pfdp)[p].revents = 0; if (errno == EINTR) return; /* Note: we might still have data in the buffers. */ quit_message("poll: %s", strerror(errno)); return; } *conn_in_readyp = (*pfdp)[0].revents != 0; *conn_out_readyp = (*pfdp)[1].revents != 0; if (options.server_alive_interval > 0 && !*conn_in_readyp && monotime() >= server_alive_time) { /* * ServerAlive check is needed. We can't rely on the poll * timing out since traffic on the client side such as port * forwards can keep waking it up. */ server_alive_check(ssh); } } static void client_suspend_self(struct sshbuf *bin, struct sshbuf *bout, struct sshbuf *berr) { /* Flush stdout and stderr buffers. */ if (sshbuf_len(bout) > 0) atomicio(vwrite, fileno(stdout), sshbuf_mutable_ptr(bout), sshbuf_len(bout)); if (sshbuf_len(berr) > 0) atomicio(vwrite, fileno(stderr), sshbuf_mutable_ptr(berr), sshbuf_len(berr)); leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); sshbuf_reset(bin); sshbuf_reset(bout); sshbuf_reset(berr); /* Send the suspend signal to the program itself. */ kill(getpid(), SIGTSTP); /* Reset window sizes in case they have changed */ received_window_change_signal = 1; enter_raw_mode(options.request_tty == REQUEST_TTY_FORCE); } static void client_process_net_input(struct ssh *ssh) { int r; /* * Read input from the server, and add any such data to the buffer of * the packet subsystem. */ schedule_server_alive_check(); if ((r = ssh_packet_process_read(ssh, connection_in)) == 0) return; /* success */ if (r == SSH_ERR_SYSTEM_ERROR) { if (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK) return; if (errno == EPIPE) { quit_message("Connection to %s closed by remote host.", host); return; } } quit_message("Read from remote host %s: %s", host, ssh_err(r)); } static void client_status_confirm(struct ssh *ssh, int type, Channel *c, void *ctx) { struct channel_reply_ctx *cr = (struct channel_reply_ctx *)ctx; char errmsg[256]; int r, tochan; /* * If a TTY was explicitly requested, then a failure to allocate * one is fatal. */ if (cr->action == CONFIRM_TTY && (options.request_tty == REQUEST_TTY_FORCE || options.request_tty == REQUEST_TTY_YES)) cr->action = CONFIRM_CLOSE; /* XXX suppress on mux _client_ quietmode */ tochan = options.log_level >= SYSLOG_LEVEL_ERROR && c->ctl_chan != -1 && c->extended_usage == CHAN_EXTENDED_WRITE; if (type == SSH2_MSG_CHANNEL_SUCCESS) { debug2("%s request accepted on channel %d", cr->request_type, c->self); } else if (type == SSH2_MSG_CHANNEL_FAILURE) { if (tochan) { snprintf(errmsg, sizeof(errmsg), "%s request failed\r\n", cr->request_type); } else { snprintf(errmsg, sizeof(errmsg), "%s request failed on channel %d", cr->request_type, c->self); } /* If error occurred on primary session channel, then exit */ if (cr->action == CONFIRM_CLOSE && c->self == session_ident) fatal("%s", errmsg); /* * If error occurred on mux client, append to * their stderr. */ if (tochan) { debug3_f("channel %d: mux request: %s", c->self, cr->request_type); if ((r = sshbuf_put(c->extended, errmsg, strlen(errmsg))) != 0) fatal_fr(r, "sshbuf_put"); } else error("%s", errmsg); if (cr->action == CONFIRM_TTY) { /* * If a TTY allocation error occurred, then arrange * for the correct TTY to leave raw mode. */ if (c->self == session_ident) leave_raw_mode(0); else mux_tty_alloc_failed(ssh, c); } else if (cr->action == CONFIRM_CLOSE) { chan_read_failed(ssh, c); chan_write_failed(ssh, c); } } free(cr); } static void client_abandon_status_confirm(struct ssh *ssh, Channel *c, void *ctx) { free(ctx); } void client_expect_confirm(struct ssh *ssh, int id, const char *request, enum confirm_action action) { struct channel_reply_ctx *cr = xcalloc(1, sizeof(*cr)); cr->request_type = request; cr->action = action; channel_register_status_confirm(ssh, id, client_status_confirm, client_abandon_status_confirm, cr); } void client_register_global_confirm(global_confirm_cb *cb, void *ctx) { struct global_confirm *gc, *last_gc; /* Coalesce identical callbacks */ last_gc = TAILQ_LAST(&global_confirms, global_confirms); if (last_gc && last_gc->cb == cb && last_gc->ctx == ctx) { if (++last_gc->ref_count >= INT_MAX) fatal_f("last_gc->ref_count = %d", last_gc->ref_count); return; } gc = xcalloc(1, sizeof(*gc)); gc->cb = cb; gc->ctx = ctx; gc->ref_count = 1; TAILQ_INSERT_TAIL(&global_confirms, gc, entry); } /* * Returns non-zero if the client is able to handle a hostkeys-00@openssh.com * hostkey update request. */ static int can_update_hostkeys(void) { if (hostkeys_update_complete) return 0; if (options.update_hostkeys == SSH_UPDATE_HOSTKEYS_ASK && options.batch_mode) return 0; /* won't ask in batchmode, so don't even try */ if (!options.update_hostkeys || options.num_user_hostfiles <= 0) return 0; return 1; } static void client_repledge(void) { debug3_f("enter"); /* Might be able to tighten pledge now that session is established */ if (options.control_master || options.control_path != NULL || options.forward_x11 || options.fork_after_authentication || can_update_hostkeys() || (session_ident != -1 && !session_setup_complete)) { /* Can't tighten */ return; } /* * LocalCommand and UpdateHostkeys have finished, so can get rid of * filesystem. * * XXX protocol allows a server can to change hostkeys during the * connection at rekey time that could trigger a hostkeys update * but AFAIK no implementations support this. Could improve by * forcing known_hosts to be read-only or via unveil(2). */ if (options.num_local_forwards != 0 || options.num_remote_forwards != 0 || options.num_permitted_remote_opens != 0 || options.enable_escape_commandline != 0) { /* rfwd needs inet */ debug("pledge: network"); if (pledge("stdio unix inet dns proc tty", NULL) == -1) fatal_f("pledge(): %s", strerror(errno)); } else if (options.forward_agent != 0) { /* agent forwarding needs to open $SSH_AUTH_SOCK at will */ debug("pledge: agent"); if (pledge("stdio unix proc tty", NULL) == -1) fatal_f("pledge(): %s", strerror(errno)); } else { debug("pledge: fork"); if (pledge("stdio proc tty", NULL) == -1) fatal_f("pledge(): %s", strerror(errno)); } /* XXX further things to do: * * - might be able to get rid of proc if we kill ~^Z * - ssh -N (no session) * - stdio forwarding * - sessions without tty */ } static void process_cmdline(struct ssh *ssh) { void (*handler)(int); char *s, *cmd; int ok, delete = 0, local = 0, remote = 0, dynamic = 0; struct Forward fwd; memset(&fwd, 0, sizeof(fwd)); leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); handler = ssh_signal(SIGINT, SIG_IGN); cmd = s = read_passphrase("\r\nssh> ", RP_ECHO); if (s == NULL) goto out; while (isspace((u_char)*s)) s++; if (*s == '-') s++; /* Skip cmdline '-', if any */ if (*s == '\0') goto out; if (*s == 'h' || *s == 'H' || *s == '?') { logit("Commands:"); logit(" -L[bind_address:]port:host:hostport " "Request local forward"); logit(" -R[bind_address:]port:host:hostport " "Request remote forward"); logit(" -D[bind_address:]port " "Request dynamic forward"); logit(" -KL[bind_address:]port " "Cancel local forward"); logit(" -KR[bind_address:]port " "Cancel remote forward"); logit(" -KD[bind_address:]port " "Cancel dynamic forward"); if (!options.permit_local_command) goto out; logit(" !args " "Execute local command"); goto out; } if (*s == '!' && options.permit_local_command) { s++; ssh_local_cmd(s); goto out; } if (*s == 'K') { delete = 1; s++; } if (*s == 'L') local = 1; else if (*s == 'R') remote = 1; else if (*s == 'D') dynamic = 1; else { logit("Invalid command."); goto out; } while (isspace((u_char)*++s)) ; /* XXX update list of forwards in options */ if (delete) { /* We pass 1 for dynamicfwd to restrict to 1 or 2 fields. */ if (!parse_forward(&fwd, s, 1, 0)) { logit("Bad forwarding close specification."); goto out; } if (remote) ok = channel_request_rforward_cancel(ssh, &fwd) == 0; else if (dynamic) ok = channel_cancel_lport_listener(ssh, &fwd, 0, &options.fwd_opts) > 0; else ok = channel_cancel_lport_listener(ssh, &fwd, CHANNEL_CANCEL_PORT_STATIC, &options.fwd_opts) > 0; if (!ok) { logit("Unknown port forwarding."); goto out; } logit("Canceled forwarding."); } else { /* -R specs can be both dynamic or not, so check both. */ if (remote) { if (!parse_forward(&fwd, s, 0, remote) && !parse_forward(&fwd, s, 1, remote)) { logit("Bad remote forwarding specification."); goto out; } } else if (!parse_forward(&fwd, s, dynamic, remote)) { logit("Bad local forwarding specification."); goto out; } if (local || dynamic) { if (!channel_setup_local_fwd_listener(ssh, &fwd, &options.fwd_opts)) { logit("Port forwarding failed."); goto out; } } else { if (channel_request_remote_forwarding(ssh, &fwd) < 0) { logit("Port forwarding failed."); goto out; } } logit("Forwarding port."); } out: ssh_signal(SIGINT, handler); enter_raw_mode(options.request_tty == REQUEST_TTY_FORCE); free(cmd); free(fwd.listen_host); free(fwd.listen_path); free(fwd.connect_host); free(fwd.connect_path); } /* reasons to suppress output of an escape command in help output */ #define SUPPRESS_NEVER 0 /* never suppress, always show */ #define SUPPRESS_MUXCLIENT 1 /* don't show in mux client sessions */ #define SUPPRESS_MUXMASTER 2 /* don't show in mux master sessions */ #define SUPPRESS_SYSLOG 4 /* don't show when logging to syslog */ #define SUPPRESS_NOCMDLINE 8 /* don't show when cmdline disabled*/ struct escape_help_text { const char *cmd; const char *text; unsigned int flags; }; static struct escape_help_text esc_txt[] = { {".", "terminate session", SUPPRESS_MUXMASTER}, {".", "terminate connection (and any multiplexed sessions)", SUPPRESS_MUXCLIENT}, {"B", "send a BREAK to the remote system", SUPPRESS_NEVER}, {"C", "open a command line", SUPPRESS_MUXCLIENT|SUPPRESS_NOCMDLINE}, {"R", "request rekey", SUPPRESS_NEVER}, {"V/v", "decrease/increase verbosity (LogLevel)", SUPPRESS_MUXCLIENT}, {"^Z", "suspend ssh", SUPPRESS_MUXCLIENT}, {"#", "list forwarded connections", SUPPRESS_NEVER}, {"&", "background ssh (when waiting for connections to terminate)", SUPPRESS_MUXCLIENT}, {"?", "this message", SUPPRESS_NEVER}, }; static void print_escape_help(struct sshbuf *b, int escape_char, int mux_client, int using_stderr) { unsigned int i, suppress_flags; int r; if ((r = sshbuf_putf(b, "%c?\r\nSupported escape sequences:\r\n", escape_char)) != 0) fatal_fr(r, "sshbuf_putf"); suppress_flags = (mux_client ? SUPPRESS_MUXCLIENT : 0) | (mux_client ? 0 : SUPPRESS_MUXMASTER) | (using_stderr ? 0 : SUPPRESS_SYSLOG) | (options.enable_escape_commandline == 0 ? SUPPRESS_NOCMDLINE : 0); for (i = 0; i < sizeof(esc_txt)/sizeof(esc_txt[0]); i++) { if (esc_txt[i].flags & suppress_flags) continue; if ((r = sshbuf_putf(b, " %c%-3s - %s\r\n", escape_char, esc_txt[i].cmd, esc_txt[i].text)) != 0) fatal_fr(r, "sshbuf_putf"); } if ((r = sshbuf_putf(b, " %c%c - send the escape character by typing it twice\r\n" "(Note that escapes are only recognized immediately after " "newline.)\r\n", escape_char, escape_char)) != 0) fatal_fr(r, "sshbuf_putf"); } /* * Process the characters one by one. */ static int process_escapes(struct ssh *ssh, Channel *c, struct sshbuf *bin, struct sshbuf *bout, struct sshbuf *berr, char *buf, int len) { pid_t pid; int r, bytes = 0; u_int i; u_char ch; char *s; struct escape_filter_ctx *efc; if (c == NULL || c->filter_ctx == NULL || len <= 0) return 0; efc = (struct escape_filter_ctx *)c->filter_ctx; for (i = 0; i < (u_int)len; i++) { /* Get one character at a time. */ ch = buf[i]; if (efc->escape_pending) { /* We have previously seen an escape character. */ /* Clear the flag now. */ efc->escape_pending = 0; /* Process the escaped character. */ switch (ch) { case '.': /* Terminate the connection. */ if ((r = sshbuf_putf(berr, "%c.\r\n", efc->escape_char)) != 0) fatal_fr(r, "sshbuf_putf"); if (c && c->ctl_chan != -1) { channel_force_close(ssh, c, 1); return 0; } else quit_pending = 1; return -1; case 'Z' - 64: /* XXX support this for mux clients */ if (c && c->ctl_chan != -1) { char b[16]; noescape: if (ch == 'Z' - 64) snprintf(b, sizeof b, "^Z"); else snprintf(b, sizeof b, "%c", ch); if ((r = sshbuf_putf(berr, "%c%s escape not available to " "multiplexed sessions\r\n", efc->escape_char, b)) != 0) fatal_fr(r, "sshbuf_putf"); continue; } /* Suspend the program. Inform the user */ if ((r = sshbuf_putf(berr, "%c^Z [suspend ssh]\r\n", efc->escape_char)) != 0) fatal_fr(r, "sshbuf_putf"); /* Restore terminal modes and suspend. */ client_suspend_self(bin, bout, berr); /* We have been continued. */ continue; case 'B': if ((r = sshbuf_putf(berr, "%cB\r\n", efc->escape_char)) != 0) fatal_fr(r, "sshbuf_putf"); channel_request_start(ssh, c->self, "break", 0); if ((r = sshpkt_put_u32(ssh, 1000)) != 0 || (r = sshpkt_send(ssh)) != 0) fatal_fr(r, "send packet"); continue; case 'R': if (ssh->compat & SSH_BUG_NOREKEY) logit("Server does not " "support re-keying"); else need_rekeying = 1; continue; case 'V': /* FALLTHROUGH */ case 'v': if (c && c->ctl_chan != -1) goto noescape; if (!log_is_on_stderr()) { if ((r = sshbuf_putf(berr, "%c%c [Logging to syslog]\r\n", efc->escape_char, ch)) != 0) fatal_fr(r, "sshbuf_putf"); continue; } if (ch == 'V' && options.log_level > SYSLOG_LEVEL_QUIET) log_change_level(--options.log_level); if (ch == 'v' && options.log_level < SYSLOG_LEVEL_DEBUG3) log_change_level(++options.log_level); if ((r = sshbuf_putf(berr, "%c%c [LogLevel %s]\r\n", efc->escape_char, ch, log_level_name(options.log_level))) != 0) fatal_fr(r, "sshbuf_putf"); continue; case '&': if (c->ctl_chan != -1) goto noescape; /* * Detach the program (continue to serve * connections, but put in background and no * more new connections). */ /* Restore tty modes. */ leave_raw_mode( options.request_tty == REQUEST_TTY_FORCE); /* Stop listening for new connections. */ channel_stop_listening(ssh); if ((r = sshbuf_putf(berr, "%c& " "[backgrounded]\n", efc->escape_char)) != 0) fatal_fr(r, "sshbuf_putf"); /* Fork into background. */ pid = fork(); if (pid == -1) { error("fork: %.100s", strerror(errno)); continue; } if (pid != 0) { /* This is the parent. */ /* The parent just exits. */ exit(0); } /* The child continues serving connections. */ /* fake EOF on stdin */ if ((r = sshbuf_put_u8(bin, 4)) != 0) fatal_fr(r, "sshbuf_put_u8"); return -1; case '?': print_escape_help(berr, efc->escape_char, (c && c->ctl_chan != -1), log_is_on_stderr()); continue; case '#': if ((r = sshbuf_putf(berr, "%c#\r\n", efc->escape_char)) != 0) fatal_fr(r, "sshbuf_putf"); s = channel_open_message(ssh); if ((r = sshbuf_put(berr, s, strlen(s))) != 0) fatal_fr(r, "sshbuf_put"); free(s); continue; case 'C': if (c && c->ctl_chan != -1) goto noescape; if (options.enable_escape_commandline == 0) { if ((r = sshbuf_putf(berr, "commandline disabled\r\n")) != 0) fatal_fr(r, "sshbuf_putf"); continue; } process_cmdline(ssh); continue; default: if (ch != efc->escape_char) { if ((r = sshbuf_put_u8(bin, efc->escape_char)) != 0) fatal_fr(r, "sshbuf_put_u8"); bytes++; } /* Escaped characters fall through here */ break; } } else { /* * The previous character was not an escape char. * Check if this is an escape. */ if (last_was_cr && ch == efc->escape_char) { /* * It is. Set the flag and continue to * next character. */ efc->escape_pending = 1; continue; } } /* * Normal character. Record whether it was a newline, * and append it to the buffer. */ last_was_cr = (ch == '\r' || ch == '\n'); if ((r = sshbuf_put_u8(bin, ch)) != 0) fatal_fr(r, "sshbuf_put_u8"); bytes++; } return bytes; } /* * Get packets from the connection input buffer, and process them as long as * there are packets available. * * Any unknown packets received during the actual * session cause the session to terminate. This is * intended to make debugging easier since no * confirmations are sent. Any compatible protocol * extensions must be negotiated during the * preparatory phase. */ static void client_process_buffered_input_packets(struct ssh *ssh) { ssh_dispatch_run_fatal(ssh, DISPATCH_NONBLOCK, &quit_pending); } /* scan buf[] for '~' before sending data to the peer */ /* Helper: allocate a new escape_filter_ctx and fill in its escape char */ void * client_new_escape_filter_ctx(int escape_char) { struct escape_filter_ctx *ret; ret = xcalloc(1, sizeof(*ret)); ret->escape_pending = 0; ret->escape_char = escape_char; return (void *)ret; } /* Free the escape filter context on channel free */ void client_filter_cleanup(struct ssh *ssh, int cid, void *ctx) { free(ctx); } int client_simple_escape_filter(struct ssh *ssh, Channel *c, char *buf, int len) { if (c->extended_usage != CHAN_EXTENDED_WRITE) return 0; return process_escapes(ssh, c, c->input, c->output, c->extended, buf, len); } static void client_channel_closed(struct ssh *ssh, int id, int force, void *arg) { channel_cancel_cleanup(ssh, id); session_closed = 1; leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); } /* * Implements the interactive session with the server. This is called after * the user has been authenticated, and a command has been started on the * remote host. If escape_char != SSH_ESCAPECHAR_NONE, it is the character * used as an escape character for terminating or suspending the session. */ int client_loop(struct ssh *ssh, int have_pty, int escape_char_arg, int ssh2_chan_id) { struct pollfd *pfd = NULL; u_int npfd_alloc = 0, npfd_active = 0; double start_time, total_time; - int r, len; + int channel_did_enqueue = 0, r, len; u_int64_t ibytes, obytes; int conn_in_ready, conn_out_ready; debug("Entering interactive session."); session_ident = ssh2_chan_id; if (options.control_master && !option_clear_or_none(options.control_path)) { debug("pledge: id"); if (pledge("stdio rpath wpath cpath unix inet dns recvfd sendfd proc exec id tty", NULL) == -1) fatal_f("pledge(): %s", strerror(errno)); } else if (options.forward_x11 || options.permit_local_command) { debug("pledge: exec"); if (pledge("stdio rpath wpath cpath unix inet dns proc exec tty", NULL) == -1) fatal_f("pledge(): %s", strerror(errno)); } else if (options.update_hostkeys) { debug("pledge: filesystem"); if (pledge("stdio rpath wpath cpath unix inet dns proc tty", NULL) == -1) fatal_f("pledge(): %s", strerror(errno)); } else if (!option_clear_or_none(options.proxy_command) || options.fork_after_authentication) { debug("pledge: proc"); if (pledge("stdio cpath unix inet dns proc tty", NULL) == -1) fatal_f("pledge(): %s", strerror(errno)); } else { debug("pledge: network"); if (pledge("stdio unix inet dns proc tty", NULL) == -1) fatal_f("pledge(): %s", strerror(errno)); } /* might be able to tighten now */ client_repledge(); start_time = monotime_double(); /* Initialize variables. */ last_was_cr = 1; exit_status = -1; connection_in = ssh_packet_get_connection_in(ssh); connection_out = ssh_packet_get_connection_out(ssh); quit_pending = 0; /* Initialize buffer. */ if ((stderr_buffer = sshbuf_new()) == NULL) fatal_f("sshbuf_new failed"); client_init_dispatch(ssh); /* * Set signal handlers, (e.g. to restore non-blocking mode) * but don't overwrite SIG_IGN, matches behaviour from rsh(1) */ if (ssh_signal(SIGHUP, SIG_IGN) != SIG_IGN) ssh_signal(SIGHUP, signal_handler); if (ssh_signal(SIGINT, SIG_IGN) != SIG_IGN) ssh_signal(SIGINT, signal_handler); if (ssh_signal(SIGQUIT, SIG_IGN) != SIG_IGN) ssh_signal(SIGQUIT, signal_handler); if (ssh_signal(SIGTERM, SIG_IGN) != SIG_IGN) ssh_signal(SIGTERM, signal_handler); ssh_signal(SIGWINCH, window_change_handler); if (have_pty) enter_raw_mode(options.request_tty == REQUEST_TTY_FORCE); if (session_ident != -1) { if (escape_char_arg != SSH_ESCAPECHAR_NONE) { channel_register_filter(ssh, session_ident, client_simple_escape_filter, NULL, client_filter_cleanup, client_new_escape_filter_ctx( escape_char_arg)); } channel_register_cleanup(ssh, session_ident, client_channel_closed, 0); } schedule_server_alive_check(); /* Main loop of the client for the interactive session mode. */ while (!quit_pending) { + channel_did_enqueue = 0; /* Process buffered packets sent by the server. */ client_process_buffered_input_packets(ssh); if (session_closed && !channel_still_open(ssh)) break; if (ssh_packet_is_rekeying(ssh)) { debug("rekeying in progress"); } else if (need_rekeying) { /* manual rekey request */ debug("need rekeying"); if ((r = kex_start_rekex(ssh)) != 0) fatal_fr(r, "kex_start_rekex"); need_rekeying = 0; } else { /* * Make packets from buffered channel data, and * enqueue them for sending to the server. */ if (ssh_packet_not_very_much_data_to_write(ssh)) - channel_output_poll(ssh); + channel_did_enqueue = channel_output_poll(ssh); /* * Check if the window size has changed, and buffer a * message about it to the server if so. */ client_check_window_change(ssh); if (quit_pending) break; } /* * Wait until we have something to do (something becomes * available on one of the descriptors). */ client_wait_until_can_do_something(ssh, &pfd, &npfd_alloc, - &npfd_active, ssh_packet_is_rekeying(ssh), + &npfd_active, channel_did_enqueue, &conn_in_ready, &conn_out_ready); if (quit_pending) break; /* Do channel operations. */ channel_after_poll(ssh, pfd, npfd_active); /* Buffer input from the connection. */ if (conn_in_ready) client_process_net_input(ssh); if (quit_pending) break; /* A timeout may have triggered rekeying */ if ((r = ssh_packet_check_rekey(ssh)) != 0) fatal_fr(r, "cannot start rekeying"); /* * Send as much buffered packet data as possible to the * sender. */ if (conn_out_ready) { if ((r = ssh_packet_write_poll(ssh)) != 0) { sshpkt_fatal(ssh, r, "%s: ssh_packet_write_poll", __func__); } } /* * If we are a backgrounded control master, and the * timeout has expired without any active client * connections, then quit. */ if (control_persist_exit_time > 0) { if (monotime() >= control_persist_exit_time) { debug("ControlPersist timeout expired"); break; } } } free(pfd); /* Terminate the session. */ /* Stop watching for window change. */ ssh_signal(SIGWINCH, SIG_DFL); if ((r = sshpkt_start(ssh, SSH2_MSG_DISCONNECT)) != 0 || (r = sshpkt_put_u32(ssh, SSH2_DISCONNECT_BY_APPLICATION)) != 0 || (r = sshpkt_put_cstring(ssh, "disconnected by user")) != 0 || (r = sshpkt_put_cstring(ssh, "")) != 0 || /* language tag */ (r = sshpkt_send(ssh)) != 0 || (r = ssh_packet_write_wait(ssh)) != 0) fatal_fr(r, "send disconnect"); channel_free_all(ssh); if (have_pty) leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); /* * If there was no shell or command requested, there will be no remote * exit status to be returned. In that case, clear error code if the * connection was deliberately terminated at this end. */ if (options.session_type == SESSION_TYPE_NONE && received_signal == SIGTERM) { received_signal = 0; exit_status = 0; } if (received_signal) { verbose("Killed by signal %d.", (int) received_signal); cleanup_exit(255); } /* * In interactive mode (with pseudo tty) display a message indicating * that the connection has been closed. */ if (have_pty && options.log_level >= SYSLOG_LEVEL_INFO) quit_message("Connection to %s closed.", host); /* Output any buffered data for stderr. */ if (sshbuf_len(stderr_buffer) > 0) { len = atomicio(vwrite, fileno(stderr), (u_char *)sshbuf_ptr(stderr_buffer), sshbuf_len(stderr_buffer)); if (len < 0 || (u_int)len != sshbuf_len(stderr_buffer)) error("Write failed flushing stderr buffer."); else if ((r = sshbuf_consume(stderr_buffer, len)) != 0) fatal_fr(r, "sshbuf_consume"); } /* Clear and free any buffers. */ sshbuf_free(stderr_buffer); /* Report bytes transferred, and transfer rates. */ total_time = monotime_double() - start_time; ssh_packet_get_bytes(ssh, &ibytes, &obytes); verbose("Transferred: sent %llu, received %llu bytes, in %.1f seconds", (unsigned long long)obytes, (unsigned long long)ibytes, total_time); if (total_time > 0) verbose("Bytes per second: sent %.1f, received %.1f", obytes / total_time, ibytes / total_time); /* Return the exit status of the program. */ debug("Exit status %d", exit_status); return exit_status; } /*********/ static Channel * client_request_forwarded_tcpip(struct ssh *ssh, const char *request_type, int rchan, u_int rwindow, u_int rmaxpack) { Channel *c = NULL; struct sshbuf *b = NULL; char *listen_address, *originator_address; u_int listen_port, originator_port; int r; /* Get rest of the packet */ if ((r = sshpkt_get_cstring(ssh, &listen_address, NULL)) != 0 || (r = sshpkt_get_u32(ssh, &listen_port)) != 0 || (r = sshpkt_get_cstring(ssh, &originator_address, NULL)) != 0 || (r = sshpkt_get_u32(ssh, &originator_port)) != 0 || (r = sshpkt_get_end(ssh)) != 0) fatal_fr(r, "parse packet"); debug_f("listen %s port %d, originator %s port %d", listen_address, listen_port, originator_address, originator_port); if (listen_port > 0xffff) error_f("invalid listen port"); else if (originator_port > 0xffff) error_f("invalid originator port"); else { c = channel_connect_by_listen_address(ssh, listen_address, listen_port, "forwarded-tcpip", originator_address); } if (c != NULL && c->type == SSH_CHANNEL_MUX_CLIENT) { if ((b = sshbuf_new()) == NULL) { error_f("alloc reply"); goto out; } /* reconstruct and send to muxclient */ if ((r = sshbuf_put_u8(b, 0)) != 0 || /* padlen */ (r = sshbuf_put_u8(b, SSH2_MSG_CHANNEL_OPEN)) != 0 || (r = sshbuf_put_cstring(b, request_type)) != 0 || (r = sshbuf_put_u32(b, rchan)) != 0 || (r = sshbuf_put_u32(b, rwindow)) != 0 || (r = sshbuf_put_u32(b, rmaxpack)) != 0 || (r = sshbuf_put_cstring(b, listen_address)) != 0 || (r = sshbuf_put_u32(b, listen_port)) != 0 || (r = sshbuf_put_cstring(b, originator_address)) != 0 || (r = sshbuf_put_u32(b, originator_port)) != 0 || (r = sshbuf_put_stringb(c->output, b)) != 0) { error_fr(r, "compose for muxclient"); goto out; } } out: sshbuf_free(b); free(originator_address); free(listen_address); return c; } static Channel * client_request_forwarded_streamlocal(struct ssh *ssh, const char *request_type, int rchan) { Channel *c = NULL; char *listen_path; int r; /* Get the remote path. */ if ((r = sshpkt_get_cstring(ssh, &listen_path, NULL)) != 0 || (r = sshpkt_get_string(ssh, NULL, NULL)) != 0 || /* reserved */ (r = sshpkt_get_end(ssh)) != 0) fatal_fr(r, "parse packet"); debug_f("request: %s", listen_path); c = channel_connect_by_listen_path(ssh, listen_path, "forwarded-streamlocal@openssh.com", "forwarded-streamlocal"); free(listen_path); return c; } static Channel * client_request_x11(struct ssh *ssh, const char *request_type, int rchan) { Channel *c = NULL; char *originator; u_int originator_port; int r, sock; if (!options.forward_x11) { error("Warning: ssh server tried X11 forwarding."); error("Warning: this is probably a break-in attempt by a " "malicious server."); return NULL; } if (x11_refuse_time != 0 && monotime() >= x11_refuse_time) { verbose("Rejected X11 connection after ForwardX11Timeout " "expired"); return NULL; } if ((r = sshpkt_get_cstring(ssh, &originator, NULL)) != 0 || (r = sshpkt_get_u32(ssh, &originator_port)) != 0 || (r = sshpkt_get_end(ssh)) != 0) fatal_fr(r, "parse packet"); /* XXX check permission */ /* XXX range check originator port? */ debug("client_request_x11: request from %s %u", originator, originator_port); free(originator); sock = x11_connect_display(ssh); if (sock < 0) return NULL; c = channel_new(ssh, "x11", SSH_CHANNEL_X11_OPEN, sock, sock, -1, CHAN_TCP_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 0, "x11", 1); c->force_drain = 1; return c; } static Channel * client_request_agent(struct ssh *ssh, const char *request_type, int rchan) { Channel *c = NULL; int r, sock; if (!options.forward_agent) { error("Warning: ssh server tried agent forwarding."); error("Warning: this is probably a break-in attempt by a " "malicious server."); return NULL; } if (forward_agent_sock_path == NULL) { r = ssh_get_authentication_socket(&sock); } else { r = ssh_get_authentication_socket_path(forward_agent_sock_path, &sock); } if (r != 0) { if (r != SSH_ERR_AGENT_NOT_PRESENT) debug_fr(r, "ssh_get_authentication_socket"); return NULL; } if ((r = ssh_agent_bind_hostkey(sock, ssh->kex->initial_hostkey, ssh->kex->session_id, ssh->kex->initial_sig, 1)) == 0) debug_f("bound agent to hostkey"); else debug2_fr(r, "ssh_agent_bind_hostkey"); c = channel_new(ssh, "authentication agent connection", SSH_CHANNEL_OPEN, sock, sock, -1, CHAN_X11_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, "authentication agent connection", 1); c->force_drain = 1; return c; } char * client_request_tun_fwd(struct ssh *ssh, int tun_mode, int local_tun, int remote_tun, channel_open_fn *cb, void *cbctx) { Channel *c; int r, fd; char *ifname = NULL; if (tun_mode == SSH_TUNMODE_NO) return 0; debug("Requesting tun unit %d in mode %d", local_tun, tun_mode); /* Open local tunnel device */ if ((fd = tun_open(local_tun, tun_mode, &ifname)) == -1) { error("Tunnel device open failed."); return NULL; } debug("Tunnel forwarding using interface %s", ifname); c = channel_new(ssh, "tun", SSH_CHANNEL_OPENING, fd, fd, -1, CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, "tun", 1); c->datagram = 1; #if defined(SSH_TUN_FILTER) if (options.tun_open == SSH_TUNMODE_POINTOPOINT) channel_register_filter(ssh, c->self, sys_tun_infilter, sys_tun_outfilter, NULL, NULL); #endif if (cb != NULL) channel_register_open_confirm(ssh, c->self, cb, cbctx); if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_OPEN)) != 0 || (r = sshpkt_put_cstring(ssh, "tun@openssh.com")) != 0 || (r = sshpkt_put_u32(ssh, c->self)) != 0 || (r = sshpkt_put_u32(ssh, c->local_window_max)) != 0 || (r = sshpkt_put_u32(ssh, c->local_maxpacket)) != 0 || (r = sshpkt_put_u32(ssh, tun_mode)) != 0 || (r = sshpkt_put_u32(ssh, remote_tun)) != 0 || (r = sshpkt_send(ssh)) != 0) sshpkt_fatal(ssh, r, "%s: send reply", __func__); return ifname; } /* XXXX move to generic input handler */ static int client_input_channel_open(int type, u_int32_t seq, struct ssh *ssh) { Channel *c = NULL; char *ctype = NULL; int r; u_int rchan; size_t len; u_int rmaxpack, rwindow; if ((r = sshpkt_get_cstring(ssh, &ctype, &len)) != 0 || (r = sshpkt_get_u32(ssh, &rchan)) != 0 || (r = sshpkt_get_u32(ssh, &rwindow)) != 0 || (r = sshpkt_get_u32(ssh, &rmaxpack)) != 0) goto out; debug("client_input_channel_open: ctype %s rchan %d win %d max %d", ctype, rchan, rwindow, rmaxpack); if (strcmp(ctype, "forwarded-tcpip") == 0) { c = client_request_forwarded_tcpip(ssh, ctype, rchan, rwindow, rmaxpack); } else if (strcmp(ctype, "forwarded-streamlocal@openssh.com") == 0) { c = client_request_forwarded_streamlocal(ssh, ctype, rchan); } else if (strcmp(ctype, "x11") == 0) { c = client_request_x11(ssh, ctype, rchan); } else if (strcmp(ctype, "auth-agent@openssh.com") == 0) { c = client_request_agent(ssh, ctype, rchan); } if (c != NULL && c->type == SSH_CHANNEL_MUX_CLIENT) { debug3("proxied to downstream: %s", ctype); } else if (c != NULL) { debug("confirm %s", ctype); c->remote_id = rchan; c->have_remote_id = 1; c->remote_window = rwindow; c->remote_maxpacket = rmaxpack; if (c->type != SSH_CHANNEL_CONNECTING) { if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_OPEN_CONFIRMATION)) != 0 || (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || (r = sshpkt_put_u32(ssh, c->self)) != 0 || (r = sshpkt_put_u32(ssh, c->local_window)) != 0 || (r = sshpkt_put_u32(ssh, c->local_maxpacket)) != 0 || (r = sshpkt_send(ssh)) != 0) sshpkt_fatal(ssh, r, "%s: send reply", __func__); } } else { debug("failure %s", ctype); if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_OPEN_FAILURE)) != 0 || (r = sshpkt_put_u32(ssh, rchan)) != 0 || (r = sshpkt_put_u32(ssh, SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED)) != 0 || (r = sshpkt_put_cstring(ssh, "open failed")) != 0 || (r = sshpkt_put_cstring(ssh, "")) != 0 || (r = sshpkt_send(ssh)) != 0) sshpkt_fatal(ssh, r, "%s: send failure", __func__); } r = 0; out: free(ctype); return r; } static int client_input_channel_req(int type, u_int32_t seq, struct ssh *ssh) { Channel *c = NULL; char *rtype = NULL; u_char reply; u_int id, exitval; int r, success = 0; if ((r = sshpkt_get_u32(ssh, &id)) != 0) return r; if (id <= INT_MAX) c = channel_lookup(ssh, id); if (channel_proxy_upstream(c, type, seq, ssh)) return 0; if ((r = sshpkt_get_cstring(ssh, &rtype, NULL)) != 0 || (r = sshpkt_get_u8(ssh, &reply)) != 0) goto out; debug("client_input_channel_req: channel %u rtype %s reply %d", id, rtype, reply); if (c == NULL) { error("client_input_channel_req: channel %d: " "unknown channel", id); } else if (strcmp(rtype, "eow@openssh.com") == 0) { if ((r = sshpkt_get_end(ssh)) != 0) goto out; chan_rcvd_eow(ssh, c); } else if (strcmp(rtype, "exit-status") == 0) { if ((r = sshpkt_get_u32(ssh, &exitval)) != 0) goto out; if (c->ctl_chan != -1) { mux_exit_message(ssh, c, exitval); success = 1; } else if ((int)id == session_ident) { /* Record exit value of local session */ success = 1; exit_status = exitval; } else { /* Probably for a mux channel that has already closed */ debug_f("no sink for exit-status on channel %d", id); } if ((r = sshpkt_get_end(ssh)) != 0) goto out; } if (reply && c != NULL && !(c->flags & CHAN_CLOSE_SENT)) { if (!c->have_remote_id) fatal_f("channel %d: no remote_id", c->self); if ((r = sshpkt_start(ssh, success ? SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE)) != 0 || (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || (r = sshpkt_send(ssh)) != 0) sshpkt_fatal(ssh, r, "%s: send failure", __func__); } r = 0; out: free(rtype); return r; } struct hostkeys_update_ctx { /* The hostname and (optionally) IP address string for the server */ char *host_str, *ip_str; /* * Keys received from the server and a flag for each indicating * whether they already exist in known_hosts. * keys_match is filled in by hostkeys_find() and later (for new * keys) by client_global_hostkeys_prove_confirm(). */ struct sshkey **keys; u_int *keys_match; /* mask of HKF_MATCH_* from hostfile.h */ int *keys_verified; /* flag for new keys verified by server */ size_t nkeys, nnew, nincomplete; /* total, new keys, incomplete match */ /* * Keys that are in known_hosts, but were not present in the update * from the server (i.e. scheduled to be deleted). * Filled in by hostkeys_find(). */ struct sshkey **old_keys; size_t nold; /* Various special cases. */ int complex_hostspec; /* wildcard or manual pattern-list host name */ int ca_available; /* saw CA key for this host */ int old_key_seen; /* saw old key with other name/addr */ int other_name_seen; /* saw key with other name/addr */ }; static void hostkeys_update_ctx_free(struct hostkeys_update_ctx *ctx) { size_t i; if (ctx == NULL) return; for (i = 0; i < ctx->nkeys; i++) sshkey_free(ctx->keys[i]); free(ctx->keys); free(ctx->keys_match); free(ctx->keys_verified); for (i = 0; i < ctx->nold; i++) sshkey_free(ctx->old_keys[i]); free(ctx->old_keys); free(ctx->host_str); free(ctx->ip_str); free(ctx); } /* * Returns non-zero if a known_hosts hostname list is not of a form that * can be handled by UpdateHostkeys. These include wildcard hostnames and * hostnames lists that do not follow the form host[,ip]. */ static int hostspec_is_complex(const char *hosts) { char *cp; /* wildcard */ if (strchr(hosts, '*') != NULL || strchr(hosts, '?') != NULL) return 1; /* single host/ip = ok */ if ((cp = strchr(hosts, ',')) == NULL) return 0; /* more than two entries on the line */ if (strchr(cp + 1, ',') != NULL) return 1; /* XXX maybe parse cp+1 and ensure it is an IP? */ return 0; } /* callback to search for ctx->keys in known_hosts */ static int hostkeys_find(struct hostkey_foreach_line *l, void *_ctx) { struct hostkeys_update_ctx *ctx = (struct hostkeys_update_ctx *)_ctx; size_t i; struct sshkey **tmp; if (l->key == NULL) return 0; if (l->status != HKF_STATUS_MATCHED) { /* Record if one of the keys appears on a non-matching line */ for (i = 0; i < ctx->nkeys; i++) { if (sshkey_equal(l->key, ctx->keys[i])) { ctx->other_name_seen = 1; debug3_f("found %s key under different " "name/addr at %s:%ld", sshkey_ssh_name(ctx->keys[i]), l->path, l->linenum); return 0; } } return 0; } /* Don't proceed if revocation or CA markers are present */ /* XXX relax this */ if (l->marker != MRK_NONE) { debug3_f("hostkeys file %s:%ld has CA/revocation marker", l->path, l->linenum); ctx->complex_hostspec = 1; return 0; } /* If CheckHostIP is enabled, then check for mismatched hostname/addr */ if (ctx->ip_str != NULL && strchr(l->hosts, ',') != NULL) { if ((l->match & HKF_MATCH_HOST) == 0) { /* Record if address matched a different hostname. */ ctx->other_name_seen = 1; debug3_f("found address %s against different hostname " "at %s:%ld", ctx->ip_str, l->path, l->linenum); return 0; } else if ((l->match & HKF_MATCH_IP) == 0) { /* Record if hostname matched a different address. */ ctx->other_name_seen = 1; debug3_f("found hostname %s against different address " "at %s:%ld", ctx->host_str, l->path, l->linenum); } } /* * UpdateHostkeys is skipped for wildcard host names and hostnames * that contain more than two entries (ssh never writes these). */ if (hostspec_is_complex(l->hosts)) { debug3_f("hostkeys file %s:%ld complex host specification", l->path, l->linenum); ctx->complex_hostspec = 1; return 0; } /* Mark off keys we've already seen for this host */ for (i = 0; i < ctx->nkeys; i++) { if (!sshkey_equal(l->key, ctx->keys[i])) continue; debug3_f("found %s key at %s:%ld", sshkey_ssh_name(ctx->keys[i]), l->path, l->linenum); ctx->keys_match[i] |= l->match; return 0; } /* This line contained a key that not offered by the server */ debug3_f("deprecated %s key at %s:%ld", sshkey_ssh_name(l->key), l->path, l->linenum); if ((tmp = recallocarray(ctx->old_keys, ctx->nold, ctx->nold + 1, sizeof(*ctx->old_keys))) == NULL) fatal_f("recallocarray failed nold = %zu", ctx->nold); ctx->old_keys = tmp; ctx->old_keys[ctx->nold++] = l->key; l->key = NULL; return 0; } /* callback to search for ctx->old_keys in known_hosts under other names */ static int hostkeys_check_old(struct hostkey_foreach_line *l, void *_ctx) { struct hostkeys_update_ctx *ctx = (struct hostkeys_update_ctx *)_ctx; size_t i; int hashed; /* only care about lines that *don't* match the active host spec */ if (l->status == HKF_STATUS_MATCHED || l->key == NULL) return 0; hashed = l->match & (HKF_MATCH_HOST_HASHED|HKF_MATCH_IP_HASHED); for (i = 0; i < ctx->nold; i++) { if (!sshkey_equal(l->key, ctx->old_keys[i])) continue; debug3_f("found deprecated %s key at %s:%ld as %s", sshkey_ssh_name(ctx->old_keys[i]), l->path, l->linenum, hashed ? "[HASHED]" : l->hosts); ctx->old_key_seen = 1; break; } return 0; } /* * Check known_hosts files for deprecated keys under other names. Returns 0 * on success or -1 on failure. Updates ctx->old_key_seen if deprecated keys * exist under names other than the active hostname/IP. */ static int check_old_keys_othernames(struct hostkeys_update_ctx *ctx) { size_t i; int r; debug2_f("checking for %zu deprecated keys", ctx->nold); for (i = 0; i < options.num_user_hostfiles; i++) { debug3_f("searching %s for %s / %s", options.user_hostfiles[i], ctx->host_str, ctx->ip_str ? ctx->ip_str : "(none)"); if ((r = hostkeys_foreach(options.user_hostfiles[i], hostkeys_check_old, ctx, ctx->host_str, ctx->ip_str, HKF_WANT_PARSE_KEY, 0)) != 0) { if (r == SSH_ERR_SYSTEM_ERROR && errno == ENOENT) { debug_f("hostkeys file %s does not exist", options.user_hostfiles[i]); continue; } error_fr(r, "hostkeys_foreach failed for %s", options.user_hostfiles[i]); return -1; } } return 0; } static void hostkey_change_preamble(LogLevel loglevel) { do_log2(loglevel, "The server has updated its host keys."); do_log2(loglevel, "These changes were verified by the server's " "existing trusted key."); } static void update_known_hosts(struct hostkeys_update_ctx *ctx) { int r, was_raw = 0, first = 1; int asking = options.update_hostkeys == SSH_UPDATE_HOSTKEYS_ASK; LogLevel loglevel = asking ? SYSLOG_LEVEL_INFO : SYSLOG_LEVEL_VERBOSE; char *fp, *response; size_t i; struct stat sb; for (i = 0; i < ctx->nkeys; i++) { if (!ctx->keys_verified[i]) continue; if ((fp = sshkey_fingerprint(ctx->keys[i], options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) fatal_f("sshkey_fingerprint failed"); if (first && asking) hostkey_change_preamble(loglevel); do_log2(loglevel, "Learned new hostkey: %s %s", sshkey_type(ctx->keys[i]), fp); first = 0; free(fp); } for (i = 0; i < ctx->nold; i++) { if ((fp = sshkey_fingerprint(ctx->old_keys[i], options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) fatal_f("sshkey_fingerprint failed"); if (first && asking) hostkey_change_preamble(loglevel); do_log2(loglevel, "Deprecating obsolete hostkey: %s %s", sshkey_type(ctx->old_keys[i]), fp); first = 0; free(fp); } if (options.update_hostkeys == SSH_UPDATE_HOSTKEYS_ASK) { if (get_saved_tio() != NULL) { leave_raw_mode(1); was_raw = 1; } response = NULL; for (i = 0; !quit_pending && i < 3; i++) { free(response); response = read_passphrase("Accept updated hostkeys? " "(yes/no): ", RP_ECHO); if (response != NULL && strcasecmp(response, "yes") == 0) break; else if (quit_pending || response == NULL || strcasecmp(response, "no") == 0) { options.update_hostkeys = 0; break; } else { do_log2(loglevel, "Please enter " "\"yes\" or \"no\""); } } if (quit_pending || i >= 3 || response == NULL) options.update_hostkeys = 0; free(response); if (was_raw) enter_raw_mode(1); } if (options.update_hostkeys == 0) return; /* * Now that all the keys are verified, we can go ahead and replace * them in known_hosts (assuming SSH_UPDATE_HOSTKEYS_ASK didn't * cancel the operation). */ for (i = 0; i < options.num_user_hostfiles; i++) { /* * NB. keys are only added to hostfiles[0], for the rest we * just delete the hostname entries. */ if (stat(options.user_hostfiles[i], &sb) != 0) { if (errno == ENOENT) { debug_f("known hosts file %s does not " "exist", options.user_hostfiles[i]); } else { error_f("known hosts file %s " "inaccessible: %s", options.user_hostfiles[i], strerror(errno)); } continue; } if ((r = hostfile_replace_entries(options.user_hostfiles[i], ctx->host_str, ctx->ip_str, i == 0 ? ctx->keys : NULL, i == 0 ? ctx->nkeys : 0, options.hash_known_hosts, 0, options.fingerprint_hash)) != 0) { error_fr(r, "hostfile_replace_entries failed for %s", options.user_hostfiles[i]); } } } static void client_global_hostkeys_prove_confirm(struct ssh *ssh, int type, u_int32_t seq, void *_ctx) { struct hostkeys_update_ctx *ctx = (struct hostkeys_update_ctx *)_ctx; size_t i, ndone; struct sshbuf *signdata; int r, plaintype; const u_char *sig; const char *rsa_kexalg = NULL; char *alg = NULL; size_t siglen; if (ctx->nnew == 0) fatal_f("ctx->nnew == 0"); /* sanity */ if (type != SSH2_MSG_REQUEST_SUCCESS) { error("Server failed to confirm ownership of " "private host keys"); hostkeys_update_ctx_free(ctx); return; } if (sshkey_type_plain(sshkey_type_from_name( ssh->kex->hostkey_alg)) == KEY_RSA) rsa_kexalg = ssh->kex->hostkey_alg; if ((signdata = sshbuf_new()) == NULL) fatal_f("sshbuf_new failed"); /* * Expect a signature for each of the ctx->nnew private keys we * haven't seen before. They will be in the same order as the * ctx->keys where the corresponding ctx->keys_match[i] == 0. */ for (ndone = i = 0; i < ctx->nkeys; i++) { if (ctx->keys_match[i]) continue; plaintype = sshkey_type_plain(ctx->keys[i]->type); /* Prepare data to be signed: session ID, unique string, key */ sshbuf_reset(signdata); if ( (r = sshbuf_put_cstring(signdata, "hostkeys-prove-00@openssh.com")) != 0 || (r = sshbuf_put_stringb(signdata, ssh->kex->session_id)) != 0 || (r = sshkey_puts(ctx->keys[i], signdata)) != 0) fatal_fr(r, "compose signdata"); /* Extract and verify signature */ if ((r = sshpkt_get_string_direct(ssh, &sig, &siglen)) != 0) { error_fr(r, "parse sig"); goto out; } if ((r = sshkey_get_sigtype(sig, siglen, &alg)) != 0) { error_fr(r, "server gave unintelligible signature " "for %s key %zu", sshkey_type(ctx->keys[i]), i); goto out; } /* * Special case for RSA keys: if a RSA hostkey was negotiated, * then use its signature type for verification of RSA hostkey * proofs. Otherwise, accept only RSA-SHA256/512 signatures. */ if (plaintype == KEY_RSA && rsa_kexalg == NULL && match_pattern_list(alg, HOSTKEY_PROOF_RSA_ALGS, 0) != 1) { debug_f("server used untrusted RSA signature algorithm " "%s for key %zu, disregarding", alg, i); free(alg); /* zap the key from the list */ sshkey_free(ctx->keys[i]); ctx->keys[i] = NULL; ndone++; continue; } debug3_f("verify %s key %zu using sigalg %s", sshkey_type(ctx->keys[i]), i, alg); free(alg); if ((r = sshkey_verify(ctx->keys[i], sig, siglen, sshbuf_ptr(signdata), sshbuf_len(signdata), plaintype == KEY_RSA ? rsa_kexalg : NULL, 0, NULL)) != 0) { error_fr(r, "server gave bad signature for %s key %zu", sshkey_type(ctx->keys[i]), i); goto out; } /* Key is good. Mark it as 'seen' */ ctx->keys_verified[i] = 1; ndone++; } /* Shouldn't happen */ if (ndone != ctx->nnew) fatal_f("ndone != ctx->nnew (%zu / %zu)", ndone, ctx->nnew); if ((r = sshpkt_get_end(ssh)) != 0) { error_f("protocol error"); goto out; } /* Make the edits to known_hosts */ update_known_hosts(ctx); out: hostkeys_update_ctx_free(ctx); hostkeys_update_complete = 1; client_repledge(); } /* * Returns non-zero if the key is accepted by HostkeyAlgorithms. * Made slightly less trivial by the multiple RSA signature algorithm names. */ static int key_accepted_by_hostkeyalgs(const struct sshkey *key) { const char *ktype = sshkey_ssh_name(key); const char *hostkeyalgs = options.hostkeyalgorithms; if (key->type == KEY_UNSPEC) return 0; if (key->type == KEY_RSA && (match_pattern_list("rsa-sha2-256", hostkeyalgs, 0) == 1 || match_pattern_list("rsa-sha2-512", hostkeyalgs, 0) == 1)) return 1; return match_pattern_list(ktype, hostkeyalgs, 0) == 1; } /* * Handle hostkeys-00@openssh.com global request to inform the client of all * the server's hostkeys. The keys are checked against the user's * HostkeyAlgorithms preference before they are accepted. */ static int client_input_hostkeys(struct ssh *ssh) { const u_char *blob = NULL; size_t i, len = 0; struct sshbuf *buf = NULL; struct sshkey *key = NULL, **tmp; int r, prove_sent = 0; char *fp; static int hostkeys_seen = 0; /* XXX use struct ssh */ extern struct sockaddr_storage hostaddr; /* XXX from ssh.c */ struct hostkeys_update_ctx *ctx = NULL; u_int want; if (hostkeys_seen) fatal_f("server already sent hostkeys"); if (!can_update_hostkeys()) return 1; hostkeys_seen = 1; ctx = xcalloc(1, sizeof(*ctx)); while (ssh_packet_remaining(ssh) > 0) { sshkey_free(key); key = NULL; if ((r = sshpkt_get_string_direct(ssh, &blob, &len)) != 0) { error_fr(r, "parse key"); goto out; } if ((r = sshkey_from_blob(blob, len, &key)) != 0) { do_log2_fr(r, r == SSH_ERR_KEY_TYPE_UNKNOWN ? SYSLOG_LEVEL_DEBUG1 : SYSLOG_LEVEL_ERROR, "convert key"); continue; } fp = sshkey_fingerprint(key, options.fingerprint_hash, SSH_FP_DEFAULT); debug3_f("received %s key %s", sshkey_type(key), fp); free(fp); if (!key_accepted_by_hostkeyalgs(key)) { debug3_f("%s key not permitted by " "HostkeyAlgorithms", sshkey_ssh_name(key)); continue; } /* Skip certs */ if (sshkey_is_cert(key)) { debug3_f("%s key is a certificate; skipping", sshkey_ssh_name(key)); continue; } /* Ensure keys are unique */ for (i = 0; i < ctx->nkeys; i++) { if (sshkey_equal(key, ctx->keys[i])) { error_f("received duplicated %s host key", sshkey_ssh_name(key)); goto out; } } /* Key is good, record it */ if ((tmp = recallocarray(ctx->keys, ctx->nkeys, ctx->nkeys + 1, sizeof(*ctx->keys))) == NULL) fatal_f("recallocarray failed nkeys = %zu", ctx->nkeys); ctx->keys = tmp; ctx->keys[ctx->nkeys++] = key; key = NULL; } if (ctx->nkeys == 0) { debug_f("server sent no hostkeys"); goto out; } if ((ctx->keys_match = calloc(ctx->nkeys, sizeof(*ctx->keys_match))) == NULL || (ctx->keys_verified = calloc(ctx->nkeys, sizeof(*ctx->keys_verified))) == NULL) fatal_f("calloc failed"); get_hostfile_hostname_ipaddr(host, options.check_host_ip ? (struct sockaddr *)&hostaddr : NULL, options.port, &ctx->host_str, options.check_host_ip ? &ctx->ip_str : NULL); /* Find which keys we already know about. */ for (i = 0; i < options.num_user_hostfiles; i++) { debug_f("searching %s for %s / %s", options.user_hostfiles[i], ctx->host_str, ctx->ip_str ? ctx->ip_str : "(none)"); if ((r = hostkeys_foreach(options.user_hostfiles[i], hostkeys_find, ctx, ctx->host_str, ctx->ip_str, HKF_WANT_PARSE_KEY, 0)) != 0) { if (r == SSH_ERR_SYSTEM_ERROR && errno == ENOENT) { debug_f("hostkeys file %s does not exist", options.user_hostfiles[i]); continue; } error_fr(r, "hostkeys_foreach failed for %s", options.user_hostfiles[i]); goto out; } } /* Figure out if we have any new keys to add */ ctx->nnew = ctx->nincomplete = 0; want = HKF_MATCH_HOST | ( options.check_host_ip ? HKF_MATCH_IP : 0); for (i = 0; i < ctx->nkeys; i++) { if (ctx->keys_match[i] == 0) ctx->nnew++; if ((ctx->keys_match[i] & want) != want) ctx->nincomplete++; } debug3_f("%zu server keys: %zu new, %zu retained, " "%zu incomplete match. %zu to remove", ctx->nkeys, ctx->nnew, ctx->nkeys - ctx->nnew - ctx->nincomplete, ctx->nincomplete, ctx->nold); if (ctx->nnew == 0 && ctx->nold == 0) { debug_f("no new or deprecated keys from server"); goto out; } /* Various reasons why we cannot proceed with the update */ if (ctx->complex_hostspec) { debug_f("CA/revocation marker, manual host list or wildcard " "host pattern found, skipping UserKnownHostsFile update"); goto out; } if (ctx->other_name_seen) { debug_f("host key found matching a different name/address, " "skipping UserKnownHostsFile update"); goto out; } /* * If removing keys, check whether they appear under different * names/addresses and refuse to proceed if they do. This avoids * cases such as hosts with multiple names becoming inconsistent * with regards to CheckHostIP entries. * XXX UpdateHostkeys=force to override this (and other) checks? */ if (ctx->nold != 0) { if (check_old_keys_othernames(ctx) != 0) goto out; /* error already logged */ if (ctx->old_key_seen) { debug_f("key(s) for %s%s%s exist under other names; " "skipping UserKnownHostsFile update", ctx->host_str, ctx->ip_str == NULL ? "" : ",", ctx->ip_str == NULL ? "" : ctx->ip_str); goto out; } } if (ctx->nnew == 0) { /* * We have some keys to remove or fix matching for. * We can proceed to do this without requiring a fresh proof * from the server. */ update_known_hosts(ctx); goto out; } /* * We have received previously-unseen keys from the server. * Ask the server to confirm ownership of the private halves. */ debug3_f("asking server to prove ownership for %zu keys", ctx->nnew); if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || (r = sshpkt_put_cstring(ssh, "hostkeys-prove-00@openssh.com")) != 0 || (r = sshpkt_put_u8(ssh, 1)) != 0) /* bool: want reply */ fatal_fr(r, "prepare hostkeys-prove"); if ((buf = sshbuf_new()) == NULL) fatal_f("sshbuf_new"); for (i = 0; i < ctx->nkeys; i++) { if (ctx->keys_match[i]) continue; sshbuf_reset(buf); if ((r = sshkey_putb(ctx->keys[i], buf)) != 0 || (r = sshpkt_put_stringb(ssh, buf)) != 0) fatal_fr(r, "assemble hostkeys-prove"); } if ((r = sshpkt_send(ssh)) != 0) fatal_fr(r, "send hostkeys-prove"); client_register_global_confirm( client_global_hostkeys_prove_confirm, ctx); ctx = NULL; /* will be freed in callback */ prove_sent = 1; /* Success */ out: hostkeys_update_ctx_free(ctx); sshkey_free(key); sshbuf_free(buf); if (!prove_sent) { /* UpdateHostkeys handling completed */ hostkeys_update_complete = 1; client_repledge(); } /* * NB. Return success for all cases. The server doesn't need to know * what the client does with its hosts file. */ return 1; } static int client_input_global_request(int type, u_int32_t seq, struct ssh *ssh) { char *rtype; u_char want_reply; int r, success = 0; if ((r = sshpkt_get_cstring(ssh, &rtype, NULL)) != 0 || (r = sshpkt_get_u8(ssh, &want_reply)) != 0) goto out; debug("client_input_global_request: rtype %s want_reply %d", rtype, want_reply); if (strcmp(rtype, "hostkeys-00@openssh.com") == 0) success = client_input_hostkeys(ssh); if (want_reply) { if ((r = sshpkt_start(ssh, success ? SSH2_MSG_REQUEST_SUCCESS : SSH2_MSG_REQUEST_FAILURE)) != 0 || (r = sshpkt_send(ssh)) != 0 || (r = ssh_packet_write_wait(ssh)) != 0) goto out; } r = 0; out: free(rtype); return r; } static void client_send_env(struct ssh *ssh, int id, const char *name, const char *val) { int r; debug("channel %d: setting env %s = \"%s\"", id, name, val); channel_request_start(ssh, id, "env", 0); if ((r = sshpkt_put_cstring(ssh, name)) != 0 || (r = sshpkt_put_cstring(ssh, val)) != 0 || (r = sshpkt_send(ssh)) != 0) fatal_fr(r, "send setenv"); } void client_session2_setup(struct ssh *ssh, int id, int want_tty, int want_subsystem, const char *term, struct termios *tiop, int in_fd, struct sshbuf *cmd, char **env) { size_t i, j, len; int matched, r; char *name, *val; Channel *c = NULL; debug2_f("id %d", id); if ((c = channel_lookup(ssh, id)) == NULL) fatal_f("channel %d: unknown channel", id); ssh_packet_set_interactive(ssh, want_tty, options.ip_qos_interactive, options.ip_qos_bulk); if (want_tty) { struct winsize ws; /* Store window size in the packet. */ if (ioctl(in_fd, TIOCGWINSZ, &ws) == -1) memset(&ws, 0, sizeof(ws)); channel_request_start(ssh, id, "pty-req", 1); client_expect_confirm(ssh, id, "PTY allocation", CONFIRM_TTY); if ((r = sshpkt_put_cstring(ssh, term != NULL ? term : "")) != 0 || (r = sshpkt_put_u32(ssh, (u_int)ws.ws_col)) != 0 || (r = sshpkt_put_u32(ssh, (u_int)ws.ws_row)) != 0 || (r = sshpkt_put_u32(ssh, (u_int)ws.ws_xpixel)) != 0 || (r = sshpkt_put_u32(ssh, (u_int)ws.ws_ypixel)) != 0) fatal_fr(r, "build pty-req"); if (tiop == NULL) tiop = get_saved_tio(); ssh_tty_make_modes(ssh, -1, tiop); if ((r = sshpkt_send(ssh)) != 0) fatal_fr(r, "send pty-req"); /* XXX wait for reply */ c->client_tty = 1; } /* Transfer any environment variables from client to server */ if (options.num_send_env != 0 && env != NULL) { debug("Sending environment."); for (i = 0; env[i] != NULL; i++) { /* Split */ name = xstrdup(env[i]); if ((val = strchr(name, '=')) == NULL) { free(name); continue; } *val++ = '\0'; matched = 0; for (j = 0; j < options.num_send_env; j++) { if (match_pattern(name, options.send_env[j])) { matched = 1; break; } } if (!matched) { debug3("Ignored env %s", name); free(name); continue; } client_send_env(ssh, id, name, val); free(name); } } for (i = 0; i < options.num_setenv; i++) { /* Split */ name = xstrdup(options.setenv[i]); if ((val = strchr(name, '=')) == NULL) { free(name); continue; } *val++ = '\0'; client_send_env(ssh, id, name, val); free(name); } len = sshbuf_len(cmd); if (len > 0) { if (len > 900) len = 900; if (want_subsystem) { debug("Sending subsystem: %.*s", (int)len, (const u_char*)sshbuf_ptr(cmd)); channel_request_start(ssh, id, "subsystem", 1); client_expect_confirm(ssh, id, "subsystem", CONFIRM_CLOSE); } else { debug("Sending command: %.*s", (int)len, (const u_char*)sshbuf_ptr(cmd)); channel_request_start(ssh, id, "exec", 1); client_expect_confirm(ssh, id, "exec", CONFIRM_CLOSE); } if ((r = sshpkt_put_stringb(ssh, cmd)) != 0 || (r = sshpkt_send(ssh)) != 0) fatal_fr(r, "send command"); } else { channel_request_start(ssh, id, "shell", 1); client_expect_confirm(ssh, id, "shell", CONFIRM_CLOSE); if ((r = sshpkt_send(ssh)) != 0) fatal_fr(r, "send shell"); } session_setup_complete = 1; client_repledge(); } static void client_init_dispatch(struct ssh *ssh) { ssh_dispatch_init(ssh, &dispatch_protocol_error); ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_CLOSE, &channel_input_oclose); ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_DATA, &channel_input_data); ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_EOF, &channel_input_ieof); ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_EXTENDED_DATA, &channel_input_extended_data); ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_OPEN, &client_input_channel_open); ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation); ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure); ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_REQUEST, &client_input_channel_req); ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_WINDOW_ADJUST, &channel_input_window_adjust); ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_SUCCESS, &channel_input_status_confirm); ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_FAILURE, &channel_input_status_confirm); ssh_dispatch_set(ssh, SSH2_MSG_GLOBAL_REQUEST, &client_input_global_request); /* rekeying */ ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_input_kexinit); /* global request reply messages */ ssh_dispatch_set(ssh, SSH2_MSG_REQUEST_FAILURE, &client_global_request_reply); ssh_dispatch_set(ssh, SSH2_MSG_REQUEST_SUCCESS, &client_global_request_reply); } void client_stop_mux(void) { if (options.control_path != NULL && muxserver_sock != -1) unlink(options.control_path); /* * If we are in persist mode, or don't have a shell, signal that we * should close when all active channels are closed. */ if (options.control_persist || options.session_type == SESSION_TYPE_NONE) { session_closed = 1; setproctitle("[stopped mux]"); } } /* client specific fatal cleanup */ void cleanup_exit(int i) { leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); if (options.control_path != NULL && muxserver_sock != -1) unlink(options.control_path); ssh_kill_proxy_command(); _exit(i); } diff --git a/configure b/configure index d38df9738d71..5f5986e923de 100755 --- a/configure +++ b/configure @@ -1,24114 +1,24120 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.71 for OpenSSH Portable. # # Report bugs to . # # # Copyright (C) 1992-1996, 1998-2017, 2020-2021 Free Software Foundation, # Inc. # # # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh as_nop=: if test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else $as_nop case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi # Reset variables that may have inherited troublesome values from # the environment. # IFS needs to be set, to space, tab, and newline, in precisely that order. # (If _AS_PATH_WALK were called with IFS unset, it would have the # side effect of setting IFS to empty, thus disabling word splitting.) # Quoting is to prevent editors from complaining about space-tab. as_nl=' ' export as_nl IFS=" "" $as_nl" PS1='$ ' PS2='> ' PS4='+ ' # Ensure predictable behavior from utilities with locale-dependent output. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # We cannot yet rely on "unset" to work, but we need these variables # to be unset--not just set to an empty or harmless value--now, to # avoid bugs in old shells (e.g. pre-3.0 UWIN ksh). This construct # also avoids known problems related to "unset" and subshell syntax # in other old shells (e.g. bash 2.01 and pdksh 5.2.14). for as_var in BASH_ENV ENV MAIL MAILPATH CDPATH do eval test \${$as_var+y} \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done # Ensure that fds 0, 1, and 2 are open. if (exec 3>&0) 2>/dev/null; then :; else exec 0&1) 2>/dev/null; then :; else exec 1>/dev/null; fi if (exec 3>&2) ; then :; else exec 2>/dev/null; fi # The user is always right. if ${PATH_SEPARATOR+false} :; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac test -r "$as_dir$0" && as_myself=$as_dir$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then printf "%s\n" "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Use a proper internal environment variable to ensure we don't fall # into an infinite loop, continuously re-executing ourselves. if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then _as_can_reexec=no; export _as_can_reexec; # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi # We don't want this to propagate to other subprocesses. { _as_can_reexec=; unset _as_can_reexec;} if test "x$CONFIG_SHELL" = x; then as_bourne_compatible="as_nop=: if test \${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else \$as_nop case \`(set -o) 2>/dev/null\` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi " as_required="as_fn_return () { (exit \$1); } as_fn_success () { as_fn_return 0; } as_fn_failure () { as_fn_return 1; } as_fn_ret_success () { return 0; } as_fn_ret_failure () { return 1; } exitcode=0 as_fn_success || { exitcode=1; echo as_fn_success failed.; } as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } if ( set x; as_fn_ret_success y && test x = \"\$1\" ) then : else \$as_nop exitcode=1; echo positional parameters were not saved. fi test x\$exitcode = x0 || exit 1 blah=\$(echo \$(echo blah)) test x\"\$blah\" = xblah || exit 1 test -x / || exit 1" as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 test \$(( 1 + 1 )) = 2 || exit 1" if (eval "$as_required") 2>/dev/null then : as_have_required=yes else $as_nop as_have_required=no fi if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null then : else $as_nop as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac as_found=: case $as_dir in #( /*) for as_base in sh bash ksh sh5; do # Try only shells that exist, to save several forks. as_shell=$as_dir$as_base if { test -f "$as_shell" || test -f "$as_shell.exe"; } && as_run=a "$as_shell" -c "$as_bourne_compatible""$as_required" 2>/dev/null then : CONFIG_SHELL=$as_shell as_have_required=yes if as_run=a "$as_shell" -c "$as_bourne_compatible""$as_suggested" 2>/dev/null then : break 2 fi fi done;; esac as_found=false done IFS=$as_save_IFS if $as_found then : else $as_nop if { test -f "$SHELL" || test -f "$SHELL.exe"; } && as_run=a "$SHELL" -c "$as_bourne_compatible""$as_required" 2>/dev/null then : CONFIG_SHELL=$SHELL as_have_required=yes fi fi if test "x$CONFIG_SHELL" != x then : export CONFIG_SHELL # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi if test x$as_have_required = xno then : printf "%s\n" "$0: This script requires a shell more modern than all" printf "%s\n" "$0: the shells that I found on your system." if test ${ZSH_VERSION+y} ; then printf "%s\n" "$0: In particular, zsh $ZSH_VERSION has bugs and should" printf "%s\n" "$0: be upgraded to zsh 4.3.4 or later." else printf "%s\n" "$0: Please tell bug-autoconf@gnu.org and $0: openssh-unix-dev@mindrot.org about your system, $0: including any error possibly output before this $0: message. Then install a modern shell, or manually run $0: the script under such a shell if you do have one." fi exit 1 fi fi fi SHELL=${CONFIG_SHELL-/bin/sh} export SHELL # Unset more variables known to interfere with behavior of common tools. CLICOLOR_FORCE= GREP_OPTIONS= unset CLICOLOR_FORCE GREP_OPTIONS ## --------------------- ## ## M4sh Shell Functions. ## ## --------------------- ## # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_nop # --------- # Do nothing but, unlike ":", preserve the value of $?. as_fn_nop () { return $? } as_nop=as_fn_nop # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`printf "%s\n" "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null then : eval 'as_fn_append () { eval $1+=\$2 }' else $as_nop as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null then : eval 'as_fn_arith () { as_val=$(( $* )) }' else $as_nop as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith # as_fn_nop # --------- # Do nothing but, unlike ":", preserve the value of $?. as_fn_nop () { return $? } as_nop=as_fn_nop # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi printf "%s\n" "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits as_lineno_1=$LINENO as_lineno_1a=$LINENO as_lineno_2=$LINENO as_lineno_2a=$LINENO eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) sed -n ' p /[$]LINENO/= ' <$as_myself | sed ' s/[$]LINENO.*/&-/ t lineno b :lineno N :loop s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ t loop s/-\n.*// ' >$as_me.lineno && chmod +x "$as_me.lineno" || { printf "%s\n" "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } # If we had to re-execute with $CONFIG_SHELL, we're ensured to have # already done that, so ensure we don't try to do so again and fall # in an infinite loop. This has already happened in practice. _as_can_reexec=no; export _as_can_reexec # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensitive to this). . "./$as_me.lineno" # Exit status is that of the last command. exit } # Determine whether it's possible to make 'echo' print without a newline. # These variables are no longer used directly by Autoconf, but are AC_SUBSTed # for compatibility with existing Makefiles. ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac # For backward compatibility with old third-party macros, we provide # the shell variables $as_echo and $as_echo_n. New code should use # AS_ECHO(["message"]) and AS_ECHO_N(["message"]), respectively. as_echo='printf %s\n' as_echo_n='printf %s' rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" test -n "$DJDIR" || exec 7<&0 &1 # Name of the host. # hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, # so uname gets run too. ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` # # Initializations. # ac_default_prefix=/usr/local ac_clean_files= ac_config_libobj_dir=. LIBOBJS= cross_compiling=no subdirs= MFLAGS= MAKEFLAGS= # Identity of this package. PACKAGE_NAME='OpenSSH' PACKAGE_TARNAME='openssh' PACKAGE_VERSION='Portable' PACKAGE_STRING='OpenSSH Portable' PACKAGE_BUGREPORT='openssh-unix-dev@mindrot.org' PACKAGE_URL='' ac_unique_file="ssh.c" # Factoring default headers for most tests. ac_includes_default="\ #include #ifdef HAVE_STDIO_H # include #endif #ifdef HAVE_STDLIB_H # include #endif #ifdef HAVE_STRING_H # include #endif #ifdef HAVE_INTTYPES_H # include #endif #ifdef HAVE_STDINT_H # include #endif #ifdef HAVE_STRINGS_H # include #endif #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef HAVE_UNISTD_H # include #endif" ac_header_c_list= ac_subst_vars='LTLIBOBJS CFLAGS_NOPIE LDFLAGS_NOPIE DEPEND UNSUPPORTED_ALGORITHMS TEST_MALLOC_OPTIONS TEST_SSH_UTF8 TEST_SSH_IPV6 piddir user_path mansubdir MANTYPE XAUTH_PATH STRIP_OPT xauth_path PRIVSEP_PATH CHANNELLIBS K5LIBS GSSLIBS KRB5CONF SSHDLIBS SSH_PRIVSEP_USER LIBFIDO2 SK_DUMMY_LIBRARY OPENSSL_BIN openssl_bin PICFLAG LIBEDIT LDNSCONFIG LIBOBJS LD PATH_PASSWD_PROG STARTUP_SCRIPT_SHELL MAKE_PACKAGE_SUPPORTED PATH_USERADD_PROG PATH_GROUPADD_PROG MANFMT TEST_SHELL PKGCONFIG MANDOC NROFF GROFF SH TEST_MINUS_S_SH SED KILL CAT ac_ct_AR AR MKDIR_P EGREP GREP INSTALL_DATA INSTALL_SCRIPT INSTALL_PROGRAM RANLIB CPP AWK host_os host_vendor host_cpu host build_os build_vendor build_cpu build OBJEXT EXEEXT ac_ct_CC CPPFLAGS LDFLAGS CFLAGS CC target_alias host_alias build_alias LIBS ECHO_T ECHO_N ECHO_C DEFS mandir localedir libdir psdir pdfdir dvidir htmldir infodir docdir oldincludedir includedir runstatedir localstatedir sharedstatedir sysconfdir datadir datarootdir libexecdir sbindir bindir program_transform_name prefix exec_prefix PACKAGE_URL PACKAGE_BUGREPORT PACKAGE_STRING PACKAGE_VERSION PACKAGE_TARNAME PACKAGE_NAME PATH_SEPARATOR SHELL' ac_subst_files='' ac_user_opts=' enable_option_checking enable_largefile with_openssl with_stackprotect with_hardening with_rpath with_cflags with_cflags_after with_cppflags with_ldflags with_ldflags_after with_libs with_Werror with_solaris_contracts with_solaris_projects with_solaris_privs with_osfsia with_zlib with_zlib_version_check with_ldns with_libedit with_audit with_pie enable_pkcs11 enable_security_key with_security_key_builtin with_ssl_dir with_openssl_header_check with_ssl_engine with_prngd_port with_prngd_socket with_pam with_pam_service with_privsep_user with_sandbox with_selinux with_kerberos5 with_privsep_path with_xauth enable_strip with_maildir with_mantype with_shadow with_ipaddr_display enable_etc_default_login with_default_path with_superuser_path with_4in6 with_bsd_auth with_pid_dir enable_lastlog enable_utmp enable_utmpx enable_wtmp enable_wtmpx enable_libutil enable_pututline enable_pututxline with_lastlog ' ac_precious_vars='build_alias host_alias target_alias CC CFLAGS LDFLAGS LIBS CPPFLAGS CPP' # Initialize some variables set by options. ac_init_help= ac_init_version=false ac_unrecognized_opts= ac_unrecognized_sep= # The variables have the same names as the options, with # dashes changed to underlines. cache_file=/dev/null exec_prefix=NONE no_create= no_recursion= prefix=NONE program_prefix=NONE program_suffix=NONE program_transform_name=s,x,x, silent= site= srcdir= verbose= x_includes=NONE x_libraries=NONE # Installation directory options. # These are left unexpanded so users can "make install exec_prefix=/foo" # and all the variables that are supposed to be based on exec_prefix # by default will actually change. # Use braces instead of parens because sh, perl, etc. also accept them. # (The list follows the same order as the GNU Coding Standards.) bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datarootdir='${prefix}/share' datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' runstatedir='${localstatedir}/run' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' infodir='${datarootdir}/info' htmldir='${docdir}' dvidir='${docdir}' pdfdir='${docdir}' psdir='${docdir}' libdir='${exec_prefix}/lib' localedir='${datarootdir}/locale' mandir='${datarootdir}/man' ac_prev= ac_dashdash= for ac_option do # If the previous option needs an argument, assign it. if test -n "$ac_prev"; then eval $ac_prev=\$ac_option ac_prev= continue fi case $ac_option in *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; *=) ac_optarg= ;; *) ac_optarg=yes ;; esac case $ac_dashdash$ac_option in --) ac_dashdash=yes ;; -bindir | --bindir | --bindi | --bind | --bin | --bi) ac_prev=bindir ;; -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) bindir=$ac_optarg ;; -build | --build | --buil | --bui | --bu) ac_prev=build_alias ;; -build=* | --build=* | --buil=* | --bui=* | --bu=*) build_alias=$ac_optarg ;; -cache-file | --cache-file | --cache-fil | --cache-fi \ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) ac_prev=cache_file ;; -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) cache_file=$ac_optarg ;; --config-cache | -C) cache_file=config.cache ;; -datadir | --datadir | --datadi | --datad) ac_prev=datadir ;; -datadir=* | --datadir=* | --datadi=* | --datad=*) datadir=$ac_optarg ;; -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ | --dataroo | --dataro | --datar) ac_prev=datarootdir ;; -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) datarootdir=$ac_optarg ;; -disable-* | --disable-*) ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: \`$ac_useropt'" ac_useropt_orig=$ac_useropt ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=no ;; -docdir | --docdir | --docdi | --doc | --do) ac_prev=docdir ;; -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) docdir=$ac_optarg ;; -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) ac_prev=dvidir ;; -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) dvidir=$ac_optarg ;; -enable-* | --enable-*) ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: \`$ac_useropt'" ac_useropt_orig=$ac_useropt ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=\$ac_optarg ;; -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ | --exec | --exe | --ex) ac_prev=exec_prefix ;; -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ | --exec=* | --exe=* | --ex=*) exec_prefix=$ac_optarg ;; -gas | --gas | --ga | --g) # Obsolete; use --with-gas. with_gas=yes ;; -help | --help | --hel | --he | -h) ac_init_help=long ;; -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) ac_init_help=recursive ;; -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) ac_init_help=short ;; -host | --host | --hos | --ho) ac_prev=host_alias ;; -host=* | --host=* | --hos=* | --ho=*) host_alias=$ac_optarg ;; -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) ac_prev=htmldir ;; -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ | --ht=*) htmldir=$ac_optarg ;; -includedir | --includedir | --includedi | --included | --include \ | --includ | --inclu | --incl | --inc) ac_prev=includedir ;; -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ | --includ=* | --inclu=* | --incl=* | --inc=*) includedir=$ac_optarg ;; -infodir | --infodir | --infodi | --infod | --info | --inf) ac_prev=infodir ;; -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) infodir=$ac_optarg ;; -libdir | --libdir | --libdi | --libd) ac_prev=libdir ;; -libdir=* | --libdir=* | --libdi=* | --libd=*) libdir=$ac_optarg ;; -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ | --libexe | --libex | --libe) ac_prev=libexecdir ;; -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ | --libexe=* | --libex=* | --libe=*) libexecdir=$ac_optarg ;; -localedir | --localedir | --localedi | --localed | --locale) ac_prev=localedir ;; -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) localedir=$ac_optarg ;; -localstatedir | --localstatedir | --localstatedi | --localstated \ | --localstate | --localstat | --localsta | --localst | --locals) ac_prev=localstatedir ;; -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) localstatedir=$ac_optarg ;; -mandir | --mandir | --mandi | --mand | --man | --ma | --m) ac_prev=mandir ;; -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) mandir=$ac_optarg ;; -nfp | --nfp | --nf) # Obsolete; use --without-fp. with_fp=no ;; -no-create | --no-create | --no-creat | --no-crea | --no-cre \ | --no-cr | --no-c | -n) no_create=yes ;; -no-recursion | --no-recursion | --no-recursio | --no-recursi \ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) no_recursion=yes ;; -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ | --oldin | --oldi | --old | --ol | --o) ac_prev=oldincludedir ;; -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) oldincludedir=$ac_optarg ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ac_prev=prefix ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) prefix=$ac_optarg ;; -program-prefix | --program-prefix | --program-prefi | --program-pref \ | --program-pre | --program-pr | --program-p) ac_prev=program_prefix ;; -program-prefix=* | --program-prefix=* | --program-prefi=* \ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) program_prefix=$ac_optarg ;; -program-suffix | --program-suffix | --program-suffi | --program-suff \ | --program-suf | --program-su | --program-s) ac_prev=program_suffix ;; -program-suffix=* | --program-suffix=* | --program-suffi=* \ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) program_suffix=$ac_optarg ;; -program-transform-name | --program-transform-name \ | --program-transform-nam | --program-transform-na \ | --program-transform-n | --program-transform- \ | --program-transform | --program-transfor \ | --program-transfo | --program-transf \ | --program-trans | --program-tran \ | --progr-tra | --program-tr | --program-t) ac_prev=program_transform_name ;; -program-transform-name=* | --program-transform-name=* \ | --program-transform-nam=* | --program-transform-na=* \ | --program-transform-n=* | --program-transform-=* \ | --program-transform=* | --program-transfor=* \ | --program-transfo=* | --program-transf=* \ | --program-trans=* | --program-tran=* \ | --progr-tra=* | --program-tr=* | --program-t=*) program_transform_name=$ac_optarg ;; -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) ac_prev=pdfdir ;; -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) pdfdir=$ac_optarg ;; -psdir | --psdir | --psdi | --psd | --ps) ac_prev=psdir ;; -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) psdir=$ac_optarg ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) silent=yes ;; -runstatedir | --runstatedir | --runstatedi | --runstated \ | --runstate | --runstat | --runsta | --runst | --runs \ | --run | --ru | --r) ac_prev=runstatedir ;; -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ | --run=* | --ru=* | --r=*) runstatedir=$ac_optarg ;; -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ | --sbi=* | --sb=*) sbindir=$ac_optarg ;; -sharedstatedir | --sharedstatedir | --sharedstatedi \ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ | --sharedst | --shareds | --shared | --share | --shar \ | --sha | --sh) ac_prev=sharedstatedir ;; -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ | --sha=* | --sh=*) sharedstatedir=$ac_optarg ;; -site | --site | --sit) ac_prev=site ;; -site=* | --site=* | --sit=*) site=$ac_optarg ;; -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ac_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) srcdir=$ac_optarg ;; -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ | --syscon | --sysco | --sysc | --sys | --sy) ac_prev=sysconfdir ;; -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) sysconfdir=$ac_optarg ;; -target | --target | --targe | --targ | --tar | --ta | --t) ac_prev=target_alias ;; -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) target_alias=$ac_optarg ;; -v | -verbose | --verbose | --verbos | --verbo | --verb) verbose=yes ;; -version | --version | --versio | --versi | --vers | -V) ac_init_version=: ;; -with-* | --with-*) ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: \`$ac_useropt'" ac_useropt_orig=$ac_useropt ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=\$ac_optarg ;; -without-* | --without-*) ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: \`$ac_useropt'" ac_useropt_orig=$ac_useropt ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=no ;; --x) # Obsolete; use --with-x. with_x=yes ;; -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ | --x-incl | --x-inc | --x-in | --x-i) ac_prev=x_includes ;; -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) x_includes=$ac_optarg ;; -x-libraries | --x-libraries | --x-librarie | --x-librari \ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) ac_prev=x_libraries ;; -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; -*) as_fn_error $? "unrecognized option: \`$ac_option' Try \`$0 --help' for more information" ;; *=*) ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` # Reject names that are not valid shell variable names. case $ac_envvar in #( '' | [0-9]* | *[!_$as_cr_alnum]* ) as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; esac eval $ac_envvar=\$ac_optarg export $ac_envvar ;; *) # FIXME: should be removed in autoconf 3.0. printf "%s\n" "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && printf "%s\n" "$as_me: WARNING: invalid host type: $ac_option" >&2 : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" ;; esac done if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` as_fn_error $? "missing argument to $ac_option" fi if test -n "$ac_unrecognized_opts"; then case $enable_option_checking in no) ;; fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; *) printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; esac fi # Check all directory arguments for consistency. for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ libdir localedir mandir runstatedir do eval ac_val=\$$ac_var # Remove trailing slashes. case $ac_val in */ ) ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` eval $ac_var=\$ac_val;; esac # Be sure to have absolute directory names. case $ac_val in [\\/$]* | ?:[\\/]* ) continue;; NONE | '' ) case $ac_var in *prefix ) continue;; esac;; esac as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" done # There might be people who depend on the old broken behavior: `$host' # used to hold the argument of --host etc. # FIXME: To remove some day. build=$build_alias host=$host_alias target=$target_alias # FIXME: To remove some day. if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi fi ac_tool_prefix= test -n "$host_alias" && ac_tool_prefix=$host_alias- test "$silent" = yes && exec 6>/dev/null ac_pwd=`pwd` && test -n "$ac_pwd" && ac_ls_di=`ls -di .` && ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || as_fn_error $? "working directory cannot be determined" test "X$ac_ls_di" = "X$ac_pwd_ls_di" || as_fn_error $? "pwd does not report name of working directory" # Find the source files, if location was not specified. if test -z "$srcdir"; then ac_srcdir_defaulted=yes # Try the directory containing this script, then the parent directory. ac_confdir=`$as_dirname -- "$as_myself" || $as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_myself" : 'X\(//\)[^/]' \| \ X"$as_myself" : 'X\(//\)$' \| \ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X"$as_myself" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` srcdir=$ac_confdir if test ! -r "$srcdir/$ac_unique_file"; then srcdir=.. fi else ac_srcdir_defaulted=no fi if test ! -r "$srcdir/$ac_unique_file"; then test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" fi ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" ac_abs_confdir=`( cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" pwd)` # When building in place, set srcdir=. if test "$ac_abs_confdir" = "$ac_pwd"; then srcdir=. fi # Remove unnecessary trailing slashes from srcdir. # Double slashes in file names in object file debugging info # mess up M-x gdb in Emacs. case $srcdir in */) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; esac for ac_var in $ac_precious_vars; do eval ac_env_${ac_var}_set=\${${ac_var}+set} eval ac_env_${ac_var}_value=\$${ac_var} eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} eval ac_cv_env_${ac_var}_value=\$${ac_var} done # # Report the --help message. # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF \`configure' configures OpenSSH Portable to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. Defaults for the options are specified in brackets. Configuration: -h, --help display this help and exit --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit -q, --quiet, --silent do not print \`checking ...' messages --cache-file=FILE cache test results in FILE [disabled] -C, --config-cache alias for \`--cache-file=config.cache' -n, --no-create do not create output files --srcdir=DIR find the sources in DIR [configure dir or \`..'] Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX [$ac_default_prefix] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [PREFIX] By default, \`make install' will install all the files in \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify an installation prefix other than \`$ac_default_prefix' using \`--prefix', for instance \`--prefix=\$HOME'. For better control, use the options below. Fine tuning of the installation directories: --bindir=DIR user executables [EPREFIX/bin] --sbindir=DIR system admin executables [EPREFIX/sbin] --libexecdir=DIR program executables [EPREFIX/libexec] --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] --datadir=DIR read-only architecture-independent data [DATAROOTDIR] --infodir=DIR info documentation [DATAROOTDIR/info] --localedir=DIR locale-dependent data [DATAROOTDIR/locale] --mandir=DIR man documentation [DATAROOTDIR/man] --docdir=DIR documentation root [DATAROOTDIR/doc/openssh] --htmldir=DIR html documentation [DOCDIR] --dvidir=DIR dvi documentation [DOCDIR] --pdfdir=DIR pdf documentation [DOCDIR] --psdir=DIR ps documentation [DOCDIR] _ACEOF cat <<\_ACEOF System types: --build=BUILD configure for building on BUILD [guessed] --host=HOST cross-compile to build programs to run on HOST [BUILD] _ACEOF fi if test -n "$ac_init_help"; then case $ac_init_help in short | recursive ) echo "Configuration of OpenSSH Portable:";; esac cat <<\_ACEOF Optional Features: --disable-option-checking ignore unrecognized --enable/--with options --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --disable-largefile omit support for large files --disable-pkcs11 disable PKCS#11 support code [no] --disable-security-key disable U2F/FIDO support code no --disable-strip Disable calling strip(1) on install --disable-etc-default-login Disable using PATH from /etc/default/login no --disable-lastlog disable use of lastlog even if detected no --disable-utmp disable use of utmp even if detected no --disable-utmpx disable use of utmpx even if detected no --disable-wtmp disable use of wtmp even if detected no --disable-wtmpx disable use of wtmpx even if detected no --disable-libutil disable use of libutil (login() etc.) no --disable-pututline disable use of pututline() etc. (uwtmp) no --disable-pututxline disable use of pututxline() etc. (uwtmpx) no Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) --without-openssl Disable use of OpenSSL; use only limited internal crypto **EXPERIMENTAL** --without-stackprotect Don't use compiler's stack protection --without-hardening Don't use toolchain hardening flags --without-rpath Disable auto-added -R linker paths --with-cflags Specify additional flags to pass to compiler --with-cflags-after Specify additional flags to pass to compiler after configure --with-cppflags Specify additional flags to pass to preprocessor --with-ldflags Specify additional flags to pass to linker --with-ldflags-after Specify additional flags to pass to linker after configure --with-libs Specify additional libraries to link with --with-Werror Build main code with -Werror --with-solaris-contracts Enable Solaris process contracts (experimental) --with-solaris-projects Enable Solaris projects (experimental) --with-solaris-privs Enable Solaris/Illumos privileges (experimental) --with-osfsia Enable Digital Unix SIA --with-zlib=PATH Use zlib in PATH --without-zlib-version-check Disable zlib version check --with-ldns[=PATH] Use ldns for DNSSEC support (optionally in PATH) --with-libedit[=PATH] Enable libedit support for sftp --with-audit=module Enable audit support (modules=debug,bsm,linux) --with-pie Build Position Independent Executables if possible --with-security-key-builtin include builtin U2F/FIDO support --with-ssl-dir=PATH Specify path to OpenSSL installation --without-openssl-header-check Disable OpenSSL version consistency check --with-ssl-engine Enable OpenSSL (hardware) ENGINE support --with-prngd-port=PORT read entropy from PRNGD/EGD TCP localhost:PORT --with-prngd-socket=FILE read entropy from PRNGD/EGD socket FILE (default=/var/run/egd-pool) --with-pam Enable PAM support --with-pam-service=name Specify PAM service name --with-privsep-user=user Specify non-privileged user for privilege separation --with-sandbox=style Specify privilege separation sandbox (no, capsicum, darwin, rlimit, seccomp_filter, systrace, pledge) --with-selinux Enable SELinux support --with-kerberos5=PATH Enable Kerberos 5 support --with-privsep-path=xxx Path for privilege separation chroot (default=/var/empty) --with-xauth=PATH Specify path to xauth program --with-maildir=/path/to/mail Specify your system mail directory --with-mantype=man|cat|doc Set man page type --without-shadow Disable shadow password support --with-ipaddr-display Use ip address instead of hostname in $DISPLAY --with-default-path= Specify default $PATH environment for server --with-superuser-path= Specify different path for super-user --with-4in6 Check for and convert IPv4 in IPv6 mapped addresses --with-bsd-auth Enable BSD auth support --with-pid-dir=PATH Specify location of sshd.pid file --with-lastlog=FILE|DIR specify lastlog location common locations Some influential environment variables: CC C compiler command CFLAGS C compiler flags LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory LIBS libraries to pass to the linker, e.g. -l CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory CPP C preprocessor Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. Report bugs to . _ACEOF ac_status=$? fi if test "$ac_init_help" = "recursive"; then # If there are subdirs, report their specific --help. for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue test -d "$ac_dir" || { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || continue ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`printf "%s\n" "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix cd "$ac_dir" || { ac_status=$?; continue; } # Check for configure.gnu first; this name is used for a wrapper for # Metaconfig's "Configure" on case-insensitive file systems. if test -f "$ac_srcdir/configure.gnu"; then echo && $SHELL "$ac_srcdir/configure.gnu" --help=recursive elif test -f "$ac_srcdir/configure"; then echo && $SHELL "$ac_srcdir/configure" --help=recursive else printf "%s\n" "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi || ac_status=$? cd "$ac_pwd" || { ac_status=$?; break; } done fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF OpenSSH configure Portable generated by GNU Autoconf 2.71 Copyright (C) 2021 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF exit fi ## ------------------------ ## ## Autoconf initialization. ## ## ------------------------ ## # ac_fn_c_try_compile LINENO # -------------------------- # Try to compile conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext conftest.beam if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext then : ac_retval=0 else $as_nop printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_compile # ac_fn_c_try_run LINENO # ---------------------- # Try to run conftest.$ac_ext, and return whether this succeeded. Assumes that # executables *can* be run. ac_fn_c_try_run () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; } then : ac_retval=0 else $as_nop printf "%s\n" "$as_me: program exited with status $ac_status" >&5 printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=$ac_status fi rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_run # ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists and can be compiled using the include files in # INCLUDES, setting the cache variable VAR accordingly. ac_fn_c_check_header_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 printf %s "checking for $2... " >&6; } if eval test \${$3+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_c_try_compile "$LINENO" then : eval "$3=yes" else $as_nop eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi eval ac_res=\$$3 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 printf "%s\n" "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_compile # ac_fn_c_try_cpp LINENO # ---------------------- # Try to preprocess conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_cpp () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } > conftest.i && { test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || test ! -s conftest.err } then : ac_retval=0 else $as_nop printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_cpp # ac_fn_check_decl LINENO SYMBOL VAR INCLUDES EXTRA-OPTIONS FLAG-VAR # ------------------------------------------------------------------ # Tests whether SYMBOL is declared in INCLUDES, setting cache variable VAR # accordingly. Pass EXTRA-OPTIONS to the compiler, using FLAG-VAR. ac_fn_check_decl () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack as_decl_name=`echo $2|sed 's/ *(.*//'` { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $as_decl_name is declared" >&5 printf %s "checking whether $as_decl_name is declared... " >&6; } if eval test \${$3+y} then : printf %s "(cached) " >&6 else $as_nop as_decl_use=`echo $2|sed -e 's/(/((/' -e 's/)/) 0&/' -e 's/,/) 0& (/g'` eval ac_save_FLAGS=\$$6 as_fn_append $6 " $5" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main (void) { #ifndef $as_decl_name #ifdef __cplusplus (void) $as_decl_use; #else (void) $as_decl_name; #endif #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : eval "$3=yes" else $as_nop eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext eval $6=\$ac_save_FLAGS fi eval ac_res=\$$3 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 printf "%s\n" "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_check_decl # ac_fn_c_try_link LINENO # ----------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_link () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext conftest.beam conftest$ac_exeext if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || test -x conftest$ac_exeext } then : ac_retval=0 else $as_nop printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would # interfere with the next link command; also delete a directory that is # left behind by Apple's compiler. We do this before executing the actions. rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_link # ac_fn_c_check_func LINENO FUNC VAR # ---------------------------------- # Tests whether FUNC exists, setting the cache variable VAR accordingly ac_fn_c_check_func () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 printf %s "checking for $2... " >&6; } if eval test \${$3+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Define $2 to an innocuous variant, in case declares $2. For example, HP-UX 11i declares gettimeofday. */ #define $2 innocuous_$2 /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $2 (); below. */ #include #undef $2 /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char $2 (); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined __stub_$2 || defined __stub___$2 choke me #endif int main (void) { return $2 (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : eval "$3=yes" else $as_nop eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext fi eval ac_res=\$$3 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 printf "%s\n" "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_func # ac_fn_c_check_type LINENO TYPE VAR INCLUDES # ------------------------------------------- # Tests whether TYPE exists after having included INCLUDES, setting cache # variable VAR accordingly. ac_fn_c_check_type () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 printf %s "checking for $2... " >&6; } if eval test \${$3+y} then : printf %s "(cached) " >&6 else $as_nop eval "$3=no" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main (void) { if (sizeof ($2)) return 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main (void) { if (sizeof (($2))) return 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : else $as_nop eval "$3=yes" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi eval ac_res=\$$3 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 printf "%s\n" "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_type # ac_fn_c_check_member LINENO AGGR MEMBER VAR INCLUDES # ---------------------------------------------------- # Tries to find if the field MEMBER exists in type AGGR, after including # INCLUDES, setting cache variable VAR accordingly. ac_fn_c_check_member () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2.$3" >&5 printf %s "checking for $2.$3... " >&6; } if eval test \${$4+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $5 int main (void) { static $2 ac_aggr; if (ac_aggr.$3) return 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : eval "$4=yes" else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $5 int main (void) { static $2 ac_aggr; if (sizeof ac_aggr.$3) return 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : eval "$4=yes" else $as_nop eval "$4=no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi eval ac_res=\$$4 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 printf "%s\n" "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_member # ac_fn_c_compute_int LINENO EXPR VAR INCLUDES # -------------------------------------------- # Tries to find the compile-time value of EXPR in a program that includes # INCLUDES, setting VAR accordingly. Returns whether the value could be # computed ac_fn_c_compute_int () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if test "$cross_compiling" = yes; then # Depending upon the size, compute the lo and hi bounds. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main (void) { static int test_array [1 - 2 * !(($2) >= 0)]; test_array [0] = 0; return test_array [0]; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_lo=0 ac_mid=0 while :; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main (void) { static int test_array [1 - 2 * !(($2) <= $ac_mid)]; test_array [0] = 0; return test_array [0]; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_hi=$ac_mid; break else $as_nop as_fn_arith $ac_mid + 1 && ac_lo=$as_val if test $ac_lo -le $ac_mid; then ac_lo= ac_hi= break fi as_fn_arith 2 '*' $ac_mid + 1 && ac_mid=$as_val fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext done else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main (void) { static int test_array [1 - 2 * !(($2) < 0)]; test_array [0] = 0; return test_array [0]; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_hi=-1 ac_mid=-1 while :; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main (void) { static int test_array [1 - 2 * !(($2) >= $ac_mid)]; test_array [0] = 0; return test_array [0]; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_lo=$ac_mid; break else $as_nop as_fn_arith '(' $ac_mid ')' - 1 && ac_hi=$as_val if test $ac_mid -le $ac_hi; then ac_lo= ac_hi= break fi as_fn_arith 2 '*' $ac_mid && ac_mid=$as_val fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext done else $as_nop ac_lo= ac_hi= fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext # Binary search between lo and hi bounds. while test "x$ac_lo" != "x$ac_hi"; do as_fn_arith '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo && ac_mid=$as_val cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main (void) { static int test_array [1 - 2 * !(($2) <= $ac_mid)]; test_array [0] = 0; return test_array [0]; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_hi=$ac_mid else $as_nop as_fn_arith '(' $ac_mid ')' + 1 && ac_lo=$as_val fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext done case $ac_lo in #(( ?*) eval "$3=\$ac_lo"; ac_retval=0 ;; '') ac_retval=1 ;; esac else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 static long int longval (void) { return $2; } static unsigned long int ulongval (void) { return $2; } #include #include int main (void) { FILE *f = fopen ("conftest.val", "w"); if (! f) return 1; if (($2) < 0) { long int i = longval (); if (i != ($2)) return 1; fprintf (f, "%ld", i); } else { unsigned long int i = ulongval (); if (i != ($2)) return 1; fprintf (f, "%lu", i); } /* Do not output a trailing newline, as this causes \r\n confusion on some platforms. */ return ferror (f) || fclose (f) != 0; ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO" then : echo >>conftest.val; read $3 config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by OpenSSH $as_me Portable, which was generated by GNU Autoconf 2.71. Invocation command line was $ $0$ac_configure_args_raw _ACEOF exec 5>>config.log { cat <<_ASUNAME ## --------- ## ## Platform. ## ## --------- ## hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` /bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` /bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` /usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` /bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` /bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` _ASUNAME as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac printf "%s\n" "PATH: $as_dir" done IFS=$as_save_IFS } >&5 cat >&5 <<_ACEOF ## ----------- ## ## Core tests. ## ## ----------- ## _ACEOF # Keep a trace of the command line. # Strip out --no-create and --no-recursion so they do not pile up. # Strip out --silent because we don't want to record it for future runs. # Also quote any args containing shell meta-characters. # Make two passes to allow for proper duplicate-argument suppression. ac_configure_args= ac_configure_args0= ac_configure_args1= ac_must_keep_next=false for ac_pass in 1 2 do for ac_arg do case $ac_arg in -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) continue ;; *\'*) ac_arg=`printf "%s\n" "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac case $ac_pass in 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; 2) as_fn_append ac_configure_args1 " '$ac_arg'" if test $ac_must_keep_next = true; then ac_must_keep_next=false # Got value, back to normal. else case $ac_arg in *=* | --config-cache | -C | -disable-* | --disable-* \ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ | -with-* | --with-* | -without-* | --without-* | --x) case "$ac_configure_args0 " in "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; esac ;; -* ) ac_must_keep_next=true ;; esac fi as_fn_append ac_configure_args " '$ac_arg'" ;; esac done done { ac_configure_args0=; unset ac_configure_args0;} { ac_configure_args1=; unset ac_configure_args1;} # When interrupted or exit'd, cleanup temporary files, and complete # config.log. We remove comments because anyway the quotes in there # would cause problems or look ugly. # WARNING: Use '\'' to represent an apostrophe within the trap. # WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. trap 'exit_status=$? # Sanitize IFS. IFS=" "" $as_nl" # Save into config.log some information that might help in debugging. { echo printf "%s\n" "## ---------------- ## ## Cache variables. ## ## ---------------- ##" echo # The following way of writing the cache mishandles newlines in values, ( for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 printf "%s\n" "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( *${as_nl}ac_space=\ *) sed -n \ "s/'\''/'\''\\\\'\'''\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" ;; #( *) sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) echo printf "%s\n" "## ----------------- ## ## Output variables. ## ## ----------------- ##" echo for ac_var in $ac_subst_vars do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`printf "%s\n" "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac printf "%s\n" "$ac_var='\''$ac_val'\''" done | sort echo if test -n "$ac_subst_files"; then printf "%s\n" "## ------------------- ## ## File substitutions. ## ## ------------------- ##" echo for ac_var in $ac_subst_files do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`printf "%s\n" "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac printf "%s\n" "$ac_var='\''$ac_val'\''" done | sort echo fi if test -s confdefs.h; then printf "%s\n" "## ----------- ## ## confdefs.h. ## ## ----------- ##" echo cat confdefs.h echo fi test "$ac_signal" != 0 && printf "%s\n" "$as_me: caught signal $ac_signal" printf "%s\n" "$as_me: exit $exit_status" } >&5 rm -f core *.core core.conftest.* && rm -f -r conftest* confdefs* conf$$* $ac_clean_files && exit $exit_status ' 0 for ac_signal in 1 2 13 15; do trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal done ac_signal=0 # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -f -r conftest* confdefs.h printf "%s\n" "/* confdefs.h */" > confdefs.h # Predefined preprocessor variables. printf "%s\n" "#define PACKAGE_NAME \"$PACKAGE_NAME\"" >>confdefs.h printf "%s\n" "#define PACKAGE_TARNAME \"$PACKAGE_TARNAME\"" >>confdefs.h printf "%s\n" "#define PACKAGE_VERSION \"$PACKAGE_VERSION\"" >>confdefs.h printf "%s\n" "#define PACKAGE_STRING \"$PACKAGE_STRING\"" >>confdefs.h printf "%s\n" "#define PACKAGE_BUGREPORT \"$PACKAGE_BUGREPORT\"" >>confdefs.h printf "%s\n" "#define PACKAGE_URL \"$PACKAGE_URL\"" >>confdefs.h # Let the site file select an alternate cache file if it wants to. # Prefer an explicitly selected file to automatically selected ones. if test -n "$CONFIG_SITE"; then ac_site_files="$CONFIG_SITE" elif test "x$prefix" != xNONE; then ac_site_files="$prefix/share/config.site $prefix/etc/config.site" else ac_site_files="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" fi for ac_site_file in $ac_site_files do case $ac_site_file in #( */*) : ;; #( *) : ac_site_file=./$ac_site_file ;; esac if test -f "$ac_site_file" && test -r "$ac_site_file"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 printf "%s\n" "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" \ || { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "failed to load site script $ac_site_file See \`config.log' for more details" "$LINENO" 5; } fi done if test -r "$cache_file"; then # Some versions of bash will fail to source /dev/null (special files # actually), so we avoid doing that. DJGPP emulates it as a regular file. if test /dev/null != "$cache_file" && test -f "$cache_file"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 printf "%s\n" "$as_me: loading cache $cache_file" >&6;} case $cache_file in [\\/]* | ?:[\\/]* ) . "$cache_file";; *) . "./$cache_file";; esac fi else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 printf "%s\n" "$as_me: creating cache $cache_file" >&6;} >$cache_file fi # Test code for whether the C compiler supports C89 (global declarations) ac_c_conftest_c89_globals=' /* Does the compiler advertise C89 conformance? Do not test the value of __STDC__, because some compilers set it to 0 while being otherwise adequately conformant. */ #if !defined __STDC__ # error "Compiler does not advertise C89 conformance" #endif #include #include struct stat; /* Most of the following tests are stolen from RCS 5.7 src/conf.sh. */ struct buf { int x; }; struct buf * (*rcsopen) (struct buf *, struct stat *, int); static char *e (p, i) char **p; int i; { return p[i]; } static char *f (char * (*g) (char **, int), char **p, ...) { char *s; va_list v; va_start (v,p); s = g (p, va_arg (v,int)); va_end (v); return s; } /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has function prototypes and stuff, but not \xHH hex character constants. These do not provoke an error unfortunately, instead are silently treated as an "x". The following induces an error, until -std is added to get proper ANSI mode. Curiously \x00 != x always comes out true, for an array size at least. It is necessary to write \x00 == 0 to get something that is true only with -std. */ int osf4_cc_array ['\''\x00'\'' == 0 ? 1 : -1]; /* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters inside strings and character constants. */ #define FOO(x) '\''x'\'' int xlc6_cc_array[FOO(a) == '\''x'\'' ? 1 : -1]; int test (int i, double x); struct s1 {int (*f) (int a);}; struct s2 {int (*f) (double a);}; int pairnames (int, char **, int *(*)(struct buf *, struct stat *, int), int, int);' # Test code for whether the C compiler supports C89 (body of main). ac_c_conftest_c89_main=' ok |= (argc == 0 || f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]); ' # Test code for whether the C compiler supports C99 (global declarations) ac_c_conftest_c99_globals=' // Does the compiler advertise C99 conformance? #if !defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L # error "Compiler does not advertise C99 conformance" #endif #include extern int puts (const char *); extern int printf (const char *, ...); extern int dprintf (int, const char *, ...); extern void *malloc (size_t); // Check varargs macros. These examples are taken from C99 6.10.3.5. // dprintf is used instead of fprintf to avoid needing to declare // FILE and stderr. #define debug(...) dprintf (2, __VA_ARGS__) #define showlist(...) puts (#__VA_ARGS__) #define report(test,...) ((test) ? puts (#test) : printf (__VA_ARGS__)) static void test_varargs_macros (void) { int x = 1234; int y = 5678; debug ("Flag"); debug ("X = %d\n", x); showlist (The first, second, and third items.); report (x>y, "x is %d but y is %d", x, y); } // Check long long types. #define BIG64 18446744073709551615ull #define BIG32 4294967295ul #define BIG_OK (BIG64 / BIG32 == 4294967297ull && BIG64 % BIG32 == 0) #if !BIG_OK #error "your preprocessor is broken" #endif #if BIG_OK #else #error "your preprocessor is broken" #endif static long long int bignum = -9223372036854775807LL; static unsigned long long int ubignum = BIG64; struct incomplete_array { int datasize; double data[]; }; struct named_init { int number; const wchar_t *name; double average; }; typedef const char *ccp; static inline int test_restrict (ccp restrict text) { // See if C++-style comments work. // Iterate through items via the restricted pointer. // Also check for declarations in for loops. for (unsigned int i = 0; *(text+i) != '\''\0'\''; ++i) continue; return 0; } // Check varargs and va_copy. static bool test_varargs (const char *format, ...) { va_list args; va_start (args, format); va_list args_copy; va_copy (args_copy, args); const char *str = ""; int number = 0; float fnumber = 0; while (*format) { switch (*format++) { case '\''s'\'': // string str = va_arg (args_copy, const char *); break; case '\''d'\'': // int number = va_arg (args_copy, int); break; case '\''f'\'': // float fnumber = va_arg (args_copy, double); break; default: break; } } va_end (args_copy); va_end (args); return *str && number && fnumber; } ' # Test code for whether the C compiler supports C99 (body of main). ac_c_conftest_c99_main=' // Check bool. _Bool success = false; success |= (argc != 0); // Check restrict. if (test_restrict ("String literal") == 0) success = true; char *restrict newvar = "Another string"; // Check varargs. success &= test_varargs ("s, d'\'' f .", "string", 65, 34.234); test_varargs_macros (); // Check flexible array members. struct incomplete_array *ia = malloc (sizeof (struct incomplete_array) + (sizeof (double) * 10)); ia->datasize = 10; for (int i = 0; i < ia->datasize; ++i) ia->data[i] = i * 1.234; // Check named initializers. struct named_init ni = { .number = 34, .name = L"Test wide string", .average = 543.34343, }; ni.number = 58; int dynamic_array[ni.number]; dynamic_array[0] = argv[0][0]; dynamic_array[ni.number - 1] = 543; // work around unused variable warnings ok |= (!success || bignum == 0LL || ubignum == 0uLL || newvar[0] == '\''x'\'' || dynamic_array[ni.number - 1] != 543); ' # Test code for whether the C compiler supports C11 (global declarations) ac_c_conftest_c11_globals=' // Does the compiler advertise C11 conformance? #if !defined __STDC_VERSION__ || __STDC_VERSION__ < 201112L # error "Compiler does not advertise C11 conformance" #endif // Check _Alignas. char _Alignas (double) aligned_as_double; char _Alignas (0) no_special_alignment; extern char aligned_as_int; char _Alignas (0) _Alignas (int) aligned_as_int; // Check _Alignof. enum { int_alignment = _Alignof (int), int_array_alignment = _Alignof (int[100]), char_alignment = _Alignof (char) }; _Static_assert (0 < -_Alignof (int), "_Alignof is signed"); // Check _Noreturn. int _Noreturn does_not_return (void) { for (;;) continue; } // Check _Static_assert. struct test_static_assert { int x; _Static_assert (sizeof (int) <= sizeof (long int), "_Static_assert does not work in struct"); long int y; }; // Check UTF-8 literals. #define u8 syntax error! char const utf8_literal[] = u8"happens to be ASCII" "another string"; // Check duplicate typedefs. typedef long *long_ptr; typedef long int *long_ptr; typedef long_ptr long_ptr; // Anonymous structures and unions -- taken from C11 6.7.2.1 Example 1. struct anonymous { union { struct { int i; int j; }; struct { int k; long int l; } w; }; int m; } v1; ' # Test code for whether the C compiler supports C11 (body of main). ac_c_conftest_c11_main=' _Static_assert ((offsetof (struct anonymous, i) == offsetof (struct anonymous, w.k)), "Anonymous union alignment botch"); v1.i = 2; v1.w.k = 5; ok |= v1.i != 5; ' # Test code for whether the C compiler supports C11 (complete). ac_c_conftest_c11_program="${ac_c_conftest_c89_globals} ${ac_c_conftest_c99_globals} ${ac_c_conftest_c11_globals} int main (int argc, char **argv) { int ok = 0; ${ac_c_conftest_c89_main} ${ac_c_conftest_c99_main} ${ac_c_conftest_c11_main} return ok; } " # Test code for whether the C compiler supports C99 (complete). ac_c_conftest_c99_program="${ac_c_conftest_c89_globals} ${ac_c_conftest_c99_globals} int main (int argc, char **argv) { int ok = 0; ${ac_c_conftest_c89_main} ${ac_c_conftest_c99_main} return ok; } " # Test code for whether the C compiler supports C89 (complete). ac_c_conftest_c89_program="${ac_c_conftest_c89_globals} int main (int argc, char **argv) { int ok = 0; ${ac_c_conftest_c89_main} return ok; } " as_fn_append ac_header_c_list " stdio.h stdio_h HAVE_STDIO_H" as_fn_append ac_header_c_list " stdlib.h stdlib_h HAVE_STDLIB_H" as_fn_append ac_header_c_list " string.h string_h HAVE_STRING_H" as_fn_append ac_header_c_list " inttypes.h inttypes_h HAVE_INTTYPES_H" as_fn_append ac_header_c_list " stdint.h stdint_h HAVE_STDINT_H" as_fn_append ac_header_c_list " strings.h strings_h HAVE_STRINGS_H" as_fn_append ac_header_c_list " sys/stat.h sys_stat_h HAVE_SYS_STAT_H" as_fn_append ac_header_c_list " sys/types.h sys_types_h HAVE_SYS_TYPES_H" as_fn_append ac_header_c_list " unistd.h unistd_h HAVE_UNISTD_H" # Auxiliary files required by this configure script. ac_aux_files="install-sh config.guess config.sub" # Locations in which to look for auxiliary files. ac_aux_dir_candidates="${srcdir}${PATH_SEPARATOR}${srcdir}/..${PATH_SEPARATOR}${srcdir}/../.." # Search for a directory containing all of the required auxiliary files, # $ac_aux_files, from the $PATH-style list $ac_aux_dir_candidates. # If we don't find one directory that contains all the files we need, # we report the set of missing files from the *first* directory in # $ac_aux_dir_candidates and give up. ac_missing_aux_files="" ac_first_candidate=: printf "%s\n" "$as_me:${as_lineno-$LINENO}: looking for aux files: $ac_aux_files" >&5 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in $ac_aux_dir_candidates do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac as_found=: printf "%s\n" "$as_me:${as_lineno-$LINENO}: trying $as_dir" >&5 ac_aux_dir_found=yes ac_install_sh= for ac_aux in $ac_aux_files do # As a special case, if "install-sh" is required, that requirement # can be satisfied by any of "install-sh", "install.sh", or "shtool", # and $ac_install_sh is set appropriately for whichever one is found. if test x"$ac_aux" = x"install-sh" then if test -f "${as_dir}install-sh"; then printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}install-sh found" >&5 ac_install_sh="${as_dir}install-sh -c" elif test -f "${as_dir}install.sh"; then printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}install.sh found" >&5 ac_install_sh="${as_dir}install.sh -c" elif test -f "${as_dir}shtool"; then printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}shtool found" >&5 ac_install_sh="${as_dir}shtool install -c" else ac_aux_dir_found=no if $ac_first_candidate; then ac_missing_aux_files="${ac_missing_aux_files} install-sh" else break fi fi else if test -f "${as_dir}${ac_aux}"; then printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}${ac_aux} found" >&5 else ac_aux_dir_found=no if $ac_first_candidate; then ac_missing_aux_files="${ac_missing_aux_files} ${ac_aux}" else break fi fi fi done if test "$ac_aux_dir_found" = yes; then ac_aux_dir="$as_dir" break fi ac_first_candidate=false as_found=false done IFS=$as_save_IFS if $as_found then : else $as_nop as_fn_error $? "cannot find required auxiliary files:$ac_missing_aux_files" "$LINENO" 5 fi # These three variables are undocumented and unsupported, # and are intended to be withdrawn in a future Autoconf release. # They can cause serious problems if a builder's source tree is in a directory # whose full name contains unusual characters. if test -f "${ac_aux_dir}config.guess"; then ac_config_guess="$SHELL ${ac_aux_dir}config.guess" fi if test -f "${ac_aux_dir}config.sub"; then ac_config_sub="$SHELL ${ac_aux_dir}config.sub" fi if test -f "$ac_aux_dir/configure"; then ac_configure="$SHELL ${ac_aux_dir}configure" fi # Check that the precious variables saved in the cache have kept the same # value. ac_cache_corrupted=false for ac_var in $ac_precious_vars; do eval ac_old_set=\$ac_cv_env_${ac_var}_set eval ac_new_set=\$ac_env_${ac_var}_set eval ac_old_val=\$ac_cv_env_${ac_var}_value eval ac_new_val=\$ac_env_${ac_var}_value case $ac_old_set,$ac_new_set in set,) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 printf "%s\n" "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 printf "%s\n" "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} ac_cache_corrupted=: ;; ,);; *) if test "x$ac_old_val" != "x$ac_new_val"; then # differences in whitespace do not lead to failure. ac_old_val_w=`echo x $ac_old_val` ac_new_val_w=`echo x $ac_new_val` if test "$ac_old_val_w" != "$ac_new_val_w"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 printf "%s\n" "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} ac_cache_corrupted=: else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 printf "%s\n" "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} eval $ac_var=\$ac_old_val fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 printf "%s\n" "$as_me: former value: \`$ac_old_val'" >&2;} { printf "%s\n" "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 printf "%s\n" "$as_me: current value: \`$ac_new_val'" >&2;} fi;; esac # Pass precious variables to config.status. if test "$ac_new_set" = set; then case $ac_new_val in *\'*) ac_arg=$ac_var=`printf "%s\n" "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; *) ac_arg=$ac_var=$ac_new_val ;; esac case " $ac_configure_args " in *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. *) as_fn_append ac_configure_args " '$ac_arg'" ;; esac fi done if $ac_cache_corrupted; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 printf "%s\n" "$as_me: error: changes in the environment can compromise the build" >&2;} as_fn_error $? "run \`${MAKE-make} distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 fi ## -------------------- ## ## Main body of script. ## ## -------------------- ## ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu # Check for stale configure as early as possible. for i in $srcdir/configure.ac $srcdir/m4/*.m4; do if test "$i" -nt "$srcdir/configure"; then as_fn_error $? "$i newer than configure, run autoreconf" "$LINENO" 5 fi done ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_config_headers="$ac_config_headers config.h" ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then for ac_prog in cc gcc clang do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf "%s\n" "$CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in cc gcc clang do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_CC+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="$ac_prog" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 printf "%s\n" "$ac_ct_CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -n "$ac_ct_CC" && break done if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi fi test -z "$CC" && { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "no acceptable C compiler found in \$PATH See \`config.log' for more details" "$LINENO" 5; } # Provide some information about the compiler. printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion -version; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" # Try to create an executable without -o first, disregard a.out. # It will help us diagnose broken compilers, and finding out an intuition # of exeext. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 printf %s "checking whether the C compiler works... " >&6; } ac_link_default=`printf "%s\n" "$ac_link" | sed 's/ -o *conftest[^ ]*//'` # The possible output files: ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" ac_rmfiles= for ac_file in $ac_files do case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; * ) ac_rmfiles="$ac_rmfiles $ac_file";; esac done rm -f $ac_rmfiles if { { ac_try="$ac_link_default" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link_default") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } then : # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. # So ignore a value of `no', otherwise this would lead to `EXEEXT = no' # in a Makefile. We should not override ac_cv_exeext if it was cached, # so that the user can short-circuit this test for compilers unknown to # Autoconf. for ac_file in $ac_files '' do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; [ab].out ) # We found the default executable, but exeext='' is most # certainly right. break;; *.* ) if test ${ac_cv_exeext+y} && test "$ac_cv_exeext" != no; then :; else ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` fi # We set ac_cv_exeext here because the later test for it is not # safe: cross compilers may not add the suffix if given an `-o' # argument, so we may need to know it at that point already. # Even if this section looks crufty: it has the advantage of # actually working. break;; * ) break;; esac done test "$ac_cv_exeext" = no && ac_cv_exeext= else $as_nop ac_file='' fi if test -z "$ac_file" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "C compiler cannot create executables See \`config.log' for more details" "$LINENO" 5; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 printf %s "checking for C compiler default output file name... " >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 printf "%s\n" "$ac_file" >&6; } ac_exeext=$ac_cv_exeext rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out ac_clean_files=$ac_clean_files_save { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 printf %s "checking for suffix of executables... " >&6; } if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } then : # If both `conftest.exe' and `conftest' are `present' (well, observable) # catch `conftest.exe'. For instance with Cygwin, `ls conftest' will # work properly (i.e., refer to `conftest.exe'), while it won't with # `rm'. for ac_file in conftest.exe conftest conftest.*; do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` break;; * ) break;; esac done else $as_nop { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of executables: cannot compile and link See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest conftest$ac_cv_exeext { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 printf "%s\n" "$ac_cv_exeext" >&6; } rm -f conftest.$ac_ext EXEEXT=$ac_cv_exeext ac_exeext=$EXEEXT cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { FILE *f = fopen ("conftest.out", "w"); return ferror (f) || fclose (f) != 0; ; return 0; } _ACEOF ac_clean_files="$ac_clean_files conftest.out" # Check that the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 printf %s "checking whether we are cross compiling... " >&6; } if test "$cross_compiling" != yes; then { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if { ac_try='./conftest$ac_cv_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then cross_compiling=no else if test "$cross_compiling" = maybe; then cross_compiling=yes else { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "cannot run C compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details" "$LINENO" 5; } fi fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 printf "%s\n" "$cross_compiling" >&6; } rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out ac_clean_files=$ac_clean_files_save { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 printf %s "checking for suffix of object files... " >&6; } if test ${ac_cv_objext+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF rm -f conftest.o conftest.obj if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } then : for ac_file in conftest.o conftest.obj conftest.*; do test -f "$ac_file" || continue; case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` break;; esac done else $as_nop printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of object files: cannot compile See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest.$ac_cv_objext conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 printf "%s\n" "$ac_cv_objext" >&6; } OBJEXT=$ac_cv_objext ac_objext=$OBJEXT { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports GNU C" >&5 printf %s "checking whether the compiler supports GNU C... " >&6; } if test ${ac_cv_c_compiler_gnu+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_compiler_gnu=yes else $as_nop ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 printf "%s\n" "$ac_cv_c_compiler_gnu" >&6; } ac_compiler_gnu=$ac_cv_c_compiler_gnu if test $ac_compiler_gnu = yes; then GCC=yes else GCC= fi ac_test_CFLAGS=${CFLAGS+y} ac_save_CFLAGS=$CFLAGS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 printf %s "checking whether $CC accepts -g... " >&6; } if test ${ac_cv_prog_cc_g+y} then : printf %s "(cached) " >&6 else $as_nop ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes ac_cv_prog_cc_g=no CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_g=yes else $as_nop CFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : else $as_nop ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 printf "%s\n" "$ac_cv_prog_cc_g" >&6; } if test $ac_test_CFLAGS; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi ac_prog_cc_stdc=no if test x$ac_prog_cc_stdc = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C11 features" >&5 printf %s "checking for $CC option to enable C11 features... " >&6; } if test ${ac_cv_prog_cc_c11+y} then : printf %s "(cached) " >&6 else $as_nop ac_cv_prog_cc_c11=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_c_conftest_c11_program _ACEOF for ac_arg in '' -std=gnu11 do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_c11=$ac_arg fi rm -f core conftest.err conftest.$ac_objext conftest.beam test "x$ac_cv_prog_cc_c11" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi if test "x$ac_cv_prog_cc_c11" = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 printf "%s\n" "unsupported" >&6; } else $as_nop if test "x$ac_cv_prog_cc_c11" = x then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 printf "%s\n" "none needed" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c11" >&5 printf "%s\n" "$ac_cv_prog_cc_c11" >&6; } CC="$CC $ac_cv_prog_cc_c11" fi ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c11 ac_prog_cc_stdc=c11 fi fi if test x$ac_prog_cc_stdc = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C99 features" >&5 printf %s "checking for $CC option to enable C99 features... " >&6; } if test ${ac_cv_prog_cc_c99+y} then : printf %s "(cached) " >&6 else $as_nop ac_cv_prog_cc_c99=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_c_conftest_c99_program _ACEOF for ac_arg in '' -std=gnu99 -std=c99 -c99 -qlanglvl=extc1x -qlanglvl=extc99 -AC99 -D_STDC_C99= do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_c99=$ac_arg fi rm -f core conftest.err conftest.$ac_objext conftest.beam test "x$ac_cv_prog_cc_c99" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi if test "x$ac_cv_prog_cc_c99" = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 printf "%s\n" "unsupported" >&6; } else $as_nop if test "x$ac_cv_prog_cc_c99" = x then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 printf "%s\n" "none needed" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5 printf "%s\n" "$ac_cv_prog_cc_c99" >&6; } CC="$CC $ac_cv_prog_cc_c99" fi ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c99 ac_prog_cc_stdc=c99 fi fi if test x$ac_prog_cc_stdc = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C89 features" >&5 printf %s "checking for $CC option to enable C89 features... " >&6; } if test ${ac_cv_prog_cc_c89+y} then : printf %s "(cached) " >&6 else $as_nop ac_cv_prog_cc_c89=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_c_conftest_c89_program _ACEOF for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_c89=$ac_arg fi rm -f core conftest.err conftest.$ac_objext conftest.beam test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi if test "x$ac_cv_prog_cc_c89" = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 printf "%s\n" "unsupported" >&6; } else $as_nop if test "x$ac_cv_prog_cc_c89" = x then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 printf "%s\n" "none needed" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 printf "%s\n" "$ac_cv_prog_cc_c89" >&6; } CC="$CC $ac_cv_prog_cc_c89" fi ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c89 ac_prog_cc_stdc=c89 fi fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu # XXX relax this after reimplementing logit() etc. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $CC supports C99-style variadic macros" >&5 printf %s "checking if $CC supports C99-style variadic macros... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int f(int a, int b, int c) { return a + b + c; } #define F(a, ...) f(a, __VA_ARGS__) int main (void) { return F(1, 2, -3); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else $as_nop as_fn_error $? "*** OpenSSH requires support for C99-style variadic macros" "$LINENO" 5 fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext # Make sure we can run config.sub. $SHELL "${ac_aux_dir}config.sub" sun4 >/dev/null 2>&1 || as_fn_error $? "cannot run $SHELL ${ac_aux_dir}config.sub" "$LINENO" 5 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 printf %s "checking build system type... " >&6; } if test ${ac_cv_build+y} then : printf %s "(cached) " >&6 else $as_nop ac_build_alias=$build_alias test "x$ac_build_alias" = x && ac_build_alias=`$SHELL "${ac_aux_dir}config.guess"` test "x$ac_build_alias" = x && as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 ac_cv_build=`$SHELL "${ac_aux_dir}config.sub" $ac_build_alias` || as_fn_error $? "$SHELL ${ac_aux_dir}config.sub $ac_build_alias failed" "$LINENO" 5 fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 printf "%s\n" "$ac_cv_build" >&6; } case $ac_cv_build in *-*-*) ;; *) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;; esac build=$ac_cv_build ac_save_IFS=$IFS; IFS='-' set x $ac_cv_build shift build_cpu=$1 build_vendor=$2 shift; shift # Remember, the first character of IFS is used to create $*, # except with old shells: build_os=$* IFS=$ac_save_IFS case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 printf %s "checking host system type... " >&6; } if test ${ac_cv_host+y} then : printf %s "(cached) " >&6 else $as_nop if test "x$host_alias" = x; then ac_cv_host=$ac_cv_build else ac_cv_host=`$SHELL "${ac_aux_dir}config.sub" $host_alias` || as_fn_error $? "$SHELL ${ac_aux_dir}config.sub $host_alias failed" "$LINENO" 5 fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 printf "%s\n" "$ac_cv_host" >&6; } case $ac_cv_host in *-*-*) ;; *) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;; esac host=$ac_cv_host ac_save_IFS=$IFS; IFS='-' set x $ac_cv_host shift host_cpu=$1 host_vendor=$2 shift; shift # Remember, the first character of IFS is used to create $*, # except with old shells: host_os=$* IFS=$ac_save_IFS case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac ac_header= ac_cache= for ac_item in $ac_header_c_list do if test $ac_cache; then ac_fn_c_check_header_compile "$LINENO" $ac_header ac_cv_header_$ac_cache "$ac_includes_default" if eval test \"x\$ac_cv_header_$ac_cache\" = xyes; then printf "%s\n" "#define $ac_item 1" >> confdefs.h fi ac_header= ac_cache= elif test $ac_header; then ac_cache=$ac_item else ac_header=$ac_item fi done if test $ac_cv_header_stdlib_h = yes && test $ac_cv_header_string_h = yes then : printf "%s\n" "#define STDC_HEADERS 1" >>confdefs.h fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether byte ordering is bigendian" >&5 printf %s "checking whether byte ordering is bigendian... " >&6; } if test ${ac_cv_c_bigendian+y} then : printf %s "(cached) " >&6 else $as_nop ac_cv_c_bigendian=unknown # See if we're dealing with a universal compiler. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifndef __APPLE_CC__ not a universal capable compiler #endif typedef int dummy; _ACEOF if ac_fn_c_try_compile "$LINENO" then : # Check for potential -arch flags. It is not universal unless # there are at least two -arch flags with different values. ac_arch= ac_prev= for ac_word in $CC $CFLAGS $CPPFLAGS $LDFLAGS; do if test -n "$ac_prev"; then case $ac_word in i?86 | x86_64 | ppc | ppc64) if test -z "$ac_arch" || test "$ac_arch" = "$ac_word"; then ac_arch=$ac_word else ac_cv_c_bigendian=universal break fi ;; esac ac_prev= elif test "x$ac_word" = "x-arch"; then ac_prev=arch fi done fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext if test $ac_cv_c_bigendian = unknown; then # See if sys/param.h defines the BYTE_ORDER macro. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main (void) { #if ! (defined BYTE_ORDER && defined BIG_ENDIAN \ && defined LITTLE_ENDIAN && BYTE_ORDER && BIG_ENDIAN \ && LITTLE_ENDIAN) bogus endian macros #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : # It does; now see whether it defined to BIG_ENDIAN or not. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main (void) { #if BYTE_ORDER != BIG_ENDIAN not big endian #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_c_bigendian=yes else $as_nop ac_cv_c_bigendian=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi if test $ac_cv_c_bigendian = unknown; then # See if defines _LITTLE_ENDIAN or _BIG_ENDIAN (e.g., Solaris). cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { #if ! (defined _LITTLE_ENDIAN || defined _BIG_ENDIAN) bogus endian macros #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : # It does; now see whether it defined to _BIG_ENDIAN or not. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { #ifndef _BIG_ENDIAN not big endian #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_c_bigendian=yes else $as_nop ac_cv_c_bigendian=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi if test $ac_cv_c_bigendian = unknown; then # Compile a test program. if test "$cross_compiling" = yes then : # Try to guess by grepping values from an object file. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ unsigned short int ascii_mm[] = { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 }; unsigned short int ascii_ii[] = { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 }; int use_ascii (int i) { return ascii_mm[i] + ascii_ii[i]; } unsigned short int ebcdic_ii[] = { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 }; unsigned short int ebcdic_mm[] = { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 }; int use_ebcdic (int i) { return ebcdic_mm[i] + ebcdic_ii[i]; } extern int foo; int main (void) { return use_ascii (foo) == use_ebcdic (foo); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : if grep BIGenDianSyS conftest.$ac_objext >/dev/null; then ac_cv_c_bigendian=yes fi if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then if test "$ac_cv_c_bigendian" = unknown; then ac_cv_c_bigendian=no else # finding both strings is unlikely to happen, but who knows? ac_cv_c_bigendian=unknown fi fi fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default int main (void) { /* Are we little or big endian? From Harbison&Steele. */ union { long int l; char c[sizeof (long int)]; } u; u.l = 1; return u.c[sizeof (long int) - 1] == 1; ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO" then : ac_cv_c_bigendian=no else $as_nop ac_cv_c_bigendian=yes fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_bigendian" >&5 printf "%s\n" "$ac_cv_c_bigendian" >&6; } case $ac_cv_c_bigendian in #( yes) printf "%s\n" "#define WORDS_BIGENDIAN 1" >>confdefs.h ;; #( no) ;; #( universal) printf "%s\n" "#define AC_APPLE_UNIVERSAL_BUILD 1" >>confdefs.h ;; #( *) as_fn_error $? "unknown endianness presetting ac_cv_c_bigendian=no (or yes) will help" "$LINENO" 5 ;; esac # Checks for programs. for ac_prog in gawk mawk nawk awk do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_AWK+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$AWK"; then ac_cv_prog_AWK="$AWK" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_AWK="$ac_prog" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi AWK=$ac_cv_prog_AWK if test -n "$AWK"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5 printf "%s\n" "$AWK" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -n "$AWK" && break done ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 printf %s "checking how to run the C preprocessor... " >&6; } # On Suns, sometimes $CPP names a directory. if test -n "$CPP" && test -d "$CPP"; then CPP= fi if test -z "$CPP"; then if test ${ac_cv_prog_CPP+y} then : printf %s "(cached) " >&6 else $as_nop # Double quotes because $CC needs to be expanded for CPP in "$CC -E" "$CC -E -traditional-cpp" cpp /lib/cpp do ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include Syntax error _ACEOF if ac_fn_c_try_cpp "$LINENO" then : else $as_nop # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO" then : # Broken: success on invalid input. continue else $as_nop # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok then : break fi done ac_cv_prog_CPP=$CPP fi CPP=$ac_cv_prog_CPP else ac_cv_prog_CPP=$CPP fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 printf "%s\n" "$CPP" >&6; } ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include Syntax error _ACEOF if ac_fn_c_try_cpp "$LINENO" then : else $as_nop # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO" then : # Broken: success on invalid input. continue else $as_nop # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok then : else $as_nop { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "C preprocessor \"$CPP\" fails sanity check See \`config.log' for more details" "$LINENO" 5; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. set dummy ${ac_tool_prefix}ranlib; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_RANLIB+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$RANLIB"; then ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi RANLIB=$ac_cv_prog_RANLIB if test -n "$RANLIB"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5 printf "%s\n" "$RANLIB" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_prog_RANLIB"; then ac_ct_RANLIB=$RANLIB # Extract the first word of "ranlib", so it can be a program name with args. set dummy ranlib; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_RANLIB+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$ac_ct_RANLIB"; then ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_RANLIB="ranlib" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB if test -n "$ac_ct_RANLIB"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5 printf "%s\n" "$ac_ct_RANLIB" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_ct_RANLIB" = x; then RANLIB=":" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac RANLIB=$ac_ct_RANLIB fi else RANLIB="$ac_cv_prog_RANLIB" fi # Find a good install program. We prefer a C program (faster), # so one script is as good as another. But avoid the broken or # incompatible versions: # SysV /etc/install, /usr/sbin/install # SunOS /usr/etc/install # IRIX /sbin/install # AIX /bin/install # AmigaOS /C/install, which installs bootblocks on floppy discs # AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag # AFS /usr/afsws/bin/install, which mishandles nonexistent args # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" # OS/2's system install, which has a completely different semantic # ./install, which can be erroneously created by make from ./install.sh. # Reject install programs that cannot install multiple files. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 printf %s "checking for a BSD-compatible install... " >&6; } if test -z "$INSTALL"; then if test ${ac_cv_path_install+y} then : printf %s "(cached) " >&6 else $as_nop as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac # Account for fact that we put trailing slashes in our PATH walk. case $as_dir in #(( ./ | /[cC]/* | \ /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ /usr/ucb/* ) ;; *) # OSF1 and SCO ODT 3.0 have their own names for install. # Don't use installbsd from OSF since it installs stuff as root # by default. for ac_prog in ginstall scoinst install; do for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_prog$ac_exec_ext"; then if test $ac_prog = install && grep dspmsg "$as_dir$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # AIX install. It has an incompatible calling convention. : elif test $ac_prog = install && grep pwplus "$as_dir$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # program-specific install script used by HP pwplus--don't use. : else rm -rf conftest.one conftest.two conftest.dir echo one > conftest.one echo two > conftest.two mkdir conftest.dir if "$as_dir$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir/" && test -s conftest.one && test -s conftest.two && test -s conftest.dir/conftest.one && test -s conftest.dir/conftest.two then ac_cv_path_install="$as_dir$ac_prog$ac_exec_ext -c" break 3 fi fi fi done done ;; esac done IFS=$as_save_IFS rm -rf conftest.one conftest.two conftest.dir fi if test ${ac_cv_path_install+y}; then INSTALL=$ac_cv_path_install else # As a last resort, use the slow shell script. Don't cache a # value for INSTALL within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the value is a relative name. INSTALL=$ac_install_sh fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 printf "%s\n" "$INSTALL" >&6; } # Use test -z because SunOS4 sh mishandles braces in ${var-val}. # It thinks the first close brace ends the variable substitution. test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 printf %s "checking for grep that handles long lines and -e... " >&6; } if test ${ac_cv_path_GREP+y} then : printf %s "(cached) " >&6 else $as_nop if test -z "$GREP"; then ac_path_GREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_prog in grep ggrep do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_GREP="$as_dir$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_GREP" || continue # Check for GNU ac_path_GREP and select it if it is found. # Check for GNU $ac_path_GREP case `"$ac_path_GREP" --version 2>&1` in *GNU*) ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; *) ac_count=0 printf %s 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" printf "%s\n" 'GREP' >> "conftest.nl" "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_GREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_GREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_GREP"; then as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_GREP=$GREP fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 printf "%s\n" "$ac_cv_path_GREP" >&6; } GREP="$ac_cv_path_GREP" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 printf %s "checking for egrep... " >&6; } if test ${ac_cv_path_EGREP+y} then : printf %s "(cached) " >&6 else $as_nop if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 then ac_cv_path_EGREP="$GREP -E" else if test -z "$EGREP"; then ac_path_EGREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_prog in egrep do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_EGREP="$as_dir$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_EGREP" || continue # Check for GNU ac_path_EGREP and select it if it is found. # Check for GNU $ac_path_EGREP case `"$ac_path_EGREP" --version 2>&1` in *GNU*) ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; *) ac_count=0 printf %s 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" printf "%s\n" 'EGREP' >> "conftest.nl" "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_EGREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_EGREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_EGREP"; then as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_EGREP=$EGREP fi fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 printf "%s\n" "$ac_cv_path_EGREP" >&6; } EGREP="$ac_cv_path_EGREP" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for a race-free mkdir -p" >&5 printf %s "checking for a race-free mkdir -p... " >&6; } if test -z "$MKDIR_P"; then if test ${ac_cv_path_mkdir+y} then : printf %s "(cached) " >&6 else $as_nop as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_prog in mkdir gmkdir; do for ac_exec_ext in '' $ac_executable_extensions; do as_fn_executable_p "$as_dir$ac_prog$ac_exec_ext" || continue case `"$as_dir$ac_prog$ac_exec_ext" --version 2>&1` in #( 'mkdir ('*'coreutils) '* | \ 'BusyBox '* | \ 'mkdir (fileutils) '4.1*) ac_cv_path_mkdir=$as_dir$ac_prog$ac_exec_ext break 3;; esac done done done IFS=$as_save_IFS fi test -d ./--version && rmdir ./--version if test ${ac_cv_path_mkdir+y}; then MKDIR_P="$ac_cv_path_mkdir -p" else # As a last resort, use the slow shell script. Don't cache a # value for MKDIR_P within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the value is a relative name. MKDIR_P="$ac_install_sh -d" fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5 printf "%s\n" "$MKDIR_P" >&6; } if test -n "$ac_tool_prefix"; then for ac_prog in ar do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_AR+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$AR"; then ac_cv_prog_AR="$AR" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_AR="$ac_tool_prefix$ac_prog" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi AR=$ac_cv_prog_AR if test -n "$AR"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $AR" >&5 printf "%s\n" "$AR" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -n "$AR" && break done fi if test -z "$AR"; then ac_ct_AR=$AR for ac_prog in ar do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_AR+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$ac_ct_AR"; then ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_AR="$ac_prog" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_AR=$ac_cv_prog_ac_ct_AR if test -n "$ac_ct_AR"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5 printf "%s\n" "$ac_ct_AR" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -n "$ac_ct_AR" && break done if test "x$ac_ct_AR" = x; then AR="" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac AR=$ac_ct_AR fi fi # Extract the first word of "cat", so it can be a program name with args. set dummy cat; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_CAT+y} then : printf %s "(cached) " >&6 else $as_nop case $CAT in [\\/]* | ?:[\\/]*) ac_cv_path_CAT="$CAT" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_path_CAT="$as_dir$ac_word$ac_exec_ext" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi CAT=$ac_cv_path_CAT if test -n "$CAT"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CAT" >&5 printf "%s\n" "$CAT" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi # Extract the first word of "kill", so it can be a program name with args. set dummy kill; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_KILL+y} then : printf %s "(cached) " >&6 else $as_nop case $KILL in [\\/]* | ?:[\\/]*) ac_cv_path_KILL="$KILL" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_path_KILL="$as_dir$ac_word$ac_exec_ext" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi KILL=$ac_cv_path_KILL if test -n "$KILL"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $KILL" >&5 printf "%s\n" "$KILL" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi # Extract the first word of "sed", so it can be a program name with args. set dummy sed; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_SED+y} then : printf %s "(cached) " >&6 else $as_nop case $SED in [\\/]* | ?:[\\/]*) ac_cv_path_SED="$SED" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_path_SED="$as_dir$ac_word$ac_exec_ext" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi SED=$ac_cv_path_SED if test -n "$SED"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $SED" >&5 printf "%s\n" "$SED" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi # Extract the first word of "bash", so it can be a program name with args. set dummy bash; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_TEST_MINUS_S_SH+y} then : printf %s "(cached) " >&6 else $as_nop case $TEST_MINUS_S_SH in [\\/]* | ?:[\\/]*) ac_cv_path_TEST_MINUS_S_SH="$TEST_MINUS_S_SH" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_path_TEST_MINUS_S_SH="$as_dir$ac_word$ac_exec_ext" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi TEST_MINUS_S_SH=$ac_cv_path_TEST_MINUS_S_SH if test -n "$TEST_MINUS_S_SH"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $TEST_MINUS_S_SH" >&5 printf "%s\n" "$TEST_MINUS_S_SH" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi # Extract the first word of "ksh", so it can be a program name with args. set dummy ksh; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_TEST_MINUS_S_SH+y} then : printf %s "(cached) " >&6 else $as_nop case $TEST_MINUS_S_SH in [\\/]* | ?:[\\/]*) ac_cv_path_TEST_MINUS_S_SH="$TEST_MINUS_S_SH" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_path_TEST_MINUS_S_SH="$as_dir$ac_word$ac_exec_ext" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi TEST_MINUS_S_SH=$ac_cv_path_TEST_MINUS_S_SH if test -n "$TEST_MINUS_S_SH"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $TEST_MINUS_S_SH" >&5 printf "%s\n" "$TEST_MINUS_S_SH" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi # Extract the first word of "sh", so it can be a program name with args. set dummy sh; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_TEST_MINUS_S_SH+y} then : printf %s "(cached) " >&6 else $as_nop case $TEST_MINUS_S_SH in [\\/]* | ?:[\\/]*) ac_cv_path_TEST_MINUS_S_SH="$TEST_MINUS_S_SH" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_path_TEST_MINUS_S_SH="$as_dir$ac_word$ac_exec_ext" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi TEST_MINUS_S_SH=$ac_cv_path_TEST_MINUS_S_SH if test -n "$TEST_MINUS_S_SH"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $TEST_MINUS_S_SH" >&5 printf "%s\n" "$TEST_MINUS_S_SH" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi # Extract the first word of "bash", so it can be a program name with args. set dummy bash; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_SH+y} then : printf %s "(cached) " >&6 else $as_nop case $SH in [\\/]* | ?:[\\/]*) ac_cv_path_SH="$SH" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_path_SH="$as_dir$ac_word$ac_exec_ext" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi SH=$ac_cv_path_SH if test -n "$SH"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $SH" >&5 printf "%s\n" "$SH" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi # Extract the first word of "ksh", so it can be a program name with args. set dummy ksh; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_SH+y} then : printf %s "(cached) " >&6 else $as_nop case $SH in [\\/]* | ?:[\\/]*) ac_cv_path_SH="$SH" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_path_SH="$as_dir$ac_word$ac_exec_ext" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi SH=$ac_cv_path_SH if test -n "$SH"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $SH" >&5 printf "%s\n" "$SH" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi # Extract the first word of "sh", so it can be a program name with args. set dummy sh; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_SH+y} then : printf %s "(cached) " >&6 else $as_nop case $SH in [\\/]* | ?:[\\/]*) ac_cv_path_SH="$SH" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_path_SH="$as_dir$ac_word$ac_exec_ext" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi SH=$ac_cv_path_SH if test -n "$SH"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $SH" >&5 printf "%s\n" "$SH" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi # Extract the first word of "groff", so it can be a program name with args. set dummy groff; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_GROFF+y} then : printf %s "(cached) " >&6 else $as_nop case $GROFF in [\\/]* | ?:[\\/]*) ac_cv_path_GROFF="$GROFF" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_path_GROFF="$as_dir$ac_word$ac_exec_ext" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi GROFF=$ac_cv_path_GROFF if test -n "$GROFF"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $GROFF" >&5 printf "%s\n" "$GROFF" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi # Extract the first word of "nroff awf", so it can be a program name with args. set dummy nroff awf; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_NROFF+y} then : printf %s "(cached) " >&6 else $as_nop case $NROFF in [\\/]* | ?:[\\/]*) ac_cv_path_NROFF="$NROFF" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_path_NROFF="$as_dir$ac_word$ac_exec_ext" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi NROFF=$ac_cv_path_NROFF if test -n "$NROFF"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $NROFF" >&5 printf "%s\n" "$NROFF" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi # Extract the first word of "mandoc", so it can be a program name with args. set dummy mandoc; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_MANDOC+y} then : printf %s "(cached) " >&6 else $as_nop case $MANDOC in [\\/]* | ?:[\\/]*) ac_cv_path_MANDOC="$MANDOC" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_path_MANDOC="$as_dir$ac_word$ac_exec_ext" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi MANDOC=$ac_cv_path_MANDOC if test -n "$MANDOC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $MANDOC" >&5 printf "%s\n" "$MANDOC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args. set dummy ${ac_tool_prefix}pkg-config; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_PKGCONFIG+y} then : printf %s "(cached) " >&6 else $as_nop case $PKGCONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_PKGCONFIG="$PKGCONFIG" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_path_PKGCONFIG="$as_dir$ac_word$ac_exec_ext" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi PKGCONFIG=$ac_cv_path_PKGCONFIG if test -n "$PKGCONFIG"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $PKGCONFIG" >&5 printf "%s\n" "$PKGCONFIG" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_path_PKGCONFIG"; then ac_pt_PKGCONFIG=$PKGCONFIG # Extract the first word of "pkg-config", so it can be a program name with args. set dummy pkg-config; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_ac_pt_PKGCONFIG+y} then : printf %s "(cached) " >&6 else $as_nop case $ac_pt_PKGCONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_ac_pt_PKGCONFIG="$ac_pt_PKGCONFIG" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_path_ac_pt_PKGCONFIG="$as_dir$ac_word$ac_exec_ext" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi ac_pt_PKGCONFIG=$ac_cv_path_ac_pt_PKGCONFIG if test -n "$ac_pt_PKGCONFIG"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_pt_PKGCONFIG" >&5 printf "%s\n" "$ac_pt_PKGCONFIG" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_pt_PKGCONFIG" = x; then PKGCONFIG="no" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac PKGCONFIG=$ac_pt_PKGCONFIG fi else PKGCONFIG="$ac_cv_path_PKGCONFIG" fi TEST_SHELL=sh if test "x$MANDOC" != "x" ; then MANFMT="$MANDOC" elif test "x$NROFF" != "x" ; then MANFMT="$NROFF -mandoc" elif test "x$GROFF" != "x" ; then MANFMT="$GROFF -mandoc -Tascii" else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: no manpage formatter found" >&5 printf "%s\n" "$as_me: WARNING: no manpage formatter found" >&2;} MANFMT="false" fi # Extract the first word of "groupadd", so it can be a program name with args. set dummy groupadd; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_PATH_GROUPADD_PROG+y} then : printf %s "(cached) " >&6 else $as_nop case $PATH_GROUPADD_PROG in [\\/]* | ?:[\\/]*) ac_cv_path_PATH_GROUPADD_PROG="$PATH_GROUPADD_PROG" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in /usr/sbin${PATH_SEPARATOR}/etc do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_path_PATH_GROUPADD_PROG="$as_dir$ac_word$ac_exec_ext" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS test -z "$ac_cv_path_PATH_GROUPADD_PROG" && ac_cv_path_PATH_GROUPADD_PROG="groupadd" ;; esac fi PATH_GROUPADD_PROG=$ac_cv_path_PATH_GROUPADD_PROG if test -n "$PATH_GROUPADD_PROG"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $PATH_GROUPADD_PROG" >&5 printf "%s\n" "$PATH_GROUPADD_PROG" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi # Extract the first word of "useradd", so it can be a program name with args. set dummy useradd; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_PATH_USERADD_PROG+y} then : printf %s "(cached) " >&6 else $as_nop case $PATH_USERADD_PROG in [\\/]* | ?:[\\/]*) ac_cv_path_PATH_USERADD_PROG="$PATH_USERADD_PROG" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in /usr/sbin${PATH_SEPARATOR}/etc do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_path_PATH_USERADD_PROG="$as_dir$ac_word$ac_exec_ext" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS test -z "$ac_cv_path_PATH_USERADD_PROG" && ac_cv_path_PATH_USERADD_PROG="useradd" ;; esac fi PATH_USERADD_PROG=$ac_cv_path_PATH_USERADD_PROG if test -n "$PATH_USERADD_PROG"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $PATH_USERADD_PROG" >&5 printf "%s\n" "$PATH_USERADD_PROG" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi # Extract the first word of "pkgmk", so it can be a program name with args. set dummy pkgmk; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_MAKE_PACKAGE_SUPPORTED+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$MAKE_PACKAGE_SUPPORTED"; then ac_cv_prog_MAKE_PACKAGE_SUPPORTED="$MAKE_PACKAGE_SUPPORTED" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_MAKE_PACKAGE_SUPPORTED="yes" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS test -z "$ac_cv_prog_MAKE_PACKAGE_SUPPORTED" && ac_cv_prog_MAKE_PACKAGE_SUPPORTED="no" fi fi MAKE_PACKAGE_SUPPORTED=$ac_cv_prog_MAKE_PACKAGE_SUPPORTED if test -n "$MAKE_PACKAGE_SUPPORTED"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $MAKE_PACKAGE_SUPPORTED" >&5 printf "%s\n" "$MAKE_PACKAGE_SUPPORTED" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test -x /sbin/sh; then STARTUP_SCRIPT_SHELL=/sbin/sh else STARTUP_SCRIPT_SHELL=/bin/sh fi # System features # Check whether --enable-largefile was given. if test ${enable_largefile+y} then : enableval=$enable_largefile; fi if test "$enable_largefile" != no; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for special C compiler options needed for large files" >&5 printf %s "checking for special C compiler options needed for large files... " >&6; } if test ${ac_cv_sys_largefile_CC+y} then : printf %s "(cached) " >&6 else $as_nop ac_cv_sys_largefile_CC=no if test "$GCC" != yes; then ac_save_CC=$CC while :; do # IRIX 6.2 and later do not support large files by default, # so use the C compiler's -n32 option if that helps. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T (((off_t) 1 << 31 << 31) - 1 + ((off_t) 1 << 31 << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : break fi rm -f core conftest.err conftest.$ac_objext conftest.beam CC="$CC -n32" if ac_fn_c_try_compile "$LINENO" then : ac_cv_sys_largefile_CC=' -n32'; break fi rm -f core conftest.err conftest.$ac_objext conftest.beam break done CC=$ac_save_CC rm -f conftest.$ac_ext fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_largefile_CC" >&5 printf "%s\n" "$ac_cv_sys_largefile_CC" >&6; } if test "$ac_cv_sys_largefile_CC" != no; then CC=$CC$ac_cv_sys_largefile_CC fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for _FILE_OFFSET_BITS value needed for large files" >&5 printf %s "checking for _FILE_OFFSET_BITS value needed for large files... " >&6; } if test ${ac_cv_sys_file_offset_bits+y} then : printf %s "(cached) " >&6 else $as_nop while :; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T (((off_t) 1 << 31 << 31) - 1 + ((off_t) 1 << 31 << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_sys_file_offset_bits=no; break fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define _FILE_OFFSET_BITS 64 #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T (((off_t) 1 << 31 << 31) - 1 + ((off_t) 1 << 31 << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_sys_file_offset_bits=64; break fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ac_cv_sys_file_offset_bits=unknown break done fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_file_offset_bits" >&5 printf "%s\n" "$ac_cv_sys_file_offset_bits" >&6; } case $ac_cv_sys_file_offset_bits in #( no | unknown) ;; *) printf "%s\n" "#define _FILE_OFFSET_BITS $ac_cv_sys_file_offset_bits" >>confdefs.h ;; esac rm -rf conftest* if test $ac_cv_sys_file_offset_bits = unknown; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for _LARGE_FILES value needed for large files" >&5 printf %s "checking for _LARGE_FILES value needed for large files... " >&6; } if test ${ac_cv_sys_large_files+y} then : printf %s "(cached) " >&6 else $as_nop while :; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T (((off_t) 1 << 31 << 31) - 1 + ((off_t) 1 << 31 << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_sys_large_files=no; break fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define _LARGE_FILES 1 #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T (((off_t) 1 << 31 << 31) - 1 + ((off_t) 1 << 31 << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_sys_large_files=1; break fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ac_cv_sys_large_files=unknown break done fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_large_files" >&5 printf "%s\n" "$ac_cv_sys_large_files" >&6; } case $ac_cv_sys_large_files in #( no | unknown) ;; *) printf "%s\n" "#define _LARGE_FILES $ac_cv_sys_large_files" >>confdefs.h ;; esac rm -rf conftest* fi fi if test -z "$AR" ; then as_fn_error $? "*** 'ar' missing, please install or fix your \$PATH ***" "$LINENO" 5 fi # Extract the first word of "passwd", so it can be a program name with args. set dummy passwd; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_PATH_PASSWD_PROG+y} then : printf %s "(cached) " >&6 else $as_nop case $PATH_PASSWD_PROG in [\\/]* | ?:[\\/]*) ac_cv_path_PATH_PASSWD_PROG="$PATH_PASSWD_PROG" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_path_PATH_PASSWD_PROG="$as_dir$ac_word$ac_exec_ext" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi PATH_PASSWD_PROG=$ac_cv_path_PATH_PASSWD_PROG if test -n "$PATH_PASSWD_PROG"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $PATH_PASSWD_PROG" >&5 printf "%s\n" "$PATH_PASSWD_PROG" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test ! -z "$PATH_PASSWD_PROG" ; then printf "%s\n" "#define _PATH_PASSWD_PROG \"$PATH_PASSWD_PROG\"" >>confdefs.h fi LD="$CC" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for inline" >&5 printf %s "checking for inline... " >&6; } if test ${ac_cv_c_inline+y} then : printf %s "(cached) " >&6 else $as_nop ac_cv_c_inline=no for ac_kw in inline __inline__ __inline; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifndef __cplusplus typedef int foo_t; static $ac_kw foo_t static_foo (void) {return 0; } $ac_kw foo_t foo (void) {return 0; } #endif _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_c_inline=$ac_kw fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext test "$ac_cv_c_inline" != no && break done fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_inline" >&5 printf "%s\n" "$ac_cv_c_inline" >&6; } case $ac_cv_c_inline in inline | yes) ;; *) case $ac_cv_c_inline in no) ac_val=;; *) ac_val=$ac_cv_c_inline;; esac cat >>confdefs.h <<_ACEOF #ifndef __cplusplus #define inline $ac_val #endif _ACEOF ;; esac { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC options needed to detect all undeclared functions" >&5 printf %s "checking for $CC options needed to detect all undeclared functions... " >&6; } if test ${ac_cv_c_undeclared_builtin_options+y} then : printf %s "(cached) " >&6 else $as_nop ac_save_CFLAGS=$CFLAGS ac_cv_c_undeclared_builtin_options='cannot detect' for ac_arg in '' -fno-builtin; do CFLAGS="$ac_save_CFLAGS $ac_arg" # This test program should *not* compile successfully. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { (void) strchr; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : else $as_nop # This test program should compile successfully. # No library function is consistently available on # freestanding implementations, so test against a dummy # declaration. Include always-available headers on the # off chance that they somehow elicit warnings. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include extern void ac_decl (int, char *); int main (void) { (void) ac_decl (0, (char *) 0); (void) ac_decl; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : if test x"$ac_arg" = x then : ac_cv_c_undeclared_builtin_options='none needed' else $as_nop ac_cv_c_undeclared_builtin_options=$ac_arg fi break fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext done CFLAGS=$ac_save_CFLAGS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_undeclared_builtin_options" >&5 printf "%s\n" "$ac_cv_c_undeclared_builtin_options" >&6; } case $ac_cv_c_undeclared_builtin_options in #( 'cannot detect') : { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot make $CC report undeclared builtins See \`config.log' for more details" "$LINENO" 5; } ;; #( 'none needed') : ac_c_undeclared_builtin_options='' ;; #( *) : ac_c_undeclared_builtin_options=$ac_cv_c_undeclared_builtin_options ;; esac ac_fn_check_decl "$LINENO" "LLONG_MAX" "ac_cv_have_decl_LLONG_MAX" "#include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_LLONG_MAX" = xyes then : have_llong_max=1 fi ac_fn_check_decl "$LINENO" "LONG_LONG_MAX" "ac_cv_have_decl_LONG_LONG_MAX" "#include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_LONG_LONG_MAX" = xyes then : have_long_long_max=1 fi ac_fn_check_decl "$LINENO" "SYSTR_POLICY_KILL" "ac_cv_have_decl_SYSTR_POLICY_KILL" " #include #include #include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_SYSTR_POLICY_KILL" = xyes then : have_systr_policy_kill=1 fi ac_fn_check_decl "$LINENO" "RLIMIT_NPROC" "ac_cv_have_decl_RLIMIT_NPROC" " #include #include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_RLIMIT_NPROC" = xyes then : printf "%s\n" "#define HAVE_RLIMIT_NPROC /**/" >>confdefs.h fi ac_fn_check_decl "$LINENO" "PR_SET_NO_NEW_PRIVS" "ac_cv_have_decl_PR_SET_NO_NEW_PRIVS" " #include #include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_PR_SET_NO_NEW_PRIVS" = xyes then : have_linux_no_new_privs=1 fi openssl=yes openssl_bin=openssl # Check whether --with-openssl was given. if test ${with_openssl+y} then : withval=$with_openssl; if test "x$withval" = "xno" ; then openssl=no openssl_bin="" fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether OpenSSL will be used for cryptography" >&5 printf %s "checking whether OpenSSL will be used for cryptography... " >&6; } if test "x$openssl" = "xyes" ; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } printf "%s\n" "#define WITH_OPENSSL 1" >>confdefs.h else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi use_stack_protector=1 use_toolchain_hardening=1 # Check whether --with-stackprotect was given. if test ${with_stackprotect+y} then : withval=$with_stackprotect; if test "x$withval" = "xno"; then use_stack_protector=0 fi fi # Check whether --with-hardening was given. if test ${with_hardening+y} then : withval=$with_hardening; if test "x$withval" = "xno"; then use_toolchain_hardening=0 fi fi # We use -Werror for the tests only so that we catch warnings like "this is # on by default" for things like -fPIE. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $CC supports -Werror" >&5 printf %s "checking if $CC supports -Werror... " >&6; } saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -Werror" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main(void) { return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } WERROR="-Werror" else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } WERROR="" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS="$saved_CFLAGS" if test "$GCC" = "yes" || test "$GCC" = "egcs"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking gcc version" >&5 printf %s "checking gcc version... " >&6; } GCC_VER=`$CC -v 2>&1 | $AWK '/gcc version /{print $3}'` case "$GCC_VER" in 1.*) no_attrib_nonnull=1 ;; 2.8* | 2.9*) no_attrib_nonnull=1 ;; 2.*) no_attrib_nonnull=1 ;; *) ;; esac { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $GCC_VER" >&5 printf "%s\n" "$GCC_VER" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking clang version" >&5 printf %s "checking clang version... " >&6; } - CLANG_VER=`$CC -v 2>&1 | $AWK '/clang version /{print $3}'` + ver="`$CC -v 2>&1`" + if echo "$ver" | grep "Apple" >/dev/null; then + CLANG_VER="apple-`echo "$ver" | \ + awk '/Apple LLVM/ {print $4"-"$5}'`" + else + CLANG_VER=`echo "$ver" | $AWK '/clang version /{print $3}'` + fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CLANG_VER" >&5 printf "%s\n" "$CLANG_VER" >&6; } { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $CC supports compile flag -pipe" >&5 printf %s "checking if $CC supports compile flag -pipe... " >&6; } saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $WERROR -pipe" _define_flag="" test "x$_define_flag" = "x" && _define_flag="-pipe" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include /* Trivial function to help test for -fzero-call-used-regs */ void f(int n) {} int main(int argc, char **argv) { (void)argv; /* Some math to catch -ftrapv problems in the toolchain */ int i = 123 * argc, j = 456 + argc, k = 789 - argc; float l = i * 2.1; double m = l / 0.5; long long int n = argc * 12345LL, o = 12345LL * (long long int)argc; f(0); printf("%d %d %d %f %f %lld %lld\n", i, j, k, l, m, n, o); /* * Test fallthrough behaviour. clang 10's -Wimplicit-fallthrough does * not understand comments and we don't use the "fallthrough" attribute * that it's looking for. */ switch(i){ case 0: j += i; /* FALLTHROUGH */ default: j += k; } exit(0); } _ACEOF if ac_fn_c_try_compile "$LINENO" then : if $ac_cv_path_EGREP -i "unrecognized option|warning.*ignored" conftest.err >/dev/null then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } CFLAGS="$saved_CFLAGS" else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } CFLAGS="$saved_CFLAGS $_define_flag" fi else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } CFLAGS="$saved_CFLAGS" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext } { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $CC supports compile flag -Wunknown-warning-option" >&5 printf %s "checking if $CC supports compile flag -Wunknown-warning-option... " >&6; } saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $WERROR -Wunknown-warning-option" _define_flag="" test "x$_define_flag" = "x" && _define_flag="-Wunknown-warning-option" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include /* Trivial function to help test for -fzero-call-used-regs */ void f(int n) {} int main(int argc, char **argv) { (void)argv; /* Some math to catch -ftrapv problems in the toolchain */ int i = 123 * argc, j = 456 + argc, k = 789 - argc; float l = i * 2.1; double m = l / 0.5; long long int n = argc * 12345LL, o = 12345LL * (long long int)argc; f(0); printf("%d %d %d %f %f %lld %lld\n", i, j, k, l, m, n, o); /* * Test fallthrough behaviour. clang 10's -Wimplicit-fallthrough does * not understand comments and we don't use the "fallthrough" attribute * that it's looking for. */ switch(i){ case 0: j += i; /* FALLTHROUGH */ default: j += k; } exit(0); } _ACEOF if ac_fn_c_try_compile "$LINENO" then : if $ac_cv_path_EGREP -i "unrecognized option|warning.*ignored" conftest.err >/dev/null then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } CFLAGS="$saved_CFLAGS" else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } CFLAGS="$saved_CFLAGS $_define_flag" fi else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } CFLAGS="$saved_CFLAGS" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext } { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $CC supports compile flag -Wno-error=format-truncation" >&5 printf %s "checking if $CC supports compile flag -Wno-error=format-truncation... " >&6; } saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $WERROR -Wno-error=format-truncation" _define_flag="" test "x$_define_flag" = "x" && _define_flag="-Wno-error=format-truncation" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include /* Trivial function to help test for -fzero-call-used-regs */ void f(int n) {} int main(int argc, char **argv) { (void)argv; /* Some math to catch -ftrapv problems in the toolchain */ int i = 123 * argc, j = 456 + argc, k = 789 - argc; float l = i * 2.1; double m = l / 0.5; long long int n = argc * 12345LL, o = 12345LL * (long long int)argc; f(0); printf("%d %d %d %f %f %lld %lld\n", i, j, k, l, m, n, o); /* * Test fallthrough behaviour. clang 10's -Wimplicit-fallthrough does * not understand comments and we don't use the "fallthrough" attribute * that it's looking for. */ switch(i){ case 0: j += i; /* FALLTHROUGH */ default: j += k; } exit(0); } _ACEOF if ac_fn_c_try_compile "$LINENO" then : if $ac_cv_path_EGREP -i "unrecognized option|warning.*ignored" conftest.err >/dev/null then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } CFLAGS="$saved_CFLAGS" else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } CFLAGS="$saved_CFLAGS $_define_flag" fi else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } CFLAGS="$saved_CFLAGS" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext } { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $CC supports compile flag -Qunused-arguments" >&5 printf %s "checking if $CC supports compile flag -Qunused-arguments... " >&6; } saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $WERROR -Qunused-arguments" _define_flag="" test "x$_define_flag" = "x" && _define_flag="-Qunused-arguments" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include /* Trivial function to help test for -fzero-call-used-regs */ void f(int n) {} int main(int argc, char **argv) { (void)argv; /* Some math to catch -ftrapv problems in the toolchain */ int i = 123 * argc, j = 456 + argc, k = 789 - argc; float l = i * 2.1; double m = l / 0.5; long long int n = argc * 12345LL, o = 12345LL * (long long int)argc; f(0); printf("%d %d %d %f %f %lld %lld\n", i, j, k, l, m, n, o); /* * Test fallthrough behaviour. clang 10's -Wimplicit-fallthrough does * not understand comments and we don't use the "fallthrough" attribute * that it's looking for. */ switch(i){ case 0: j += i; /* FALLTHROUGH */ default: j += k; } exit(0); } _ACEOF if ac_fn_c_try_compile "$LINENO" then : if $ac_cv_path_EGREP -i "unrecognized option|warning.*ignored" conftest.err >/dev/null then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } CFLAGS="$saved_CFLAGS" else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } CFLAGS="$saved_CFLAGS $_define_flag" fi else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } CFLAGS="$saved_CFLAGS" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext } { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $CC supports compile flag -Wall" >&5 printf %s "checking if $CC supports compile flag -Wall... " >&6; } saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $WERROR -Wall" _define_flag="" test "x$_define_flag" = "x" && _define_flag="-Wall" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include /* Trivial function to help test for -fzero-call-used-regs */ void f(int n) {} int main(int argc, char **argv) { (void)argv; /* Some math to catch -ftrapv problems in the toolchain */ int i = 123 * argc, j = 456 + argc, k = 789 - argc; float l = i * 2.1; double m = l / 0.5; long long int n = argc * 12345LL, o = 12345LL * (long long int)argc; f(0); printf("%d %d %d %f %f %lld %lld\n", i, j, k, l, m, n, o); /* * Test fallthrough behaviour. clang 10's -Wimplicit-fallthrough does * not understand comments and we don't use the "fallthrough" attribute * that it's looking for. */ switch(i){ case 0: j += i; /* FALLTHROUGH */ default: j += k; } exit(0); } _ACEOF if ac_fn_c_try_compile "$LINENO" then : if $ac_cv_path_EGREP -i "unrecognized option|warning.*ignored" conftest.err >/dev/null then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } CFLAGS="$saved_CFLAGS" else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } CFLAGS="$saved_CFLAGS $_define_flag" fi else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } CFLAGS="$saved_CFLAGS" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext } { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $CC supports compile flag -Wextra" >&5 printf %s "checking if $CC supports compile flag -Wextra... " >&6; } saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $WERROR -Wextra" _define_flag="" test "x$_define_flag" = "x" && _define_flag="-Wextra" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include /* Trivial function to help test for -fzero-call-used-regs */ void f(int n) {} int main(int argc, char **argv) { (void)argv; /* Some math to catch -ftrapv problems in the toolchain */ int i = 123 * argc, j = 456 + argc, k = 789 - argc; float l = i * 2.1; double m = l / 0.5; long long int n = argc * 12345LL, o = 12345LL * (long long int)argc; f(0); printf("%d %d %d %f %f %lld %lld\n", i, j, k, l, m, n, o); /* * Test fallthrough behaviour. clang 10's -Wimplicit-fallthrough does * not understand comments and we don't use the "fallthrough" attribute * that it's looking for. */ switch(i){ case 0: j += i; /* FALLTHROUGH */ default: j += k; } exit(0); } _ACEOF if ac_fn_c_try_compile "$LINENO" then : if $ac_cv_path_EGREP -i "unrecognized option|warning.*ignored" conftest.err >/dev/null then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } CFLAGS="$saved_CFLAGS" else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } CFLAGS="$saved_CFLAGS $_define_flag" fi else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } CFLAGS="$saved_CFLAGS" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext } { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $CC supports compile flag -Wpointer-arith" >&5 printf %s "checking if $CC supports compile flag -Wpointer-arith... " >&6; } saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $WERROR -Wpointer-arith" _define_flag="" test "x$_define_flag" = "x" && _define_flag="-Wpointer-arith" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include /* Trivial function to help test for -fzero-call-used-regs */ void f(int n) {} int main(int argc, char **argv) { (void)argv; /* Some math to catch -ftrapv problems in the toolchain */ int i = 123 * argc, j = 456 + argc, k = 789 - argc; float l = i * 2.1; double m = l / 0.5; long long int n = argc * 12345LL, o = 12345LL * (long long int)argc; f(0); printf("%d %d %d %f %f %lld %lld\n", i, j, k, l, m, n, o); /* * Test fallthrough behaviour. clang 10's -Wimplicit-fallthrough does * not understand comments and we don't use the "fallthrough" attribute * that it's looking for. */ switch(i){ case 0: j += i; /* FALLTHROUGH */ default: j += k; } exit(0); } _ACEOF if ac_fn_c_try_compile "$LINENO" then : if $ac_cv_path_EGREP -i "unrecognized option|warning.*ignored" conftest.err >/dev/null then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } CFLAGS="$saved_CFLAGS" else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } CFLAGS="$saved_CFLAGS $_define_flag" fi else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } CFLAGS="$saved_CFLAGS" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext } { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $CC supports compile flag -Wuninitialized" >&5 printf %s "checking if $CC supports compile flag -Wuninitialized... " >&6; } saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $WERROR -Wuninitialized" _define_flag="" test "x$_define_flag" = "x" && _define_flag="-Wuninitialized" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include /* Trivial function to help test for -fzero-call-used-regs */ void f(int n) {} int main(int argc, char **argv) { (void)argv; /* Some math to catch -ftrapv problems in the toolchain */ int i = 123 * argc, j = 456 + argc, k = 789 - argc; float l = i * 2.1; double m = l / 0.5; long long int n = argc * 12345LL, o = 12345LL * (long long int)argc; f(0); printf("%d %d %d %f %f %lld %lld\n", i, j, k, l, m, n, o); /* * Test fallthrough behaviour. clang 10's -Wimplicit-fallthrough does * not understand comments and we don't use the "fallthrough" attribute * that it's looking for. */ switch(i){ case 0: j += i; /* FALLTHROUGH */ default: j += k; } exit(0); } _ACEOF if ac_fn_c_try_compile "$LINENO" then : if $ac_cv_path_EGREP -i "unrecognized option|warning.*ignored" conftest.err >/dev/null then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } CFLAGS="$saved_CFLAGS" else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } CFLAGS="$saved_CFLAGS $_define_flag" fi else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } CFLAGS="$saved_CFLAGS" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext } { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $CC supports compile flag -Wsign-compare" >&5 printf %s "checking if $CC supports compile flag -Wsign-compare... " >&6; } saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $WERROR -Wsign-compare" _define_flag="" test "x$_define_flag" = "x" && _define_flag="-Wsign-compare" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include /* Trivial function to help test for -fzero-call-used-regs */ void f(int n) {} int main(int argc, char **argv) { (void)argv; /* Some math to catch -ftrapv problems in the toolchain */ int i = 123 * argc, j = 456 + argc, k = 789 - argc; float l = i * 2.1; double m = l / 0.5; long long int n = argc * 12345LL, o = 12345LL * (long long int)argc; f(0); printf("%d %d %d %f %f %lld %lld\n", i, j, k, l, m, n, o); /* * Test fallthrough behaviour. clang 10's -Wimplicit-fallthrough does * not understand comments and we don't use the "fallthrough" attribute * that it's looking for. */ switch(i){ case 0: j += i; /* FALLTHROUGH */ default: j += k; } exit(0); } _ACEOF if ac_fn_c_try_compile "$LINENO" then : if $ac_cv_path_EGREP -i "unrecognized option|warning.*ignored" conftest.err >/dev/null then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } CFLAGS="$saved_CFLAGS" else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } CFLAGS="$saved_CFLAGS $_define_flag" fi else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } CFLAGS="$saved_CFLAGS" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext } { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $CC supports compile flag -Wformat-security" >&5 printf %s "checking if $CC supports compile flag -Wformat-security... " >&6; } saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $WERROR -Wformat-security" _define_flag="" test "x$_define_flag" = "x" && _define_flag="-Wformat-security" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include /* Trivial function to help test for -fzero-call-used-regs */ void f(int n) {} int main(int argc, char **argv) { (void)argv; /* Some math to catch -ftrapv problems in the toolchain */ int i = 123 * argc, j = 456 + argc, k = 789 - argc; float l = i * 2.1; double m = l / 0.5; long long int n = argc * 12345LL, o = 12345LL * (long long int)argc; f(0); printf("%d %d %d %f %f %lld %lld\n", i, j, k, l, m, n, o); /* * Test fallthrough behaviour. clang 10's -Wimplicit-fallthrough does * not understand comments and we don't use the "fallthrough" attribute * that it's looking for. */ switch(i){ case 0: j += i; /* FALLTHROUGH */ default: j += k; } exit(0); } _ACEOF if ac_fn_c_try_compile "$LINENO" then : if $ac_cv_path_EGREP -i "unrecognized option|warning.*ignored" conftest.err >/dev/null then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } CFLAGS="$saved_CFLAGS" else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } CFLAGS="$saved_CFLAGS $_define_flag" fi else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } CFLAGS="$saved_CFLAGS" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext } { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $CC supports compile flag -Wsizeof-pointer-memaccess" >&5 printf %s "checking if $CC supports compile flag -Wsizeof-pointer-memaccess... " >&6; } saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $WERROR -Wsizeof-pointer-memaccess" _define_flag="" test "x$_define_flag" = "x" && _define_flag="-Wsizeof-pointer-memaccess" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include /* Trivial function to help test for -fzero-call-used-regs */ void f(int n) {} int main(int argc, char **argv) { (void)argv; /* Some math to catch -ftrapv problems in the toolchain */ int i = 123 * argc, j = 456 + argc, k = 789 - argc; float l = i * 2.1; double m = l / 0.5; long long int n = argc * 12345LL, o = 12345LL * (long long int)argc; f(0); printf("%d %d %d %f %f %lld %lld\n", i, j, k, l, m, n, o); /* * Test fallthrough behaviour. clang 10's -Wimplicit-fallthrough does * not understand comments and we don't use the "fallthrough" attribute * that it's looking for. */ switch(i){ case 0: j += i; /* FALLTHROUGH */ default: j += k; } exit(0); } _ACEOF if ac_fn_c_try_compile "$LINENO" then : if $ac_cv_path_EGREP -i "unrecognized option|warning.*ignored" conftest.err >/dev/null then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } CFLAGS="$saved_CFLAGS" else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } CFLAGS="$saved_CFLAGS $_define_flag" fi else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } CFLAGS="$saved_CFLAGS" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext } { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $CC supports compile flag -Wpointer-sign" >&5 printf %s "checking if $CC supports compile flag -Wpointer-sign... " >&6; } saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $WERROR -Wpointer-sign" _define_flag="-Wno-pointer-sign" test "x$_define_flag" = "x" && _define_flag="-Wpointer-sign" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include /* Trivial function to help test for -fzero-call-used-regs */ void f(int n) {} int main(int argc, char **argv) { (void)argv; /* Some math to catch -ftrapv problems in the toolchain */ int i = 123 * argc, j = 456 + argc, k = 789 - argc; float l = i * 2.1; double m = l / 0.5; long long int n = argc * 12345LL, o = 12345LL * (long long int)argc; f(0); printf("%d %d %d %f %f %lld %lld\n", i, j, k, l, m, n, o); /* * Test fallthrough behaviour. clang 10's -Wimplicit-fallthrough does * not understand comments and we don't use the "fallthrough" attribute * that it's looking for. */ switch(i){ case 0: j += i; /* FALLTHROUGH */ default: j += k; } exit(0); } _ACEOF if ac_fn_c_try_compile "$LINENO" then : if $ac_cv_path_EGREP -i "unrecognized option|warning.*ignored" conftest.err >/dev/null then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } CFLAGS="$saved_CFLAGS" else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } CFLAGS="$saved_CFLAGS $_define_flag" fi else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } CFLAGS="$saved_CFLAGS" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext } { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $CC supports compile flag -Wunused-parameter" >&5 printf %s "checking if $CC supports compile flag -Wunused-parameter... " >&6; } saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $WERROR -Wunused-parameter" _define_flag="-Wno-unused-parameter" test "x$_define_flag" = "x" && _define_flag="-Wunused-parameter" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include /* Trivial function to help test for -fzero-call-used-regs */ void f(int n) {} int main(int argc, char **argv) { (void)argv; /* Some math to catch -ftrapv problems in the toolchain */ int i = 123 * argc, j = 456 + argc, k = 789 - argc; float l = i * 2.1; double m = l / 0.5; long long int n = argc * 12345LL, o = 12345LL * (long long int)argc; f(0); printf("%d %d %d %f %f %lld %lld\n", i, j, k, l, m, n, o); /* * Test fallthrough behaviour. clang 10's -Wimplicit-fallthrough does * not understand comments and we don't use the "fallthrough" attribute * that it's looking for. */ switch(i){ case 0: j += i; /* FALLTHROUGH */ default: j += k; } exit(0); } _ACEOF if ac_fn_c_try_compile "$LINENO" then : if $ac_cv_path_EGREP -i "unrecognized option|warning.*ignored" conftest.err >/dev/null then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } CFLAGS="$saved_CFLAGS" else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } CFLAGS="$saved_CFLAGS $_define_flag" fi else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } CFLAGS="$saved_CFLAGS" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext } { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $CC supports compile flag -Wunused-result" >&5 printf %s "checking if $CC supports compile flag -Wunused-result... " >&6; } saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $WERROR -Wunused-result" _define_flag="-Wno-unused-result" test "x$_define_flag" = "x" && _define_flag="-Wunused-result" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include /* Trivial function to help test for -fzero-call-used-regs */ void f(int n) {} int main(int argc, char **argv) { (void)argv; /* Some math to catch -ftrapv problems in the toolchain */ int i = 123 * argc, j = 456 + argc, k = 789 - argc; float l = i * 2.1; double m = l / 0.5; long long int n = argc * 12345LL, o = 12345LL * (long long int)argc; f(0); printf("%d %d %d %f %f %lld %lld\n", i, j, k, l, m, n, o); /* * Test fallthrough behaviour. clang 10's -Wimplicit-fallthrough does * not understand comments and we don't use the "fallthrough" attribute * that it's looking for. */ switch(i){ case 0: j += i; /* FALLTHROUGH */ default: j += k; } exit(0); } _ACEOF if ac_fn_c_try_compile "$LINENO" then : if $ac_cv_path_EGREP -i "unrecognized option|warning.*ignored" conftest.err >/dev/null then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } CFLAGS="$saved_CFLAGS" else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } CFLAGS="$saved_CFLAGS $_define_flag" fi else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } CFLAGS="$saved_CFLAGS" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext } { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $CC supports compile flag -Wimplicit-fallthrough" >&5 printf %s "checking if $CC supports compile flag -Wimplicit-fallthrough... " >&6; } saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $WERROR -Wimplicit-fallthrough" _define_flag="" test "x$_define_flag" = "x" && _define_flag="-Wimplicit-fallthrough" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include /* Trivial function to help test for -fzero-call-used-regs */ void f(int n) {} int main(int argc, char **argv) { (void)argv; /* Some math to catch -ftrapv problems in the toolchain */ int i = 123 * argc, j = 456 + argc, k = 789 - argc; float l = i * 2.1; double m = l / 0.5; long long int n = argc * 12345LL, o = 12345LL * (long long int)argc; f(0); printf("%d %d %d %f %f %lld %lld\n", i, j, k, l, m, n, o); /* * Test fallthrough behaviour. clang 10's -Wimplicit-fallthrough does * not understand comments and we don't use the "fallthrough" attribute * that it's looking for. */ switch(i){ case 0: j += i; /* FALLTHROUGH */ default: j += k; } exit(0); } _ACEOF if ac_fn_c_try_compile "$LINENO" then : if $ac_cv_path_EGREP -i "unrecognized option|warning.*ignored" conftest.err >/dev/null then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } CFLAGS="$saved_CFLAGS" else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } CFLAGS="$saved_CFLAGS $_define_flag" fi else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } CFLAGS="$saved_CFLAGS" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext } { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $CC supports compile flag -Wmisleading-indentation" >&5 printf %s "checking if $CC supports compile flag -Wmisleading-indentation... " >&6; } saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $WERROR -Wmisleading-indentation" _define_flag="" test "x$_define_flag" = "x" && _define_flag="-Wmisleading-indentation" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include /* Trivial function to help test for -fzero-call-used-regs */ void f(int n) {} int main(int argc, char **argv) { (void)argv; /* Some math to catch -ftrapv problems in the toolchain */ int i = 123 * argc, j = 456 + argc, k = 789 - argc; float l = i * 2.1; double m = l / 0.5; long long int n = argc * 12345LL, o = 12345LL * (long long int)argc; f(0); printf("%d %d %d %f %f %lld %lld\n", i, j, k, l, m, n, o); /* * Test fallthrough behaviour. clang 10's -Wimplicit-fallthrough does * not understand comments and we don't use the "fallthrough" attribute * that it's looking for. */ switch(i){ case 0: j += i; /* FALLTHROUGH */ default: j += k; } exit(0); } _ACEOF if ac_fn_c_try_compile "$LINENO" then : if $ac_cv_path_EGREP -i "unrecognized option|warning.*ignored" conftest.err >/dev/null then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } CFLAGS="$saved_CFLAGS" else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } CFLAGS="$saved_CFLAGS $_define_flag" fi else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } CFLAGS="$saved_CFLAGS" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext } { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $CC supports compile flag -Wbitwise-instead-of-logical" >&5 printf %s "checking if $CC supports compile flag -Wbitwise-instead-of-logical... " >&6; } saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $WERROR -Wbitwise-instead-of-logical" _define_flag="" test "x$_define_flag" = "x" && _define_flag="-Wbitwise-instead-of-logical" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include /* Trivial function to help test for -fzero-call-used-regs */ void f(int n) {} int main(int argc, char **argv) { (void)argv; /* Some math to catch -ftrapv problems in the toolchain */ int i = 123 * argc, j = 456 + argc, k = 789 - argc; float l = i * 2.1; double m = l / 0.5; long long int n = argc * 12345LL, o = 12345LL * (long long int)argc; f(0); printf("%d %d %d %f %f %lld %lld\n", i, j, k, l, m, n, o); /* * Test fallthrough behaviour. clang 10's -Wimplicit-fallthrough does * not understand comments and we don't use the "fallthrough" attribute * that it's looking for. */ switch(i){ case 0: j += i; /* FALLTHROUGH */ default: j += k; } exit(0); } _ACEOF if ac_fn_c_try_compile "$LINENO" then : if $ac_cv_path_EGREP -i "unrecognized option|warning.*ignored" conftest.err >/dev/null then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } CFLAGS="$saved_CFLAGS" else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } CFLAGS="$saved_CFLAGS $_define_flag" fi else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } CFLAGS="$saved_CFLAGS" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext } { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $CC supports compile flag -fno-strict-aliasing" >&5 printf %s "checking if $CC supports compile flag -fno-strict-aliasing... " >&6; } saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $WERROR -fno-strict-aliasing" _define_flag="" test "x$_define_flag" = "x" && _define_flag="-fno-strict-aliasing" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include /* Trivial function to help test for -fzero-call-used-regs */ void f(int n) {} int main(int argc, char **argv) { (void)argv; /* Some math to catch -ftrapv problems in the toolchain */ int i = 123 * argc, j = 456 + argc, k = 789 - argc; float l = i * 2.1; double m = l / 0.5; long long int n = argc * 12345LL, o = 12345LL * (long long int)argc; f(0); printf("%d %d %d %f %f %lld %lld\n", i, j, k, l, m, n, o); /* * Test fallthrough behaviour. clang 10's -Wimplicit-fallthrough does * not understand comments and we don't use the "fallthrough" attribute * that it's looking for. */ switch(i){ case 0: j += i; /* FALLTHROUGH */ default: j += k; } exit(0); } _ACEOF if ac_fn_c_try_compile "$LINENO" then : if $ac_cv_path_EGREP -i "unrecognized option|warning.*ignored" conftest.err >/dev/null then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } CFLAGS="$saved_CFLAGS" else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } CFLAGS="$saved_CFLAGS $_define_flag" fi else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } CFLAGS="$saved_CFLAGS" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext } if test "x$use_toolchain_hardening" = "x1"; then { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $CC supports compile flag -mretpoline" >&5 printf %s "checking if $CC supports compile flag -mretpoline... " >&6; } saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $WERROR -mretpoline" _define_flag="" test "x$_define_flag" = "x" && _define_flag="-mretpoline" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include /* Trivial function to help test for -fzero-call-used-regs */ void f(int n) {} int main(int argc, char **argv) { (void)argv; /* Some math to catch -ftrapv problems in the toolchain */ int i = 123 * argc, j = 456 + argc, k = 789 - argc; float l = i * 2.1; double m = l / 0.5; long long int n = argc * 12345LL, o = 12345LL * (long long int)argc; f(0); printf("%d %d %d %f %f %lld %lld\n", i, j, k, l, m, n, o); /* * Test fallthrough behaviour. clang 10's -Wimplicit-fallthrough does * not understand comments and we don't use the "fallthrough" attribute * that it's looking for. */ switch(i){ case 0: j += i; /* FALLTHROUGH */ default: j += k; } exit(0); } _ACEOF if ac_fn_c_try_compile "$LINENO" then : if $ac_cv_path_EGREP -i "unrecognized option|warning.*ignored" conftest.err >/dev/null then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } CFLAGS="$saved_CFLAGS" else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } CFLAGS="$saved_CFLAGS $_define_flag" fi else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } CFLAGS="$saved_CFLAGS" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext } # clang { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $LD supports link flag -Wl,-z,retpolineplt" >&5 printf %s "checking if $LD supports link flag -Wl,-z,retpolineplt... " >&6; } saved_LDFLAGS="$LDFLAGS" LDFLAGS="$LDFLAGS $WERROR -Wl,-z,retpolineplt" _define_flag="" test "x$_define_flag" = "x" && _define_flag="-Wl,-z,retpolineplt" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main(int argc, char **argv) { (void)argv; /* Some math to catch -ftrapv problems in the toolchain */ int i = 123 * argc, j = 456 + argc, k = 789 - argc; float l = i * 2.1; double m = l / 0.5; long long int n = argc * 12345LL, o = 12345LL * (long long int)argc; long long p = n * o; printf("%d %d %d %f %f %lld %lld %lld\n", i, j, k, l, m, n, o, p); exit(0); } _ACEOF if ac_fn_c_try_link "$LINENO" then : if $ac_cv_path_EGREP -i "unrecognized option|warning.*ignored" conftest.err >/dev/null then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } LDFLAGS="$saved_LDFLAGS" else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } LDFLAGS="$saved_LDFLAGS $_define_flag" fi else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } LDFLAGS="$saved_LDFLAGS" fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext } { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $CC supports compile flag -D_FORTIFY_SOURCE=2" >&5 printf %s "checking if $CC supports compile flag -D_FORTIFY_SOURCE=2... " >&6; } saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $WERROR -D_FORTIFY_SOURCE=2" _define_flag="" test "x$_define_flag" = "x" && _define_flag="-D_FORTIFY_SOURCE=2" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include /* Trivial function to help test for -fzero-call-used-regs */ void f(int n) {} int main(int argc, char **argv) { (void)argv; /* Some math to catch -ftrapv problems in the toolchain */ int i = 123 * argc, j = 456 + argc, k = 789 - argc; float l = i * 2.1; double m = l / 0.5; long long int n = argc * 12345LL, o = 12345LL * (long long int)argc; f(0); printf("%d %d %d %f %f %lld %lld\n", i, j, k, l, m, n, o); /* * Test fallthrough behaviour. clang 10's -Wimplicit-fallthrough does * not understand comments and we don't use the "fallthrough" attribute * that it's looking for. */ switch(i){ case 0: j += i; /* FALLTHROUGH */ default: j += k; } exit(0); } _ACEOF if ac_fn_c_try_compile "$LINENO" then : if $ac_cv_path_EGREP -i "unrecognized option|warning.*ignored" conftest.err >/dev/null then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } CFLAGS="$saved_CFLAGS" else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } CFLAGS="$saved_CFLAGS $_define_flag" fi else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } CFLAGS="$saved_CFLAGS" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext } { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $LD supports link flag -Wl,-z,relro" >&5 printf %s "checking if $LD supports link flag -Wl,-z,relro... " >&6; } saved_LDFLAGS="$LDFLAGS" LDFLAGS="$LDFLAGS $WERROR -Wl,-z,relro" _define_flag="" test "x$_define_flag" = "x" && _define_flag="-Wl,-z,relro" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main(int argc, char **argv) { (void)argv; /* Some math to catch -ftrapv problems in the toolchain */ int i = 123 * argc, j = 456 + argc, k = 789 - argc; float l = i * 2.1; double m = l / 0.5; long long int n = argc * 12345LL, o = 12345LL * (long long int)argc; long long p = n * o; printf("%d %d %d %f %f %lld %lld %lld\n", i, j, k, l, m, n, o, p); exit(0); } _ACEOF if ac_fn_c_try_link "$LINENO" then : if $ac_cv_path_EGREP -i "unrecognized option|warning.*ignored" conftest.err >/dev/null then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } LDFLAGS="$saved_LDFLAGS" else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } LDFLAGS="$saved_LDFLAGS $_define_flag" fi else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } LDFLAGS="$saved_LDFLAGS" fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext } { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $LD supports link flag -Wl,-z,now" >&5 printf %s "checking if $LD supports link flag -Wl,-z,now... " >&6; } saved_LDFLAGS="$LDFLAGS" LDFLAGS="$LDFLAGS $WERROR -Wl,-z,now" _define_flag="" test "x$_define_flag" = "x" && _define_flag="-Wl,-z,now" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main(int argc, char **argv) { (void)argv; /* Some math to catch -ftrapv problems in the toolchain */ int i = 123 * argc, j = 456 + argc, k = 789 - argc; float l = i * 2.1; double m = l / 0.5; long long int n = argc * 12345LL, o = 12345LL * (long long int)argc; long long p = n * o; printf("%d %d %d %f %f %lld %lld %lld\n", i, j, k, l, m, n, o, p); exit(0); } _ACEOF if ac_fn_c_try_link "$LINENO" then : if $ac_cv_path_EGREP -i "unrecognized option|warning.*ignored" conftest.err >/dev/null then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } LDFLAGS="$saved_LDFLAGS" else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } LDFLAGS="$saved_LDFLAGS $_define_flag" fi else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } LDFLAGS="$saved_LDFLAGS" fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext } { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $LD supports link flag -Wl,-z,noexecstack" >&5 printf %s "checking if $LD supports link flag -Wl,-z,noexecstack... " >&6; } saved_LDFLAGS="$LDFLAGS" LDFLAGS="$LDFLAGS $WERROR -Wl,-z,noexecstack" _define_flag="" test "x$_define_flag" = "x" && _define_flag="-Wl,-z,noexecstack" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main(int argc, char **argv) { (void)argv; /* Some math to catch -ftrapv problems in the toolchain */ int i = 123 * argc, j = 456 + argc, k = 789 - argc; float l = i * 2.1; double m = l / 0.5; long long int n = argc * 12345LL, o = 12345LL * (long long int)argc; long long p = n * o; printf("%d %d %d %f %f %lld %lld %lld\n", i, j, k, l, m, n, o, p); exit(0); } _ACEOF if ac_fn_c_try_link "$LINENO" then : if $ac_cv_path_EGREP -i "unrecognized option|warning.*ignored" conftest.err >/dev/null then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } LDFLAGS="$saved_LDFLAGS" else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } LDFLAGS="$saved_LDFLAGS $_define_flag" fi else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } LDFLAGS="$saved_LDFLAGS" fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext } # NB. -ftrapv expects certain support functions to be present in # the compiler library (libgcc or similar) to detect integer operations # that can overflow. We must check that the result of enabling it # actually links. The test program compiled/linked includes a number # of integer operations that should exercise this. { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $CC supports compile flag -ftrapv and linking succeeds" >&5 printf %s "checking if $CC supports compile flag -ftrapv and linking succeeds... " >&6; } saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $WERROR -ftrapv" _define_flag="" test "x$_define_flag" = "x" && _define_flag="-ftrapv" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main(int argc, char **argv) { (void)argv; /* Some math to catch -ftrapv problems in the toolchain */ int i = 123 * argc, j = 456 + argc, k = 789 - argc; float l = i * 2.1; double m = l / 0.5; long long int n = argc * 12345LL, o = 12345LL * (long long int)argc; long long int p = n * o; printf("%d %d %d %f %f %lld %lld %lld\n", i, j, k, l, m, n, o, p); exit(0); } _ACEOF if ac_fn_c_try_link "$LINENO" then : if $ac_cv_path_EGREP -i "unrecognized option|warning.*ignored" conftest.err >/dev/null then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } CFLAGS="$saved_CFLAGS" else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } CFLAGS="$saved_CFLAGS $_define_flag" fi else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } CFLAGS="$saved_CFLAGS" fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext } # clang 15 seems to have a bug in -fzero-call-used-regs=all. See # https://bugzilla.mindrot.org/show_bug.cgi?id=3475 and # https://github.com/llvm/llvm-project/issues/59242 case "$CLANG_VER" in - 15.*) { + 15.*|apple*) { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $CC supports compile flag -fzero-call-used-regs=used" >&5 printf %s "checking if $CC supports compile flag -fzero-call-used-regs=used... " >&6; } saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $WERROR -fzero-call-used-regs=used" _define_flag="" test "x$_define_flag" = "x" && _define_flag="-fzero-call-used-regs=used" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include /* Trivial function to help test for -fzero-call-used-regs */ void f(int n) {} int main(int argc, char **argv) { (void)argv; /* Some math to catch -ftrapv problems in the toolchain */ int i = 123 * argc, j = 456 + argc, k = 789 - argc; float l = i * 2.1; double m = l / 0.5; long long int n = argc * 12345LL, o = 12345LL * (long long int)argc; f(0); printf("%d %d %d %f %f %lld %lld\n", i, j, k, l, m, n, o); /* * Test fallthrough behaviour. clang 10's -Wimplicit-fallthrough does * not understand comments and we don't use the "fallthrough" attribute * that it's looking for. */ switch(i){ case 0: j += i; /* FALLTHROUGH */ default: j += k; } exit(0); } _ACEOF if ac_fn_c_try_compile "$LINENO" then : if $ac_cv_path_EGREP -i "unrecognized option|warning.*ignored" conftest.err >/dev/null then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } CFLAGS="$saved_CFLAGS" else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } CFLAGS="$saved_CFLAGS $_define_flag" fi else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } CFLAGS="$saved_CFLAGS" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext } ;; *) { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $CC supports compile flag -fzero-call-used-regs=all" >&5 printf %s "checking if $CC supports compile flag -fzero-call-used-regs=all... " >&6; } saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $WERROR -fzero-call-used-regs=all" _define_flag="" test "x$_define_flag" = "x" && _define_flag="-fzero-call-used-regs=all" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include /* Trivial function to help test for -fzero-call-used-regs */ void f(int n) {} int main(int argc, char **argv) { (void)argv; /* Some math to catch -ftrapv problems in the toolchain */ int i = 123 * argc, j = 456 + argc, k = 789 - argc; float l = i * 2.1; double m = l / 0.5; long long int n = argc * 12345LL, o = 12345LL * (long long int)argc; f(0); printf("%d %d %d %f %f %lld %lld\n", i, j, k, l, m, n, o); /* * Test fallthrough behaviour. clang 10's -Wimplicit-fallthrough does * not understand comments and we don't use the "fallthrough" attribute * that it's looking for. */ switch(i){ case 0: j += i; /* FALLTHROUGH */ default: j += k; } exit(0); } _ACEOF if ac_fn_c_try_compile "$LINENO" then : if $ac_cv_path_EGREP -i "unrecognized option|warning.*ignored" conftest.err >/dev/null then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } CFLAGS="$saved_CFLAGS" else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } CFLAGS="$saved_CFLAGS $_define_flag" fi else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } CFLAGS="$saved_CFLAGS" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext } ;; esac { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $CC supports compile flag -ftrivial-auto-var-init=zero" >&5 printf %s "checking if $CC supports compile flag -ftrivial-auto-var-init=zero... " >&6; } saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $WERROR -ftrivial-auto-var-init=zero" _define_flag="" test "x$_define_flag" = "x" && _define_flag="-ftrivial-auto-var-init=zero" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include /* Trivial function to help test for -fzero-call-used-regs */ void f(int n) {} int main(int argc, char **argv) { (void)argv; /* Some math to catch -ftrapv problems in the toolchain */ int i = 123 * argc, j = 456 + argc, k = 789 - argc; float l = i * 2.1; double m = l / 0.5; long long int n = argc * 12345LL, o = 12345LL * (long long int)argc; f(0); printf("%d %d %d %f %f %lld %lld\n", i, j, k, l, m, n, o); /* * Test fallthrough behaviour. clang 10's -Wimplicit-fallthrough does * not understand comments and we don't use the "fallthrough" attribute * that it's looking for. */ switch(i){ case 0: j += i; /* FALLTHROUGH */ default: j += k; } exit(0); } _ACEOF if ac_fn_c_try_compile "$LINENO" then : if $ac_cv_path_EGREP -i "unrecognized option|warning.*ignored" conftest.err >/dev/null then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } CFLAGS="$saved_CFLAGS" else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } CFLAGS="$saved_CFLAGS $_define_flag" fi else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } CFLAGS="$saved_CFLAGS" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext } fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $CC accepts -fno-builtin-memset" >&5 printf %s "checking if $CC accepts -fno-builtin-memset... " >&6; } saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -fno-builtin-memset" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { char b[10]; memset(b, 0, sizeof(b)); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } CFLAGS="$saved_CFLAGS" fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext # -fstack-protector-all doesn't always work for some GCC versions # and/or platforms, so we test if we can. If it's not supported # on a given platform gcc will emit a warning so we use -Werror. if test "x$use_stack_protector" = "x1"; then for t in -fstack-protector-strong -fstack-protector-all \ -fstack-protector; do { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $CC supports $t" >&5 printf %s "checking if $CC supports $t... " >&6; } saved_CFLAGS="$CFLAGS" saved_LDFLAGS="$LDFLAGS" CFLAGS="$CFLAGS $t -Werror" LDFLAGS="$LDFLAGS $t -Werror" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int func (int t) {char b[100]; snprintf(b,sizeof b,"%d",t); return t;} int main (void) { char x[256]; snprintf(x, sizeof(x), "XXX%d", func(1)); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } CFLAGS="$saved_CFLAGS $t" LDFLAGS="$saved_LDFLAGS $t" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $t works" >&5 printf %s "checking if $t works... " >&6; } if test "$cross_compiling" = yes then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cross compiling: cannot test" >&5 printf "%s\n" "$as_me: WARNING: cross compiling: cannot test" >&2;} break else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int func (int t) {char b[100]; snprintf(b,sizeof b,"%d",t); return t;} int main (void) { char x[256]; snprintf(x, sizeof(x), "XXX%d", func(1)); ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } break else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext CFLAGS="$saved_CFLAGS" LDFLAGS="$saved_LDFLAGS" done fi if test -z "$have_llong_max"; then # retry LLONG_MAX with -std=gnu99, needed on some Linuxes unset ac_cv_have_decl_LLONG_MAX saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -std=gnu99" ac_fn_check_decl "$LINENO" "LLONG_MAX" "ac_cv_have_decl_LLONG_MAX" "#include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_LLONG_MAX" = xyes then : have_llong_max=1 else $as_nop CFLAGS="$saved_CFLAGS" fi fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if compiler allows __attribute__ on return types" >&5 printf %s "checking if compiler allows __attribute__ on return types... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include __attribute__((__unused__)) static void foo(void){return;} int main (void) { exit(0); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } printf "%s\n" "#define NO_ATTRIBUTE_ON_RETURN_TYPE 1" >>confdefs.h fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if compiler allows __attribute__ prototype args" >&5 printf %s "checking if compiler allows __attribute__ prototype args... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include typedef void foo(const char *, ...) __attribute__((format(printf, 1, 2))); int main (void) { exit(0); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } printf "%s\n" "#define NO_ATTRIBUTE_ON_PROTOTYPE_ARGS 1" >>confdefs.h fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if compiler supports variable length arrays" >&5 printf %s "checking if compiler supports variable length arrays... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { int i; for (i=0; i<3; i++){int a[i]; a[i-1]=0;} exit(0); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } printf "%s\n" "#define VARIABLE_LENGTH_ARRAYS 1" >>confdefs.h else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if compiler accepts variable declarations after code" >&5 printf %s "checking if compiler accepts variable declarations after code... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { int a; a = 1; int b = 1; exit(a-b); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } printf "%s\n" "#define VARIABLE_DECLARATION_AFTER_CODE 1" >>confdefs.h else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext if test "x$no_attrib_nonnull" != "x1" ; then printf "%s\n" "#define HAVE_ATTRIBUTE__NONNULL__ 1" >>confdefs.h fi # Check whether --with-rpath was given. if test ${with_rpath+y} then : withval=$with_rpath; if test "x$withval" = "xno" ; then rpath_opt="" elif test "x$withval" = "xyes" ; then rpath_opt="-R" else rpath_opt="$withval" fi fi # Allow user to specify flags # Check whether --with-cflags was given. if test ${with_cflags+y} then : withval=$with_cflags; if test -n "$withval" && test "x$withval" != "xno" && \ test "x${withval}" != "xyes"; then CFLAGS="$CFLAGS $withval" fi fi # Check whether --with-cflags-after was given. if test ${with_cflags_after+y} then : withval=$with_cflags_after; if test -n "$withval" && test "x$withval" != "xno" && \ test "x${withval}" != "xyes"; then CFLAGS_AFTER="$withval" fi fi # Check whether --with-cppflags was given. if test ${with_cppflags+y} then : withval=$with_cppflags; if test -n "$withval" && test "x$withval" != "xno" && \ test "x${withval}" != "xyes"; then CPPFLAGS="$CPPFLAGS $withval" fi fi # Check whether --with-ldflags was given. if test ${with_ldflags+y} then : withval=$with_ldflags; if test -n "$withval" && test "x$withval" != "xno" && \ test "x${withval}" != "xyes"; then LDFLAGS="$LDFLAGS $withval" fi fi # Check whether --with-ldflags-after was given. if test ${with_ldflags_after+y} then : withval=$with_ldflags_after; if test -n "$withval" && test "x$withval" != "xno" && \ test "x${withval}" != "xyes"; then LDFLAGS_AFTER="$withval" fi fi # Check whether --with-libs was given. if test ${with_libs+y} then : withval=$with_libs; if test -n "$withval" && test "x$withval" != "xno" && \ test "x${withval}" != "xyes"; then LIBS="$LIBS $withval" fi fi # Check whether --with-Werror was given. if test ${with_Werror+y} then : withval=$with_Werror; if test -n "$withval" && test "x$withval" != "xno"; then werror_flags="-Werror" if test "x${withval}" != "xyes"; then werror_flags="$withval" fi fi fi if test "x$ac_cv_header_sys_stat_h" != "xyes"; then unset ac_cv_header_sys_stat_h ac_fn_c_check_header_compile "$LINENO" "sys/stat.h" "ac_cv_header_sys_stat_h" "$ac_includes_default" if test "x$ac_cv_header_sys_stat_h" = xyes then : printf "%s\n" "#define HAVE_SYS_STAT_H 1" >>confdefs.h fi fi ac_fn_c_check_header_compile "$LINENO" "blf.h" "ac_cv_header_blf_h" "$ac_includes_default" if test "x$ac_cv_header_blf_h" = xyes then : printf "%s\n" "#define HAVE_BLF_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "bstring.h" "ac_cv_header_bstring_h" "$ac_includes_default" if test "x$ac_cv_header_bstring_h" = xyes then : printf "%s\n" "#define HAVE_BSTRING_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "crypt.h" "ac_cv_header_crypt_h" "$ac_includes_default" if test "x$ac_cv_header_crypt_h" = xyes then : printf "%s\n" "#define HAVE_CRYPT_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "crypto/sha2.h" "ac_cv_header_crypto_sha2_h" "$ac_includes_default" if test "x$ac_cv_header_crypto_sha2_h" = xyes then : printf "%s\n" "#define HAVE_CRYPTO_SHA2_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "dirent.h" "ac_cv_header_dirent_h" "$ac_includes_default" if test "x$ac_cv_header_dirent_h" = xyes then : printf "%s\n" "#define HAVE_DIRENT_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "endian.h" "ac_cv_header_endian_h" "$ac_includes_default" if test "x$ac_cv_header_endian_h" = xyes then : printf "%s\n" "#define HAVE_ENDIAN_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "elf.h" "ac_cv_header_elf_h" "$ac_includes_default" if test "x$ac_cv_header_elf_h" = xyes then : printf "%s\n" "#define HAVE_ELF_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "err.h" "ac_cv_header_err_h" "$ac_includes_default" if test "x$ac_cv_header_err_h" = xyes then : printf "%s\n" "#define HAVE_ERR_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "features.h" "ac_cv_header_features_h" "$ac_includes_default" if test "x$ac_cv_header_features_h" = xyes then : printf "%s\n" "#define HAVE_FEATURES_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "fcntl.h" "ac_cv_header_fcntl_h" "$ac_includes_default" if test "x$ac_cv_header_fcntl_h" = xyes then : printf "%s\n" "#define HAVE_FCNTL_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "floatingpoint.h" "ac_cv_header_floatingpoint_h" "$ac_includes_default" if test "x$ac_cv_header_floatingpoint_h" = xyes then : printf "%s\n" "#define HAVE_FLOATINGPOINT_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "fnmatch.h" "ac_cv_header_fnmatch_h" "$ac_includes_default" if test "x$ac_cv_header_fnmatch_h" = xyes then : printf "%s\n" "#define HAVE_FNMATCH_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "getopt.h" "ac_cv_header_getopt_h" "$ac_includes_default" if test "x$ac_cv_header_getopt_h" = xyes then : printf "%s\n" "#define HAVE_GETOPT_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "glob.h" "ac_cv_header_glob_h" "$ac_includes_default" if test "x$ac_cv_header_glob_h" = xyes then : printf "%s\n" "#define HAVE_GLOB_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "ia.h" "ac_cv_header_ia_h" "$ac_includes_default" if test "x$ac_cv_header_ia_h" = xyes then : printf "%s\n" "#define HAVE_IA_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "iaf.h" "ac_cv_header_iaf_h" "$ac_includes_default" if test "x$ac_cv_header_iaf_h" = xyes then : printf "%s\n" "#define HAVE_IAF_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "ifaddrs.h" "ac_cv_header_ifaddrs_h" "$ac_includes_default" if test "x$ac_cv_header_ifaddrs_h" = xyes then : printf "%s\n" "#define HAVE_IFADDRS_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "inttypes.h" "ac_cv_header_inttypes_h" "$ac_includes_default" if test "x$ac_cv_header_inttypes_h" = xyes then : printf "%s\n" "#define HAVE_INTTYPES_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "langinfo.h" "ac_cv_header_langinfo_h" "$ac_includes_default" if test "x$ac_cv_header_langinfo_h" = xyes then : printf "%s\n" "#define HAVE_LANGINFO_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "limits.h" "ac_cv_header_limits_h" "$ac_includes_default" if test "x$ac_cv_header_limits_h" = xyes then : printf "%s\n" "#define HAVE_LIMITS_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "locale.h" "ac_cv_header_locale_h" "$ac_includes_default" if test "x$ac_cv_header_locale_h" = xyes then : printf "%s\n" "#define HAVE_LOCALE_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "login.h" "ac_cv_header_login_h" "$ac_includes_default" if test "x$ac_cv_header_login_h" = xyes then : printf "%s\n" "#define HAVE_LOGIN_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "maillock.h" "ac_cv_header_maillock_h" "$ac_includes_default" if test "x$ac_cv_header_maillock_h" = xyes then : printf "%s\n" "#define HAVE_MAILLOCK_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "ndir.h" "ac_cv_header_ndir_h" "$ac_includes_default" if test "x$ac_cv_header_ndir_h" = xyes then : printf "%s\n" "#define HAVE_NDIR_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "net/if_tun.h" "ac_cv_header_net_if_tun_h" "$ac_includes_default" if test "x$ac_cv_header_net_if_tun_h" = xyes then : printf "%s\n" "#define HAVE_NET_IF_TUN_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "netdb.h" "ac_cv_header_netdb_h" "$ac_includes_default" if test "x$ac_cv_header_netdb_h" = xyes then : printf "%s\n" "#define HAVE_NETDB_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "netgroup.h" "ac_cv_header_netgroup_h" "$ac_includes_default" if test "x$ac_cv_header_netgroup_h" = xyes then : printf "%s\n" "#define HAVE_NETGROUP_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "pam/pam_appl.h" "ac_cv_header_pam_pam_appl_h" "$ac_includes_default" if test "x$ac_cv_header_pam_pam_appl_h" = xyes then : printf "%s\n" "#define HAVE_PAM_PAM_APPL_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "paths.h" "ac_cv_header_paths_h" "$ac_includes_default" if test "x$ac_cv_header_paths_h" = xyes then : printf "%s\n" "#define HAVE_PATHS_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "poll.h" "ac_cv_header_poll_h" "$ac_includes_default" if test "x$ac_cv_header_poll_h" = xyes then : printf "%s\n" "#define HAVE_POLL_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "pty.h" "ac_cv_header_pty_h" "$ac_includes_default" if test "x$ac_cv_header_pty_h" = xyes then : printf "%s\n" "#define HAVE_PTY_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "readpassphrase.h" "ac_cv_header_readpassphrase_h" "$ac_includes_default" if test "x$ac_cv_header_readpassphrase_h" = xyes then : printf "%s\n" "#define HAVE_READPASSPHRASE_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "rpc/types.h" "ac_cv_header_rpc_types_h" "$ac_includes_default" if test "x$ac_cv_header_rpc_types_h" = xyes then : printf "%s\n" "#define HAVE_RPC_TYPES_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "security/pam_appl.h" "ac_cv_header_security_pam_appl_h" "$ac_includes_default" if test "x$ac_cv_header_security_pam_appl_h" = xyes then : printf "%s\n" "#define HAVE_SECURITY_PAM_APPL_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "sha2.h" "ac_cv_header_sha2_h" "$ac_includes_default" if test "x$ac_cv_header_sha2_h" = xyes then : printf "%s\n" "#define HAVE_SHA2_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "shadow.h" "ac_cv_header_shadow_h" "$ac_includes_default" if test "x$ac_cv_header_shadow_h" = xyes then : printf "%s\n" "#define HAVE_SHADOW_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "stddef.h" "ac_cv_header_stddef_h" "$ac_includes_default" if test "x$ac_cv_header_stddef_h" = xyes then : printf "%s\n" "#define HAVE_STDDEF_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "stdint.h" "ac_cv_header_stdint_h" "$ac_includes_default" if test "x$ac_cv_header_stdint_h" = xyes then : printf "%s\n" "#define HAVE_STDINT_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "string.h" "ac_cv_header_string_h" "$ac_includes_default" if test "x$ac_cv_header_string_h" = xyes then : printf "%s\n" "#define HAVE_STRING_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "strings.h" "ac_cv_header_strings_h" "$ac_includes_default" if test "x$ac_cv_header_strings_h" = xyes then : printf "%s\n" "#define HAVE_STRINGS_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "sys/bitypes.h" "ac_cv_header_sys_bitypes_h" "$ac_includes_default" if test "x$ac_cv_header_sys_bitypes_h" = xyes then : printf "%s\n" "#define HAVE_SYS_BITYPES_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "sys/byteorder.h" "ac_cv_header_sys_byteorder_h" "$ac_includes_default" if test "x$ac_cv_header_sys_byteorder_h" = xyes then : printf "%s\n" "#define HAVE_SYS_BYTEORDER_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "sys/bsdtty.h" "ac_cv_header_sys_bsdtty_h" "$ac_includes_default" if test "x$ac_cv_header_sys_bsdtty_h" = xyes then : printf "%s\n" "#define HAVE_SYS_BSDTTY_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "sys/cdefs.h" "ac_cv_header_sys_cdefs_h" "$ac_includes_default" if test "x$ac_cv_header_sys_cdefs_h" = xyes then : printf "%s\n" "#define HAVE_SYS_CDEFS_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "sys/dir.h" "ac_cv_header_sys_dir_h" "$ac_includes_default" if test "x$ac_cv_header_sys_dir_h" = xyes then : printf "%s\n" "#define HAVE_SYS_DIR_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "sys/file.h" "ac_cv_header_sys_file_h" "$ac_includes_default" if test "x$ac_cv_header_sys_file_h" = xyes then : printf "%s\n" "#define HAVE_SYS_FILE_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "sys/mman.h" "ac_cv_header_sys_mman_h" "$ac_includes_default" if test "x$ac_cv_header_sys_mman_h" = xyes then : printf "%s\n" "#define HAVE_SYS_MMAN_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "sys/label.h" "ac_cv_header_sys_label_h" "$ac_includes_default" if test "x$ac_cv_header_sys_label_h" = xyes then : printf "%s\n" "#define HAVE_SYS_LABEL_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "sys/ndir.h" "ac_cv_header_sys_ndir_h" "$ac_includes_default" if test "x$ac_cv_header_sys_ndir_h" = xyes then : printf "%s\n" "#define HAVE_SYS_NDIR_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "sys/param.h" "ac_cv_header_sys_param_h" "$ac_includes_default" if test "x$ac_cv_header_sys_param_h" = xyes then : printf "%s\n" "#define HAVE_SYS_PARAM_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "sys/poll.h" "ac_cv_header_sys_poll_h" "$ac_includes_default" if test "x$ac_cv_header_sys_poll_h" = xyes then : printf "%s\n" "#define HAVE_SYS_POLL_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "sys/prctl.h" "ac_cv_header_sys_prctl_h" "$ac_includes_default" if test "x$ac_cv_header_sys_prctl_h" = xyes then : printf "%s\n" "#define HAVE_SYS_PRCTL_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "sys/procctl.h" "ac_cv_header_sys_procctl_h" "$ac_includes_default" if test "x$ac_cv_header_sys_procctl_h" = xyes then : printf "%s\n" "#define HAVE_SYS_PROCCTL_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "sys/pstat.h" "ac_cv_header_sys_pstat_h" "$ac_includes_default" if test "x$ac_cv_header_sys_pstat_h" = xyes then : printf "%s\n" "#define HAVE_SYS_PSTAT_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "sys/ptrace.h" "ac_cv_header_sys_ptrace_h" "$ac_includes_default" if test "x$ac_cv_header_sys_ptrace_h" = xyes then : printf "%s\n" "#define HAVE_SYS_PTRACE_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "sys/random.h" "ac_cv_header_sys_random_h" "$ac_includes_default" if test "x$ac_cv_header_sys_random_h" = xyes then : printf "%s\n" "#define HAVE_SYS_RANDOM_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "sys/select.h" "ac_cv_header_sys_select_h" "$ac_includes_default" if test "x$ac_cv_header_sys_select_h" = xyes then : printf "%s\n" "#define HAVE_SYS_SELECT_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "sys/stream.h" "ac_cv_header_sys_stream_h" "$ac_includes_default" if test "x$ac_cv_header_sys_stream_h" = xyes then : printf "%s\n" "#define HAVE_SYS_STREAM_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "sys/stropts.h" "ac_cv_header_sys_stropts_h" "$ac_includes_default" if test "x$ac_cv_header_sys_stropts_h" = xyes then : printf "%s\n" "#define HAVE_SYS_STROPTS_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "sys/strtio.h" "ac_cv_header_sys_strtio_h" "$ac_includes_default" if test "x$ac_cv_header_sys_strtio_h" = xyes then : printf "%s\n" "#define HAVE_SYS_STRTIO_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "sys/statvfs.h" "ac_cv_header_sys_statvfs_h" "$ac_includes_default" if test "x$ac_cv_header_sys_statvfs_h" = xyes then : printf "%s\n" "#define HAVE_SYS_STATVFS_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "sys/sysmacros.h" "ac_cv_header_sys_sysmacros_h" "$ac_includes_default" if test "x$ac_cv_header_sys_sysmacros_h" = xyes then : printf "%s\n" "#define HAVE_SYS_SYSMACROS_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "sys/time.h" "ac_cv_header_sys_time_h" "$ac_includes_default" if test "x$ac_cv_header_sys_time_h" = xyes then : printf "%s\n" "#define HAVE_SYS_TIME_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "sys/timers.h" "ac_cv_header_sys_timers_h" "$ac_includes_default" if test "x$ac_cv_header_sys_timers_h" = xyes then : printf "%s\n" "#define HAVE_SYS_TIMERS_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "sys/vfs.h" "ac_cv_header_sys_vfs_h" "$ac_includes_default" if test "x$ac_cv_header_sys_vfs_h" = xyes then : printf "%s\n" "#define HAVE_SYS_VFS_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "time.h" "ac_cv_header_time_h" "$ac_includes_default" if test "x$ac_cv_header_time_h" = xyes then : printf "%s\n" "#define HAVE_TIME_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "tmpdir.h" "ac_cv_header_tmpdir_h" "$ac_includes_default" if test "x$ac_cv_header_tmpdir_h" = xyes then : printf "%s\n" "#define HAVE_TMPDIR_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "ttyent.h" "ac_cv_header_ttyent_h" "$ac_includes_default" if test "x$ac_cv_header_ttyent_h" = xyes then : printf "%s\n" "#define HAVE_TTYENT_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "ucred.h" "ac_cv_header_ucred_h" "$ac_includes_default" if test "x$ac_cv_header_ucred_h" = xyes then : printf "%s\n" "#define HAVE_UCRED_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "unistd.h" "ac_cv_header_unistd_h" "$ac_includes_default" if test "x$ac_cv_header_unistd_h" = xyes then : printf "%s\n" "#define HAVE_UNISTD_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "usersec.h" "ac_cv_header_usersec_h" "$ac_includes_default" if test "x$ac_cv_header_usersec_h" = xyes then : printf "%s\n" "#define HAVE_USERSEC_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "util.h" "ac_cv_header_util_h" "$ac_includes_default" if test "x$ac_cv_header_util_h" = xyes then : printf "%s\n" "#define HAVE_UTIL_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "utime.h" "ac_cv_header_utime_h" "$ac_includes_default" if test "x$ac_cv_header_utime_h" = xyes then : printf "%s\n" "#define HAVE_UTIME_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "utmp.h" "ac_cv_header_utmp_h" "$ac_includes_default" if test "x$ac_cv_header_utmp_h" = xyes then : printf "%s\n" "#define HAVE_UTMP_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "utmpx.h" "ac_cv_header_utmpx_h" "$ac_includes_default" if test "x$ac_cv_header_utmpx_h" = xyes then : printf "%s\n" "#define HAVE_UTMPX_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "vis.h" "ac_cv_header_vis_h" "$ac_includes_default" if test "x$ac_cv_header_vis_h" = xyes then : printf "%s\n" "#define HAVE_VIS_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "wchar.h" "ac_cv_header_wchar_h" "$ac_includes_default" if test "x$ac_cv_header_wchar_h" = xyes then : printf "%s\n" "#define HAVE_WCHAR_H 1" >>confdefs.h fi # On some platforms (eg SunOS4) sys/audit.h requires sys/[time|types|label.h] # to be included first. ac_fn_c_check_header_compile "$LINENO" "sys/audit.h" "ac_cv_header_sys_audit_h" " #ifdef HAVE_SYS_TIME_H # include #endif #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_LABEL_H # include #endif " if test "x$ac_cv_header_sys_audit_h" = xyes then : printf "%s\n" "#define HAVE_SYS_AUDIT_H 1" >>confdefs.h fi # sys/capsicum.h requires sys/types.h ac_fn_c_check_header_compile "$LINENO" "sys/capsicum.h" "ac_cv_header_sys_capsicum_h" " #ifdef HAVE_SYS_TYPES_H # include #endif " if test "x$ac_cv_header_sys_capsicum_h" = xyes then : printf "%s\n" "#define HAVE_SYS_CAPSICUM_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "capsicum_helpers.h" "ac_cv_header_capsicum_helpers_h" " #ifdef HAVE_SYS_TYPES_H # include #endif " if test "x$ac_cv_header_capsicum_helpers_h" = xyes then : printf "%s\n" "#define HAVE_CAPSICUM_HELPERS_H 1" >>confdefs.h fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for caph_cache_tzdata" >&5 printf %s "checking for caph_cache_tzdata... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { caph_cache_tzdata(); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } printf "%s\n" "#define HAVE_CAPH_CACHE_TZDATA 1" >>confdefs.h else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext # net/route.h requires sys/socket.h and sys/types.h. # sys/sysctl.h also requires sys/param.h ac_fn_c_check_header_compile "$LINENO" "net/route.h" "ac_cv_header_net_route_h" " #ifdef HAVE_SYS_TYPES_H # include #endif #include #include " if test "x$ac_cv_header_net_route_h" = xyes then : printf "%s\n" "#define HAVE_NET_ROUTE_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "sys/sysctl.h" "ac_cv_header_sys_sysctl_h" " #ifdef HAVE_SYS_TYPES_H # include #endif #include #include " if test "x$ac_cv_header_sys_sysctl_h" = xyes then : printf "%s\n" "#define HAVE_SYS_SYSCTL_H 1" >>confdefs.h fi # lastlog.h requires sys/time.h to be included first on Solaris ac_fn_c_check_header_compile "$LINENO" "lastlog.h" "ac_cv_header_lastlog_h" " #ifdef HAVE_SYS_TIME_H # include #endif " if test "x$ac_cv_header_lastlog_h" = xyes then : printf "%s\n" "#define HAVE_LASTLOG_H 1" >>confdefs.h fi # sys/ptms.h requires sys/stream.h to be included first on Solaris ac_fn_c_check_header_compile "$LINENO" "sys/ptms.h" "ac_cv_header_sys_ptms_h" " #ifdef HAVE_SYS_STREAM_H # include #endif " if test "x$ac_cv_header_sys_ptms_h" = xyes then : printf "%s\n" "#define HAVE_SYS_PTMS_H 1" >>confdefs.h fi # login_cap.h requires sys/types.h on NetBSD ac_fn_c_check_header_compile "$LINENO" "login_cap.h" "ac_cv_header_login_cap_h" " #include " if test "x$ac_cv_header_login_cap_h" = xyes then : printf "%s\n" "#define HAVE_LOGIN_CAP_H 1" >>confdefs.h fi # older BSDs need sys/param.h before sys/mount.h ac_fn_c_check_header_compile "$LINENO" "sys/mount.h" "ac_cv_header_sys_mount_h" " #include " if test "x$ac_cv_header_sys_mount_h" = xyes then : printf "%s\n" "#define HAVE_SYS_MOUNT_H 1" >>confdefs.h fi # Android requires sys/socket.h to be included before sys/un.h ac_fn_c_check_header_compile "$LINENO" "sys/un.h" "ac_cv_header_sys_un_h" " #include #include " if test "x$ac_cv_header_sys_un_h" = xyes then : printf "%s\n" "#define HAVE_SYS_UN_H 1" >>confdefs.h fi # Messages for features tested for in target-specific section SIA_MSG="no" SPC_MSG="no" SP_MSG="no" SPP_MSG="no" # Support for Solaris/Illumos privileges (this test is used by both # the --with-solaris-privs option and --with-sandbox=solaris). SOLARIS_PRIVS="no" # Check for some target-specific stuff case "$host" in *-*-aix*) # Some versions of VAC won't allow macro redefinitions at # -qlanglevel=ansi, and autoconf 2.60 sometimes insists on using that # particularly with older versions of vac or xlc. # It also throws errors about null macro arguments, but these are # not fatal. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if compiler allows macro redefinitions" >&5 printf %s "checking if compiler allows macro redefinitions... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define testmacro foo #define testmacro bar int main (void) { exit(0); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } CC="`echo $CC | sed 's/-qlanglvl\=ansi//g'`" CFLAGS="`echo $CFLAGS | sed 's/-qlanglvl\=ansi//g'`" CPPFLAGS="`echo $CPPFLAGS | sed 's/-qlanglvl\=ansi//g'`" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking how to specify blibpath for linker ($LD)" >&5 printf %s "checking how to specify blibpath for linker ($LD)... " >&6; } if (test -z "$blibpath"); then blibpath="/usr/lib:/lib" fi saved_LDFLAGS="$LDFLAGS" if test "$GCC" = "yes"; then flags="-Wl,-blibpath: -Wl,-rpath, -blibpath:" else flags="-blibpath: -Wl,-blibpath: -Wl,-rpath," fi for tryflags in $flags ;do if (test -z "$blibflags"); then LDFLAGS="$saved_LDFLAGS $tryflags$blibpath" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : blibflags=$tryflags fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext fi done if (test -z "$blibflags"); then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: not found" >&5 printf "%s\n" "not found" >&6; } as_fn_error $? "*** must be able to specify blibpath on AIX - check config.log" "$LINENO" 5 else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $blibflags" >&5 printf "%s\n" "$blibflags" >&6; } fi LDFLAGS="$saved_LDFLAGS" ac_fn_c_check_func "$LINENO" "authenticate" "ac_cv_func_authenticate" if test "x$ac_cv_func_authenticate" = xyes then : printf "%s\n" "#define WITH_AIXAUTHENTICATE 1" >>confdefs.h else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for authenticate in -ls" >&5 printf %s "checking for authenticate in -ls... " >&6; } if test ${ac_cv_lib_s_authenticate+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-ls $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char authenticate (); int main (void) { return authenticate (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_s_authenticate=yes else $as_nop ac_cv_lib_s_authenticate=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_s_authenticate" >&5 printf "%s\n" "$ac_cv_lib_s_authenticate" >&6; } if test "x$ac_cv_lib_s_authenticate" = xyes then : printf "%s\n" "#define WITH_AIXAUTHENTICATE 1" >>confdefs.h LIBS="$LIBS -ls" fi fi ac_fn_check_decl "$LINENO" "authenticate" "ac_cv_have_decl_authenticate" "#include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_authenticate" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_AUTHENTICATE $ac_have_decl" >>confdefs.h ac_fn_check_decl "$LINENO" "loginrestrictions" "ac_cv_have_decl_loginrestrictions" "#include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_loginrestrictions" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_LOGINRESTRICTIONS $ac_have_decl" >>confdefs.h ac_fn_check_decl "$LINENO" "loginsuccess" "ac_cv_have_decl_loginsuccess" "#include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_loginsuccess" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_LOGINSUCCESS $ac_have_decl" >>confdefs.h ac_fn_check_decl "$LINENO" "passwdexpired" "ac_cv_have_decl_passwdexpired" "#include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_passwdexpired" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_PASSWDEXPIRED $ac_have_decl" >>confdefs.h ac_fn_check_decl "$LINENO" "setauthdb" "ac_cv_have_decl_setauthdb" "#include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_setauthdb" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_SETAUTHDB $ac_have_decl" >>confdefs.h ac_fn_check_decl "$LINENO" "loginfailed" "ac_cv_have_decl_loginfailed" "#include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_loginfailed" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_LOGINFAILED $ac_have_decl" >>confdefs.h if test $ac_have_decl = 1 then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if loginfailed takes 4 arguments" >&5 printf %s "checking if loginfailed takes 4 arguments... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { (void)loginfailed("user","host","tty",0); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } printf "%s\n" "#define AIX_LOGINFAILED_4ARG 1" >>confdefs.h else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi ac_fn_c_check_func "$LINENO" "getgrset" "ac_cv_func_getgrset" if test "x$ac_cv_func_getgrset" = xyes then : printf "%s\n" "#define HAVE_GETGRSET 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "setauthdb" "ac_cv_func_setauthdb" if test "x$ac_cv_func_setauthdb" = xyes then : printf "%s\n" "#define HAVE_SETAUTHDB 1" >>confdefs.h fi ac_fn_check_decl "$LINENO" "F_CLOSEM" "ac_cv_have_decl_F_CLOSEM" " #include #include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_F_CLOSEM" = xyes then : printf "%s\n" "#define HAVE_FCNTL_CLOSEM 1" >>confdefs.h fi check_for_aix_broken_getaddrinfo=1 printf "%s\n" "#define SETEUID_BREAKS_SETUID 1" >>confdefs.h printf "%s\n" "#define BROKEN_SETREUID 1" >>confdefs.h printf "%s\n" "#define BROKEN_SETREGID 1" >>confdefs.h printf "%s\n" "#define DISABLE_LASTLOG 1" >>confdefs.h printf "%s\n" "#define LOGIN_NEEDS_UTMPX 1" >>confdefs.h printf "%s\n" "#define SPT_TYPE SPT_REUSEARGV" >>confdefs.h printf "%s\n" "#define SSHPAM_CHAUTHTOK_NEEDS_RUID 1" >>confdefs.h printf "%s\n" "#define PTY_ZEROREAD 1" >>confdefs.h printf "%s\n" "#define PLATFORM_SYS_DIR_UID 2" >>confdefs.h printf "%s\n" "#define BROKEN_STRNDUP 1" >>confdefs.h printf "%s\n" "#define BROKEN_STRNLEN 1" >>confdefs.h ;; *-*-android*) printf "%s\n" "#define DISABLE_UTMP 1" >>confdefs.h printf "%s\n" "#define DISABLE_WTMP 1" >>confdefs.h ;; *-*-cygwin*) LIBS="$LIBS /usr/lib/textreadmode.o" printf "%s\n" "#define HAVE_CYGWIN 1" >>confdefs.h printf "%s\n" "#define USE_PIPES 1" >>confdefs.h printf "%s\n" "#define NO_UID_RESTORATION_TEST 1" >>confdefs.h printf "%s\n" "#define DISABLE_SHADOW 1" >>confdefs.h printf "%s\n" "#define NO_X11_UNIX_SOCKETS 1" >>confdefs.h printf "%s\n" "#define DISABLE_FD_PASSING 1" >>confdefs.h printf "%s\n" "#define SSH_IOBUFSZ 65535" >>confdefs.h printf "%s\n" "#define FILESYSTEM_NO_BACKSLASH 1" >>confdefs.h # Cygwin defines optargs, optargs as declspec(dllimport) for historical # reasons which cause compile warnings, so we disable those warnings. { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $CC supports compile flag -Wno-attributes" >&5 printf %s "checking if $CC supports compile flag -Wno-attributes... " >&6; } saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $WERROR -Wno-attributes" _define_flag="" test "x$_define_flag" = "x" && _define_flag="-Wno-attributes" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include /* Trivial function to help test for -fzero-call-used-regs */ void f(int n) {} int main(int argc, char **argv) { (void)argv; /* Some math to catch -ftrapv problems in the toolchain */ int i = 123 * argc, j = 456 + argc, k = 789 - argc; float l = i * 2.1; double m = l / 0.5; long long int n = argc * 12345LL, o = 12345LL * (long long int)argc; f(0); printf("%d %d %d %f %f %lld %lld\n", i, j, k, l, m, n, o); /* * Test fallthrough behaviour. clang 10's -Wimplicit-fallthrough does * not understand comments and we don't use the "fallthrough" attribute * that it's looking for. */ switch(i){ case 0: j += i; /* FALLTHROUGH */ default: j += k; } exit(0); } _ACEOF if ac_fn_c_try_compile "$LINENO" then : if $ac_cv_path_EGREP -i "unrecognized option|warning.*ignored" conftest.err >/dev/null then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } CFLAGS="$saved_CFLAGS" else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } CFLAGS="$saved_CFLAGS $_define_flag" fi else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } CFLAGS="$saved_CFLAGS" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext } ;; *-*-dgux*) printf "%s\n" "#define IP_TOS_IS_BROKEN 1" >>confdefs.h printf "%s\n" "#define SETEUID_BREAKS_SETUID 1" >>confdefs.h printf "%s\n" "#define BROKEN_SETREUID 1" >>confdefs.h printf "%s\n" "#define BROKEN_SETREGID 1" >>confdefs.h ;; *-*-darwin*) use_pie=auto { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if we have working getaddrinfo" >&5 printf %s "checking if we have working getaddrinfo... " >&6; } if test "$cross_compiling" = yes then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: assume it is working" >&5 printf "%s\n" "assume it is working" >&6; } else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main(void) { if (NSVersionOfRunTimeLibrary("System") >= (60 << 16)) exit(0); else exit(1); } _ACEOF if ac_fn_c_try_run "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: working" >&5 printf "%s\n" "working" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: buggy" >&5 printf "%s\n" "buggy" >&6; } printf "%s\n" "#define BROKEN_GETADDRINFO 1" >>confdefs.h fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi printf "%s\n" "#define SETEUID_BREAKS_SETUID 1" >>confdefs.h printf "%s\n" "#define BROKEN_SETREUID 1" >>confdefs.h printf "%s\n" "#define BROKEN_SETREGID 1" >>confdefs.h printf "%s\n" "#define BROKEN_GLOB 1" >>confdefs.h printf "%s\n" "#define BIND_8_COMPAT 1" >>confdefs.h printf "%s\n" "#define SSH_TUN_FREEBSD 1" >>confdefs.h printf "%s\n" "#define SSH_TUN_COMPAT_AF 1" >>confdefs.h printf "%s\n" "#define SSH_TUN_PREPEND_AF 1" >>confdefs.h ac_fn_check_decl "$LINENO" "AU_IPv4" "ac_cv_have_decl_AU_IPv4" "$ac_includes_default" "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_AU_IPv4" = xyes then : else $as_nop printf "%s\n" "#define AU_IPv4 0" >>confdefs.h #include printf "%s\n" "#define LASTLOG_WRITE_PUTUTXLINE 1" >>confdefs.h fi printf "%s\n" "#define SPT_TYPE SPT_REUSEARGV" >>confdefs.h ac_fn_c_check_func "$LINENO" "sandbox_init" "ac_cv_func_sandbox_init" if test "x$ac_cv_func_sandbox_init" = xyes then : printf "%s\n" "#define HAVE_SANDBOX_INIT 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "sandbox.h" "ac_cv_header_sandbox_h" "$ac_includes_default" if test "x$ac_cv_header_sandbox_h" = xyes then : printf "%s\n" "#define HAVE_SANDBOX_H 1" >>confdefs.h fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for sandbox_apply in -lsandbox" >&5 printf %s "checking for sandbox_apply in -lsandbox... " >&6; } if test ${ac_cv_lib_sandbox_sandbox_apply+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lsandbox $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char sandbox_apply (); int main (void) { return sandbox_apply (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_sandbox_sandbox_apply=yes else $as_nop ac_cv_lib_sandbox_sandbox_apply=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_sandbox_sandbox_apply" >&5 printf "%s\n" "$ac_cv_lib_sandbox_sandbox_apply" >&6; } if test "x$ac_cv_lib_sandbox_sandbox_apply" = xyes then : SSHDLIBS="$SSHDLIBS -lsandbox" fi # proc_pidinfo()-based closefrom() replacement. ac_fn_c_check_header_compile "$LINENO" "libproc.h" "ac_cv_header_libproc_h" "$ac_includes_default" if test "x$ac_cv_header_libproc_h" = xyes then : printf "%s\n" "#define HAVE_LIBPROC_H 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "proc_pidinfo" "ac_cv_func_proc_pidinfo" if test "x$ac_cv_func_proc_pidinfo" = xyes then : printf "%s\n" "#define HAVE_PROC_PIDINFO 1" >>confdefs.h fi # poll(2) is broken for character-special devices (at least). # cf. Apple bug 3710161 (not public, but searchable) printf "%s\n" "#define BROKEN_POLL 1" >>confdefs.h ;; *-*-dragonfly*) SSHDLIBS="$SSHDLIBS" TEST_MALLOC_OPTIONS="AFGJPRX" ;; *-*-haiku*) LIBS="$LIBS -lbsd " CFLAGS="$CFLAGS -D_BSD_SOURCE" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for socket in -lnetwork" >&5 printf %s "checking for socket in -lnetwork... " >&6; } if test ${ac_cv_lib_network_socket+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lnetwork $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char socket (); int main (void) { return socket (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_network_socket=yes else $as_nop ac_cv_lib_network_socket=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_network_socket" >&5 printf "%s\n" "$ac_cv_lib_network_socket" >&6; } if test "x$ac_cv_lib_network_socket" = xyes then : printf "%s\n" "#define HAVE_LIBNETWORK 1" >>confdefs.h LIBS="-lnetwork $LIBS" fi printf "%s\n" "#define HAVE_U_INT64_T 1" >>confdefs.h printf "%s\n" "#define DISABLE_UTMPX 1" >>confdefs.h MANTYPE=man ;; *-*-hpux*) # first we define all of the options common to all HP-UX releases CPPFLAGS="$CPPFLAGS -D_HPUX_SOURCE -D_XOPEN_SOURCE -D_XOPEN_SOURCE_EXTENDED=1" IPADDR_IN_DISPLAY=yes printf "%s\n" "#define USE_PIPES 1" >>confdefs.h printf "%s\n" "#define LOGIN_NEEDS_UTMPX 1" >>confdefs.h printf "%s\n" "#define LOCKED_PASSWD_STRING \"*\"" >>confdefs.h printf "%s\n" "#define SPT_TYPE SPT_PSTAT" >>confdefs.h printf "%s\n" "#define PLATFORM_SYS_DIR_UID 2" >>confdefs.h maildir="/var/mail" LIBS="$LIBS -lsec" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for t_error in -lxnet" >&5 printf %s "checking for t_error in -lxnet... " >&6; } if test ${ac_cv_lib_xnet_t_error+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lxnet $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char t_error (); int main (void) { return t_error (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_xnet_t_error=yes else $as_nop ac_cv_lib_xnet_t_error=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_xnet_t_error" >&5 printf "%s\n" "$ac_cv_lib_xnet_t_error" >&6; } if test "x$ac_cv_lib_xnet_t_error" = xyes then : printf "%s\n" "#define HAVE_LIBXNET 1" >>confdefs.h LIBS="-lxnet $LIBS" else $as_nop as_fn_error $? "*** -lxnet needed on HP-UX - check config.log ***" "$LINENO" 5 fi # next, we define all of the options specific to major releases case "$host" in *-*-hpux10*) if test -z "$GCC"; then CFLAGS="$CFLAGS -Ae" fi printf "%s\n" "#define BROKEN_GETLINE 1" >>confdefs.h ;; *-*-hpux11*) printf "%s\n" "#define PAM_SUN_CODEBASE 1" >>confdefs.h printf "%s\n" "#define DISABLE_UTMP 1" >>confdefs.h printf "%s\n" "#define USE_BTMP 1" >>confdefs.h check_for_hpux_broken_getaddrinfo=1 check_for_conflicting_getspnam=1 ;; esac # lastly, we define options specific to minor releases case "$host" in *-*-hpux10.26) printf "%s\n" "#define HAVE_SECUREWARE 1" >>confdefs.h disable_ptmx_check=yes LIBS="$LIBS -lsecpw" ;; esac ;; *-*-irix5*) PATH="$PATH:/usr/etc" printf "%s\n" "#define BROKEN_INET_NTOA 1" >>confdefs.h printf "%s\n" "#define SETEUID_BREAKS_SETUID 1" >>confdefs.h printf "%s\n" "#define BROKEN_SETREUID 1" >>confdefs.h printf "%s\n" "#define BROKEN_SETREGID 1" >>confdefs.h printf "%s\n" "#define WITH_ABBREV_NO_TTY 1" >>confdefs.h printf "%s\n" "#define LOCKED_PASSWD_STRING \"*LK*\"" >>confdefs.h ;; *-*-irix6*) PATH="$PATH:/usr/etc" printf "%s\n" "#define WITH_IRIX_ARRAY 1" >>confdefs.h printf "%s\n" "#define WITH_IRIX_PROJECT 1" >>confdefs.h printf "%s\n" "#define WITH_IRIX_AUDIT 1" >>confdefs.h ac_fn_c_check_func "$LINENO" "jlimit_startjob" "ac_cv_func_jlimit_startjob" if test "x$ac_cv_func_jlimit_startjob" = xyes then : printf "%s\n" "#define WITH_IRIX_JOBS 1" >>confdefs.h fi printf "%s\n" "#define BROKEN_INET_NTOA 1" >>confdefs.h printf "%s\n" "#define SETEUID_BREAKS_SETUID 1" >>confdefs.h printf "%s\n" "#define BROKEN_SETREUID 1" >>confdefs.h printf "%s\n" "#define BROKEN_SETREGID 1" >>confdefs.h printf "%s\n" "#define BROKEN_UPDWTMPX 1" >>confdefs.h printf "%s\n" "#define WITH_ABBREV_NO_TTY 1" >>confdefs.h printf "%s\n" "#define LOCKED_PASSWD_STRING \"*LK*\"" >>confdefs.h ;; *-*-k*bsd*-gnu | *-*-kopensolaris*-gnu) printf "%s\n" "#define PAM_TTY_KLUDGE 1" >>confdefs.h printf "%s\n" "#define LOCKED_PASSWD_PREFIX \"!\"" >>confdefs.h printf "%s\n" "#define SPT_TYPE SPT_REUSEARGV" >>confdefs.h printf "%s\n" "#define _PATH_BTMP \"/var/log/btmp\"" >>confdefs.h printf "%s\n" "#define USE_BTMP 1" >>confdefs.h ;; *-*-linux*) no_dev_ptmx=1 use_pie=auto check_for_openpty_ctty_bug=1 CPPFLAGS="$CPPFLAGS -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_DEFAULT_SOURCE -D_GNU_SOURCE" printf "%s\n" "#define BROKEN_CLOSEFROM 1" >>confdefs.h printf "%s\n" "#define PAM_TTY_KLUDGE 1" >>confdefs.h printf "%s\n" "#define LOCKED_PASSWD_PREFIX \"!\"" >>confdefs.h printf "%s\n" "#define SPT_TYPE SPT_REUSEARGV" >>confdefs.h printf "%s\n" "#define LINK_OPNOTSUPP_ERRNO EPERM" >>confdefs.h printf "%s\n" "#define _PATH_BTMP \"/var/log/btmp\"" >>confdefs.h printf "%s\n" "#define USE_BTMP 1" >>confdefs.h printf "%s\n" "#define LINUX_OOM_ADJUST 1" >>confdefs.h inet6_default_4in6=yes case `uname -r` in 1.*|2.0.*) printf "%s\n" "#define BROKEN_CMSG_TYPE 1" >>confdefs.h ;; esac # tun(4) forwarding compat code ac_fn_c_check_header_compile "$LINENO" "linux/if_tun.h" "ac_cv_header_linux_if_tun_h" "$ac_includes_default" if test "x$ac_cv_header_linux_if_tun_h" = xyes then : printf "%s\n" "#define HAVE_LINUX_IF_TUN_H 1" >>confdefs.h fi if test "x$ac_cv_header_linux_if_tun_h" = "xyes" ; then printf "%s\n" "#define SSH_TUN_LINUX 1" >>confdefs.h printf "%s\n" "#define SSH_TUN_COMPAT_AF 1" >>confdefs.h printf "%s\n" "#define SSH_TUN_PREPEND_AF 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "linux/if.h" "ac_cv_header_linux_if_h" " #ifdef HAVE_SYS_TYPES_H # include #endif " if test "x$ac_cv_header_linux_if_h" = xyes then : printf "%s\n" "#define SYS_RDOMAIN_LINUX 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "linux/seccomp.h" "ac_cv_header_linux_seccomp_h" "#include " if test "x$ac_cv_header_linux_seccomp_h" = xyes then : printf "%s\n" "#define HAVE_LINUX_SECCOMP_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "linux/filter.h" "ac_cv_header_linux_filter_h" "#include " if test "x$ac_cv_header_linux_filter_h" = xyes then : printf "%s\n" "#define HAVE_LINUX_FILTER_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "linux/audit.h" "ac_cv_header_linux_audit_h" "#include " if test "x$ac_cv_header_linux_audit_h" = xyes then : printf "%s\n" "#define HAVE_LINUX_AUDIT_H 1" >>confdefs.h fi # Obtain MIPS ABI case "$host" in mips*) cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #if _MIPS_SIM != _ABIO32 #error #endif int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : mips_abi="o32" else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #if _MIPS_SIM != _ABIN32 #error #endif int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : mips_abi="n32" else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #if _MIPS_SIM != _ABI64 #error #endif int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : mips_abi="n64" else $as_nop as_fn_error $? "unknown MIPS ABI" "$LINENO" 5 fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; esac { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for seccomp architecture" >&5 printf %s "checking for seccomp architecture... " >&6; } seccomp_audit_arch= case "$host" in x86_64-*) seccomp_audit_arch=AUDIT_ARCH_X86_64 ;; i*86-*) seccomp_audit_arch=AUDIT_ARCH_I386 ;; arm*-*) seccomp_audit_arch=AUDIT_ARCH_ARM ;; aarch64*-*) seccomp_audit_arch=AUDIT_ARCH_AARCH64 ;; s390x-*) seccomp_audit_arch=AUDIT_ARCH_S390X ;; s390-*) seccomp_audit_arch=AUDIT_ARCH_S390 ;; powerpc-*) seccomp_audit_arch=AUDIT_ARCH_PPC ;; powerpc64-*) seccomp_audit_arch=AUDIT_ARCH_PPC64 ;; powerpc64le-*) seccomp_audit_arch=AUDIT_ARCH_PPC64LE ;; mips-*) seccomp_audit_arch=AUDIT_ARCH_MIPS ;; mipsel-*) seccomp_audit_arch=AUDIT_ARCH_MIPSEL ;; mips64-*) case "$mips_abi" in "n32") seccomp_audit_arch=AUDIT_ARCH_MIPS64N32 ;; "n64") seccomp_audit_arch=AUDIT_ARCH_MIPS64 ;; esac ;; mips64el-*) case "$mips_abi" in "n32") seccomp_audit_arch=AUDIT_ARCH_MIPSEL64N32 ;; "n64") seccomp_audit_arch=AUDIT_ARCH_MIPSEL64 ;; esac ;; riscv64-*) seccomp_audit_arch=AUDIT_ARCH_RISCV64 ;; esac if test "x$seccomp_audit_arch" != "x" ; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: \"$seccomp_audit_arch\"" >&5 printf "%s\n" "\"$seccomp_audit_arch\"" >&6; } printf "%s\n" "#define SECCOMP_AUDIT_ARCH $seccomp_audit_arch" >>confdefs.h else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: architecture not supported" >&5 printf "%s\n" "architecture not supported" >&6; } fi ;; *-*-minix) printf "%s\n" "#define SETEUID_BREAKS_SETUID 1" >>confdefs.h # poll(2) seems to choke on /dev/null; "Bad file descriptor" printf "%s\n" "#define BROKEN_POLL 1" >>confdefs.h ;; mips-sony-bsd|mips-sony-newsos4) printf "%s\n" "#define NEED_SETPGRP 1" >>confdefs.h SONY=1 ;; *-*-netbsd*) if test "x$withval" != "xno" ; then rpath_opt="-R" fi CPPFLAGS="$CPPFLAGS -D_OPENBSD_SOURCE" printf "%s\n" "#define SSH_TUN_FREEBSD 1" >>confdefs.h ac_fn_c_check_header_compile "$LINENO" "net/if_tap.h" "ac_cv_header_net_if_tap_h" "$ac_includes_default" if test "x$ac_cv_header_net_if_tap_h" = xyes then : else $as_nop printf "%s\n" "#define SSH_TUN_NO_L2 1" >>confdefs.h fi printf "%s\n" "#define SSH_TUN_PREPEND_AF 1" >>confdefs.h TEST_MALLOC_OPTIONS="AJRX" printf "%s\n" "#define BROKEN_READ_COMPARISON 1" >>confdefs.h ;; *-*-freebsd*) printf "%s\n" "#define LOCKED_PASSWD_PREFIX \"*LOCKED*\"" >>confdefs.h printf "%s\n" "#define SSH_TUN_FREEBSD 1" >>confdefs.h ac_fn_c_check_header_compile "$LINENO" "net/if_tap.h" "ac_cv_header_net_if_tap_h" "$ac_includes_default" if test "x$ac_cv_header_net_if_tap_h" = xyes then : else $as_nop printf "%s\n" "#define SSH_TUN_NO_L2 1" >>confdefs.h fi printf "%s\n" "#define BROKEN_GLOB 1" >>confdefs.h TEST_MALLOC_OPTIONS="AJRX" # Preauth crypto occasionally uses file descriptors for crypto offload # and will crash if they cannot be opened. printf "%s\n" "#define SANDBOX_SKIP_RLIMIT_NOFILE 1" >>confdefs.h case "$host" in *-*-freebsd9.*|*-*-freebsd10.*) # Capsicum on 9 and 10 do not allow ppoll() so don't auto-enable. disable_capsicum=yes esac ;; *-*-bsdi*) printf "%s\n" "#define SETEUID_BREAKS_SETUID 1" >>confdefs.h printf "%s\n" "#define BROKEN_SETREUID 1" >>confdefs.h printf "%s\n" "#define BROKEN_SETREGID 1" >>confdefs.h ;; *-next-*) conf_lastlog_location="/usr/adm/lastlog" conf_utmp_location=/etc/utmp conf_wtmp_location=/usr/adm/wtmp maildir=/usr/spool/mail printf "%s\n" "#define HAVE_NEXT 1" >>confdefs.h printf "%s\n" "#define USE_PIPES 1" >>confdefs.h printf "%s\n" "#define BROKEN_SAVED_UIDS 1" >>confdefs.h ;; *-*-openbsd*) use_pie=auto printf "%s\n" "#define HAVE_ATTRIBUTE__SENTINEL__ 1" >>confdefs.h printf "%s\n" "#define HAVE_ATTRIBUTE__BOUNDED__ 1" >>confdefs.h printf "%s\n" "#define SSH_TUN_OPENBSD 1" >>confdefs.h printf "%s\n" "#define SYSLOG_R_SAFE_IN_SIGHAND 1" >>confdefs.h TEST_MALLOC_OPTIONS="AFGJPRX" ;; *-*-solaris*) if test "x$withval" != "xno" ; then rpath_opt="-R" fi printf "%s\n" "#define PAM_SUN_CODEBASE 1" >>confdefs.h printf "%s\n" "#define LOGIN_NEEDS_UTMPX 1" >>confdefs.h printf "%s\n" "#define PAM_TTY_KLUDGE 1" >>confdefs.h printf "%s\n" "#define SSHPAM_CHAUTHTOK_NEEDS_RUID 1" >>confdefs.h printf "%s\n" "#define LOCKED_PASSWD_STRING \"*LK*\"" >>confdefs.h # Pushing STREAMS modules will cause sshd to acquire a controlling tty. printf "%s\n" "#define SSHD_ACQUIRES_CTTY 1" >>confdefs.h printf "%s\n" "#define PASSWD_NEEDS_USERNAME 1" >>confdefs.h printf "%s\n" "#define BROKEN_TCGETATTR_ICANON 1" >>confdefs.h external_path_file=/etc/default/login # hardwire lastlog location (can't detect it on some versions) conf_lastlog_location="/var/adm/lastlog" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for obsolete utmp and wtmp in solaris2.x" >&5 printf %s "checking for obsolete utmp and wtmp in solaris2.x... " >&6; } sol2ver=`echo "$host"| sed -e 's/.*[0-9]\.//'` if test "$sol2ver" -ge 8; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } printf "%s\n" "#define DISABLE_UTMP 1" >>confdefs.h printf "%s\n" "#define DISABLE_WTMP 1" >>confdefs.h else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi ac_fn_c_check_func "$LINENO" "setpflags" "ac_cv_func_setpflags" if test "x$ac_cv_func_setpflags" = xyes then : printf "%s\n" "#define HAVE_SETPFLAGS 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "setppriv" "ac_cv_func_setppriv" if test "x$ac_cv_func_setppriv" = xyes then : printf "%s\n" "#define HAVE_SETPPRIV 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "priv_basicset" "ac_cv_func_priv_basicset" if test "x$ac_cv_func_priv_basicset" = xyes then : printf "%s\n" "#define HAVE_PRIV_BASICSET 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "priv.h" "ac_cv_header_priv_h" "$ac_includes_default" if test "x$ac_cv_header_priv_h" = xyes then : printf "%s\n" "#define HAVE_PRIV_H 1" >>confdefs.h fi # Check whether --with-solaris-contracts was given. if test ${with_solaris_contracts+y} then : withval=$with_solaris_contracts; { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for ct_tmpl_activate in -lcontract" >&5 printf %s "checking for ct_tmpl_activate in -lcontract... " >&6; } if test ${ac_cv_lib_contract_ct_tmpl_activate+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lcontract $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char ct_tmpl_activate (); int main (void) { return ct_tmpl_activate (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_contract_ct_tmpl_activate=yes else $as_nop ac_cv_lib_contract_ct_tmpl_activate=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_contract_ct_tmpl_activate" >&5 printf "%s\n" "$ac_cv_lib_contract_ct_tmpl_activate" >&6; } if test "x$ac_cv_lib_contract_ct_tmpl_activate" = xyes then : printf "%s\n" "#define USE_SOLARIS_PROCESS_CONTRACTS 1" >>confdefs.h LIBS="$LIBS -lcontract" SPC_MSG="yes" fi fi # Check whether --with-solaris-projects was given. if test ${with_solaris_projects+y} then : withval=$with_solaris_projects; { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for setproject in -lproject" >&5 printf %s "checking for setproject in -lproject... " >&6; } if test ${ac_cv_lib_project_setproject+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lproject $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char setproject (); int main (void) { return setproject (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_project_setproject=yes else $as_nop ac_cv_lib_project_setproject=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_project_setproject" >&5 printf "%s\n" "$ac_cv_lib_project_setproject" >&6; } if test "x$ac_cv_lib_project_setproject" = xyes then : printf "%s\n" "#define USE_SOLARIS_PROJECTS 1" >>confdefs.h LIBS="$LIBS -lproject" SP_MSG="yes" fi fi # Check whether --with-solaris-privs was given. if test ${with_solaris_privs+y} then : withval=$with_solaris_privs; { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for Solaris/Illumos privilege support" >&5 printf %s "checking for Solaris/Illumos privilege support... " >&6; } if test "x$ac_cv_func_setppriv" = "xyes" -a \ "x$ac_cv_header_priv_h" = "xyes" ; then SOLARIS_PRIVS=yes { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: found" >&5 printf "%s\n" "found" >&6; } printf "%s\n" "#define NO_UID_RESTORATION_TEST 1" >>confdefs.h printf "%s\n" "#define USE_SOLARIS_PRIVS 1" >>confdefs.h SPP_MSG="yes" else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: not found" >&5 printf "%s\n" "not found" >&6; } as_fn_error $? "*** must have support for Solaris privileges to use --with-solaris-privs" "$LINENO" 5 fi fi TEST_SHELL=$SHELL # let configure find us a capable shell ;; *-*-sunos4*) CPPFLAGS="$CPPFLAGS -DSUNOS4" ac_fn_c_check_func "$LINENO" "getpwanam" "ac_cv_func_getpwanam" if test "x$ac_cv_func_getpwanam" = xyes then : printf "%s\n" "#define HAVE_GETPWANAM 1" >>confdefs.h fi printf "%s\n" "#define PAM_SUN_CODEBASE 1" >>confdefs.h conf_utmp_location=/etc/utmp conf_wtmp_location=/var/adm/wtmp conf_lastlog_location=/var/adm/lastlog printf "%s\n" "#define USE_PIPES 1" >>confdefs.h printf "%s\n" "#define DISABLE_UTMPX 1" >>confdefs.h ;; *-ncr-sysv*) LIBS="$LIBS -lc89" printf "%s\n" "#define USE_PIPES 1" >>confdefs.h printf "%s\n" "#define SSHD_ACQUIRES_CTTY 1" >>confdefs.h printf "%s\n" "#define SETEUID_BREAKS_SETUID 1" >>confdefs.h printf "%s\n" "#define BROKEN_SETREUID 1" >>confdefs.h printf "%s\n" "#define BROKEN_SETREGID 1" >>confdefs.h ;; *-sni-sysv*) # /usr/ucblib MUST NOT be searched on ReliantUNIX { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for dlsym in -ldl" >&5 printf %s "checking for dlsym in -ldl... " >&6; } if test ${ac_cv_lib_dl_dlsym+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-ldl $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char dlsym (); int main (void) { return dlsym (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_dl_dlsym=yes else $as_nop ac_cv_lib_dl_dlsym=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlsym" >&5 printf "%s\n" "$ac_cv_lib_dl_dlsym" >&6; } if test "x$ac_cv_lib_dl_dlsym" = xyes then : printf "%s\n" "#define HAVE_LIBDL 1" >>confdefs.h LIBS="-ldl $LIBS" fi # -lresolv needs to be at the end of LIBS or DNS lookups break { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for res_query in -lresolv" >&5 printf %s "checking for res_query in -lresolv... " >&6; } if test ${ac_cv_lib_resolv_res_query+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lresolv $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char res_query (); int main (void) { return res_query (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_resolv_res_query=yes else $as_nop ac_cv_lib_resolv_res_query=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_resolv_res_query" >&5 printf "%s\n" "$ac_cv_lib_resolv_res_query" >&6; } if test "x$ac_cv_lib_resolv_res_query" = xyes then : LIBS="$LIBS -lresolv" fi IPADDR_IN_DISPLAY=yes printf "%s\n" "#define USE_PIPES 1" >>confdefs.h printf "%s\n" "#define IP_TOS_IS_BROKEN 1" >>confdefs.h printf "%s\n" "#define SETEUID_BREAKS_SETUID 1" >>confdefs.h printf "%s\n" "#define BROKEN_SETREUID 1" >>confdefs.h printf "%s\n" "#define BROKEN_SETREGID 1" >>confdefs.h printf "%s\n" "#define SSHD_ACQUIRES_CTTY 1" >>confdefs.h external_path_file=/etc/default/login # /usr/ucblib/libucb.a no longer needed on ReliantUNIX # Attention: always take care to bind libsocket and libnsl before libc, # otherwise you will find lots of "SIOCGPGRP errno 22" on syslog ;; # UnixWare 1.x, UnixWare 2.x, and others based on code from Univel. *-*-sysv4.2*) printf "%s\n" "#define USE_PIPES 1" >>confdefs.h printf "%s\n" "#define SETEUID_BREAKS_SETUID 1" >>confdefs.h printf "%s\n" "#define BROKEN_SETREUID 1" >>confdefs.h printf "%s\n" "#define BROKEN_SETREGID 1" >>confdefs.h printf "%s\n" "#define PASSWD_NEEDS_USERNAME 1" >>confdefs.h printf "%s\n" "#define LOCKED_PASSWD_STRING \"*LK*\"" >>confdefs.h TEST_SHELL=$SHELL # let configure find us a capable shell ;; # UnixWare 7.x, OpenUNIX 8 *-*-sysv5*) CPPFLAGS="$CPPFLAGS -Dvsnprintf=_xvsnprintf -Dsnprintf=_xsnprintf" printf "%s\n" "#define UNIXWARE_LONG_PASSWORDS 1" >>confdefs.h printf "%s\n" "#define USE_PIPES 1" >>confdefs.h printf "%s\n" "#define SETEUID_BREAKS_SETUID 1" >>confdefs.h printf "%s\n" "#define BROKEN_GETADDRINFO 1" >>confdefs.h printf "%s\n" "#define BROKEN_SETREUID 1" >>confdefs.h printf "%s\n" "#define BROKEN_SETREGID 1" >>confdefs.h printf "%s\n" "#define PASSWD_NEEDS_USERNAME 1" >>confdefs.h printf "%s\n" "#define BROKEN_TCGETATTR_ICANON 1" >>confdefs.h TEST_SHELL=$SHELL # let configure find us a capable shell case "$host" in *-*-sysv5SCO_SV*) # SCO OpenServer 6.x maildir=/var/spool/mail printf "%s\n" "#define BROKEN_UPDWTMPX 1" >>confdefs.h { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for getluid in -lprot" >&5 printf %s "checking for getluid in -lprot... " >&6; } if test ${ac_cv_lib_prot_getluid+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lprot $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char getluid (); int main (void) { return getluid (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_prot_getluid=yes else $as_nop ac_cv_lib_prot_getluid=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_prot_getluid" >&5 printf "%s\n" "$ac_cv_lib_prot_getluid" >&6; } if test "x$ac_cv_lib_prot_getluid" = xyes then : LIBS="$LIBS -lprot" ac_fn_c_check_func "$LINENO" "getluid" "ac_cv_func_getluid" if test "x$ac_cv_func_getluid" = xyes then : printf "%s\n" "#define HAVE_GETLUID 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "setluid" "ac_cv_func_setluid" if test "x$ac_cv_func_setluid" = xyes then : printf "%s\n" "#define HAVE_SETLUID 1" >>confdefs.h fi fi ;; *) printf "%s\n" "#define LOCKED_PASSWD_STRING \"*LK*\"" >>confdefs.h ;; esac ;; *-*-sysv*) ;; # SCO UNIX and OEM versions of SCO UNIX *-*-sco3.2v4*) as_fn_error $? "\"This Platform is no longer supported.\"" "$LINENO" 5 ;; # SCO OpenServer 5.x *-*-sco3.2v5*) if test -z "$GCC"; then CFLAGS="$CFLAGS -belf" fi LIBS="$LIBS -lprot -lx -ltinfo -lm" no_dev_ptmx=1 printf "%s\n" "#define USE_PIPES 1" >>confdefs.h printf "%s\n" "#define HAVE_SECUREWARE 1" >>confdefs.h printf "%s\n" "#define DISABLE_SHADOW 1" >>confdefs.h printf "%s\n" "#define DISABLE_FD_PASSING 1" >>confdefs.h printf "%s\n" "#define SETEUID_BREAKS_SETUID 1" >>confdefs.h printf "%s\n" "#define BROKEN_GETADDRINFO 1" >>confdefs.h printf "%s\n" "#define BROKEN_SETREUID 1" >>confdefs.h printf "%s\n" "#define BROKEN_SETREGID 1" >>confdefs.h printf "%s\n" "#define WITH_ABBREV_NO_TTY 1" >>confdefs.h printf "%s\n" "#define BROKEN_UPDWTMPX 1" >>confdefs.h printf "%s\n" "#define PASSWD_NEEDS_USERNAME 1" >>confdefs.h ac_fn_c_check_func "$LINENO" "getluid" "ac_cv_func_getluid" if test "x$ac_cv_func_getluid" = xyes then : printf "%s\n" "#define HAVE_GETLUID 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "setluid" "ac_cv_func_setluid" if test "x$ac_cv_func_setluid" = xyes then : printf "%s\n" "#define HAVE_SETLUID 1" >>confdefs.h fi MANTYPE=man TEST_SHELL=$SHELL # let configure find us a capable shell SKIP_DISABLE_LASTLOG_DEFINE=yes ;; *-dec-osf*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for Digital Unix SIA" >&5 printf %s "checking for Digital Unix SIA... " >&6; } no_osfsia="" # Check whether --with-osfsia was given. if test ${with_osfsia+y} then : withval=$with_osfsia; if test "x$withval" = "xno" ; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: disabled" >&5 printf "%s\n" "disabled" >&6; } no_osfsia=1 fi fi if test -z "$no_osfsia" ; then if test -f /etc/sia/matrix.conf; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } printf "%s\n" "#define HAVE_OSF_SIA 1" >>confdefs.h printf "%s\n" "#define DISABLE_LOGIN 1" >>confdefs.h printf "%s\n" "#define DISABLE_FD_PASSING 1" >>confdefs.h LIBS="$LIBS -lsecurity -ldb -lm -laud" SIA_MSG="yes" else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } printf "%s\n" "#define LOCKED_PASSWD_SUBSTR \"Nologin\"" >>confdefs.h fi fi printf "%s\n" "#define BROKEN_GETADDRINFO 1" >>confdefs.h printf "%s\n" "#define SETEUID_BREAKS_SETUID 1" >>confdefs.h printf "%s\n" "#define BROKEN_SETREUID 1" >>confdefs.h printf "%s\n" "#define BROKEN_SETREGID 1" >>confdefs.h printf "%s\n" "#define BROKEN_READV_COMPARISON 1" >>confdefs.h ;; *-*-nto-qnx*) printf "%s\n" "#define USE_PIPES 1" >>confdefs.h printf "%s\n" "#define NO_X11_UNIX_SOCKETS 1" >>confdefs.h printf "%s\n" "#define DISABLE_LASTLOG 1" >>confdefs.h printf "%s\n" "#define SSHD_ACQUIRES_CTTY 1" >>confdefs.h printf "%s\n" "#define BROKEN_SHADOW_EXPIRE 1" >>confdefs.h enable_etc_default_login=no # has incompatible /etc/default/login case "$host" in *-*-nto-qnx6*) printf "%s\n" "#define DISABLE_FD_PASSING 1" >>confdefs.h ;; esac ;; *-*-ultrix*) printf "%s\n" "#define BROKEN_GETGROUPS 1" >>confdefs.h printf "%s\n" "#define NEED_SETPGRP 1" >>confdefs.h printf "%s\n" "#define HAVE_SYS_SYSLOG_H 1" >>confdefs.h printf "%s\n" "#define DISABLE_UTMPX 1" >>confdefs.h # DISABLE_FD_PASSING so that we call setpgrp as root, otherwise we # don't get a controlling tty. printf "%s\n" "#define DISABLE_FD_PASSING 1" >>confdefs.h # On Ultrix some headers are not protected against multiple includes, # so we create wrappers and put it where the compiler will find it. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: creating compat wrappers for headers" >&5 printf "%s\n" "$as_me: WARNING: creating compat wrappers for headers" >&2;} mkdir -p netinet for header in netinet/ip.h netdb.h resolv.h; do name=`echo $header | tr 'a-z/.' 'A-Z__'` cat >$header <>confdefs.h ;; esac { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking compiler and flags for sanity" >&5 printf %s "checking compiler and flags for sanity... " >&6; } if test "$cross_compiling" = yes then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cross compiling: not checking compiler sanity" >&5 printf "%s\n" "$as_me: WARNING: cross compiling: not checking compiler sanity" >&2;} else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { exit(0); ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } as_fn_error $? "*** compiler cannot create working executables, check config.log ***" "$LINENO" 5 fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi # Checks for libraries. ac_fn_c_check_func "$LINENO" "setsockopt" "ac_cv_func_setsockopt" if test "x$ac_cv_func_setsockopt" = xyes then : else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for setsockopt in -lsocket" >&5 printf %s "checking for setsockopt in -lsocket... " >&6; } if test ${ac_cv_lib_socket_setsockopt+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lsocket $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char setsockopt (); int main (void) { return setsockopt (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_socket_setsockopt=yes else $as_nop ac_cv_lib_socket_setsockopt=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_socket_setsockopt" >&5 printf "%s\n" "$ac_cv_lib_socket_setsockopt" >&6; } if test "x$ac_cv_lib_socket_setsockopt" = xyes then : printf "%s\n" "#define HAVE_LIBSOCKET 1" >>confdefs.h LIBS="-lsocket $LIBS" fi fi for ac_func in dirname do : ac_fn_c_check_func "$LINENO" "dirname" "ac_cv_func_dirname" if test "x$ac_cv_func_dirname" = xyes then : printf "%s\n" "#define HAVE_DIRNAME 1" >>confdefs.h ac_fn_c_check_header_compile "$LINENO" "libgen.h" "ac_cv_header_libgen_h" "$ac_includes_default" if test "x$ac_cv_header_libgen_h" = xyes then : printf "%s\n" "#define HAVE_LIBGEN_H 1" >>confdefs.h fi else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for dirname in -lgen" >&5 printf %s "checking for dirname in -lgen... " >&6; } if test ${ac_cv_lib_gen_dirname+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lgen $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char dirname (); int main (void) { return dirname (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_gen_dirname=yes else $as_nop ac_cv_lib_gen_dirname=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_gen_dirname" >&5 printf "%s\n" "$ac_cv_lib_gen_dirname" >&6; } if test "x$ac_cv_lib_gen_dirname" = xyes then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for broken dirname" >&5 printf %s "checking for broken dirname... " >&6; } if test ${ac_cv_have_broken_dirname+y} then : printf %s "(cached) " >&6 else $as_nop save_LIBS="$LIBS" LIBS="$LIBS -lgen" if test "$cross_compiling" = yes then : ac_cv_have_broken_dirname="no" else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include int main(int argc, char **argv) { char *s, buf[32]; strncpy(buf,"/etc", 32); s = dirname(buf); if (!s || strncmp(s, "/", 32) != 0) { exit(1); } else { exit(0); } } _ACEOF if ac_fn_c_try_run "$LINENO" then : ac_cv_have_broken_dirname="no" else $as_nop ac_cv_have_broken_dirname="yes" fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi LIBS="$save_LIBS" fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_broken_dirname" >&5 printf "%s\n" "$ac_cv_have_broken_dirname" >&6; } if test "x$ac_cv_have_broken_dirname" = "xno" ; then LIBS="$LIBS -lgen" printf "%s\n" "#define HAVE_DIRNAME 1" >>confdefs.h ac_fn_c_check_header_compile "$LINENO" "libgen.h" "ac_cv_header_libgen_h" "$ac_includes_default" if test "x$ac_cv_header_libgen_h" = xyes then : printf "%s\n" "#define HAVE_LIBGEN_H 1" >>confdefs.h fi fi fi fi done ac_fn_c_check_func "$LINENO" "getspnam" "ac_cv_func_getspnam" if test "x$ac_cv_func_getspnam" = xyes then : else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for getspnam in -lgen" >&5 printf %s "checking for getspnam in -lgen... " >&6; } if test ${ac_cv_lib_gen_getspnam+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lgen $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char getspnam (); int main (void) { return getspnam (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_gen_getspnam=yes else $as_nop ac_cv_lib_gen_getspnam=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_gen_getspnam" >&5 printf "%s\n" "$ac_cv_lib_gen_getspnam" >&6; } if test "x$ac_cv_lib_gen_getspnam" = xyes then : LIBS="$LIBS -lgen" fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing basename" >&5 printf %s "checking for library containing basename... " >&6; } if test ${ac_cv_search_basename+y} then : printf %s "(cached) " >&6 else $as_nop ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char basename (); int main (void) { return basename (); ; return 0; } _ACEOF for ac_lib in '' gen do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO" then : ac_cv_search_basename=$ac_res fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext if test ${ac_cv_search_basename+y} then : break fi done if test ${ac_cv_search_basename+y} then : else $as_nop ac_cv_search_basename=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_basename" >&5 printf "%s\n" "$ac_cv_search_basename" >&6; } ac_res=$ac_cv_search_basename if test "$ac_res" != no then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" printf "%s\n" "#define HAVE_BASENAME 1" >>confdefs.h fi zlib=yes # Check whether --with-zlib was given. if test ${with_zlib+y} then : withval=$with_zlib; if test "x$withval" = "xno" ; then zlib=no elif test "x$withval" != "xyes"; then if test -d "$withval/lib"; then if test -n "${rpath_opt}"; then LDFLAGS="-L${withval}/lib ${rpath_opt}${withval}/lib ${LDFLAGS}" else LDFLAGS="-L${withval}/lib ${LDFLAGS}" fi else if test -n "${rpath_opt}"; then LDFLAGS="-L${withval} ${rpath_opt}${withval} ${LDFLAGS}" else LDFLAGS="-L${withval} ${LDFLAGS}" fi fi if test -d "$withval/include"; then CPPFLAGS="-I${withval}/include ${CPPFLAGS}" else CPPFLAGS="-I${withval} ${CPPFLAGS}" fi fi fi # These libraries are needed for anything that links in the channel code. CHANNELLIBS="" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for zlib" >&5 printf %s "checking for zlib... " >&6; } if test "x${zlib}" = "xno"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } else saved_LIBS="$LIBS" CHANNELLIBS="$CHANNELLIBS -lz" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } printf "%s\n" "#define WITH_ZLIB 1" >>confdefs.h ac_fn_c_check_header_compile "$LINENO" "zlib.h" "ac_cv_header_zlib_h" "$ac_includes_default" if test "x$ac_cv_header_zlib_h" = xyes then : else $as_nop as_fn_error $? "*** zlib.h missing - please install first or check config.log ***" "$LINENO" 5 fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for deflate in -lz" >&5 printf %s "checking for deflate in -lz... " >&6; } if test ${ac_cv_lib_z_deflate+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lz $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char deflate (); int main (void) { return deflate (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_z_deflate=yes else $as_nop ac_cv_lib_z_deflate=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_z_deflate" >&5 printf "%s\n" "$ac_cv_lib_z_deflate" >&6; } if test "x$ac_cv_lib_z_deflate" = xyes then : printf "%s\n" "#define HAVE_LIBZ 1" >>confdefs.h LIBS="-lz $LIBS" else $as_nop saved_CPPFLAGS="$CPPFLAGS" saved_LDFLAGS="$LDFLAGS" if test -n "${rpath_opt}"; then LDFLAGS="-L/usr/local/lib ${rpath_opt}/usr/local/lib ${saved_LDFLAGS}" else LDFLAGS="-L/usr/local/lib ${saved_LDFLAGS}" fi CPPFLAGS="-I/usr/local/include ${saved_CPPFLAGS}" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char deflate (); int main (void) { return deflate (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : printf "%s\n" "#define HAVE_LIBZ 1" >>confdefs.h else $as_nop as_fn_error $? "*** zlib missing - please install first or check config.log ***" "$LINENO" 5 fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext fi # Check whether --with-zlib-version-check was given. if test ${with_zlib_version_check+y} then : withval=$with_zlib_version_check; if test "x$withval" = "xno" ; then zlib_check_nonfatal=1 fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for possibly buggy zlib" >&5 printf %s "checking for possibly buggy zlib... " >&6; } if test "$cross_compiling" = yes then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cross compiling: not checking zlib version" >&5 printf "%s\n" "$as_me: WARNING: cross compiling: not checking zlib version" >&2;} else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include int main (void) { int a=0, b=0, c=0, d=0, n, v; n = sscanf(ZLIB_VERSION, "%d.%d.%d.%d", &a, &b, &c, &d); - if (n != 3 && n != 4) + if (n < 1) exit(1); v = a*1000000 + b*10000 + c*100 + d; fprintf(stderr, "found zlib version %s (%d)\n", ZLIB_VERSION, v); /* 1.1.4 is OK */ if (a == 1 && b == 1 && c >= 4) exit(0); /* 1.2.3 and up are OK */ if (v >= 1020300) exit(0); exit(2); ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } if test -z "$zlib_check_nonfatal" ; then as_fn_error $? "*** zlib too old - check config.log *** Your reported zlib version has known security problems. It's possible your vendor has fixed these problems without changing the version number. If you are sure this is the case, you can disable the check by running \"./configure --without-zlib-version-check\". If you are in doubt, upgrade zlib to version 1.2.3 or greater. See http://www.gzip.org/zlib/ for details." "$LINENO" 5 else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: zlib version may have security problems" >&5 printf "%s\n" "$as_me: WARNING: zlib version may have security problems" >&2;} fi fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi LIBS="$saved_LIBS" fi ac_fn_c_check_func "$LINENO" "strcasecmp" "ac_cv_func_strcasecmp" if test "x$ac_cv_func_strcasecmp" = xyes then : else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for strcasecmp in -lresolv" >&5 printf %s "checking for strcasecmp in -lresolv... " >&6; } if test ${ac_cv_lib_resolv_strcasecmp+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lresolv $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char strcasecmp (); int main (void) { return strcasecmp (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_resolv_strcasecmp=yes else $as_nop ac_cv_lib_resolv_strcasecmp=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_resolv_strcasecmp" >&5 printf "%s\n" "$ac_cv_lib_resolv_strcasecmp" >&6; } if test "x$ac_cv_lib_resolv_strcasecmp" = xyes then : LIBS="$LIBS -lresolv" fi fi for ac_func in utimes do : ac_fn_c_check_func "$LINENO" "utimes" "ac_cv_func_utimes" if test "x$ac_cv_func_utimes" = xyes then : printf "%s\n" "#define HAVE_UTIMES 1" >>confdefs.h else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for utimes in -lc89" >&5 printf %s "checking for utimes in -lc89... " >&6; } if test ${ac_cv_lib_c89_utimes+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lc89 $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char utimes (); int main (void) { return utimes (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_c89_utimes=yes else $as_nop ac_cv_lib_c89_utimes=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_c89_utimes" >&5 printf "%s\n" "$ac_cv_lib_c89_utimes" >&6; } if test "x$ac_cv_lib_c89_utimes" = xyes then : printf "%s\n" "#define HAVE_UTIMES 1" >>confdefs.h LIBS="$LIBS -lc89" fi fi done ac_fn_c_check_header_compile "$LINENO" "bsd/libutil.h" "ac_cv_header_bsd_libutil_h" "$ac_includes_default" if test "x$ac_cv_header_bsd_libutil_h" = xyes then : printf "%s\n" "#define HAVE_BSD_LIBUTIL_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "libutil.h" "ac_cv_header_libutil_h" "$ac_includes_default" if test "x$ac_cv_header_libutil_h" = xyes then : printf "%s\n" "#define HAVE_LIBUTIL_H 1" >>confdefs.h fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing fmt_scaled" >&5 printf %s "checking for library containing fmt_scaled... " >&6; } if test ${ac_cv_search_fmt_scaled+y} then : printf %s "(cached) " >&6 else $as_nop ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char fmt_scaled (); int main (void) { return fmt_scaled (); ; return 0; } _ACEOF for ac_lib in '' util bsd do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO" then : ac_cv_search_fmt_scaled=$ac_res fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext if test ${ac_cv_search_fmt_scaled+y} then : break fi done if test ${ac_cv_search_fmt_scaled+y} then : else $as_nop ac_cv_search_fmt_scaled=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_fmt_scaled" >&5 printf "%s\n" "$ac_cv_search_fmt_scaled" >&6; } ac_res=$ac_cv_search_fmt_scaled if test "$ac_res" != no then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing scan_scaled" >&5 printf %s "checking for library containing scan_scaled... " >&6; } if test ${ac_cv_search_scan_scaled+y} then : printf %s "(cached) " >&6 else $as_nop ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char scan_scaled (); int main (void) { return scan_scaled (); ; return 0; } _ACEOF for ac_lib in '' util bsd do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO" then : ac_cv_search_scan_scaled=$ac_res fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext if test ${ac_cv_search_scan_scaled+y} then : break fi done if test ${ac_cv_search_scan_scaled+y} then : else $as_nop ac_cv_search_scan_scaled=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_scan_scaled" >&5 printf "%s\n" "$ac_cv_search_scan_scaled" >&6; } ac_res=$ac_cv_search_scan_scaled if test "$ac_res" != no then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing login" >&5 printf %s "checking for library containing login... " >&6; } if test ${ac_cv_search_login+y} then : printf %s "(cached) " >&6 else $as_nop ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char login (); int main (void) { return login (); ; return 0; } _ACEOF for ac_lib in '' util bsd do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO" then : ac_cv_search_login=$ac_res fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext if test ${ac_cv_search_login+y} then : break fi done if test ${ac_cv_search_login+y} then : else $as_nop ac_cv_search_login=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_login" >&5 printf "%s\n" "$ac_cv_search_login" >&6; } ac_res=$ac_cv_search_login if test "$ac_res" != no then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing logout" >&5 printf %s "checking for library containing logout... " >&6; } if test ${ac_cv_search_logout+y} then : printf %s "(cached) " >&6 else $as_nop ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char logout (); int main (void) { return logout (); ; return 0; } _ACEOF for ac_lib in '' util bsd do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO" then : ac_cv_search_logout=$ac_res fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext if test ${ac_cv_search_logout+y} then : break fi done if test ${ac_cv_search_logout+y} then : else $as_nop ac_cv_search_logout=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_logout" >&5 printf "%s\n" "$ac_cv_search_logout" >&6; } ac_res=$ac_cv_search_logout if test "$ac_res" != no then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing logwtmp" >&5 printf %s "checking for library containing logwtmp... " >&6; } if test ${ac_cv_search_logwtmp+y} then : printf %s "(cached) " >&6 else $as_nop ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char logwtmp (); int main (void) { return logwtmp (); ; return 0; } _ACEOF for ac_lib in '' util bsd do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO" then : ac_cv_search_logwtmp=$ac_res fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext if test ${ac_cv_search_logwtmp+y} then : break fi done if test ${ac_cv_search_logwtmp+y} then : else $as_nop ac_cv_search_logwtmp=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_logwtmp" >&5 printf "%s\n" "$ac_cv_search_logwtmp" >&6; } ac_res=$ac_cv_search_logwtmp if test "$ac_res" != no then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing openpty" >&5 printf %s "checking for library containing openpty... " >&6; } if test ${ac_cv_search_openpty+y} then : printf %s "(cached) " >&6 else $as_nop ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char openpty (); int main (void) { return openpty (); ; return 0; } _ACEOF for ac_lib in '' util bsd do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO" then : ac_cv_search_openpty=$ac_res fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext if test ${ac_cv_search_openpty+y} then : break fi done if test ${ac_cv_search_openpty+y} then : else $as_nop ac_cv_search_openpty=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_openpty" >&5 printf "%s\n" "$ac_cv_search_openpty" >&6; } ac_res=$ac_cv_search_openpty if test "$ac_res" != no then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing updwtmp" >&5 printf %s "checking for library containing updwtmp... " >&6; } if test ${ac_cv_search_updwtmp+y} then : printf %s "(cached) " >&6 else $as_nop ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char updwtmp (); int main (void) { return updwtmp (); ; return 0; } _ACEOF for ac_lib in '' util bsd do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO" then : ac_cv_search_updwtmp=$ac_res fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext if test ${ac_cv_search_updwtmp+y} then : break fi done if test ${ac_cv_search_updwtmp+y} then : else $as_nop ac_cv_search_updwtmp=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_updwtmp" >&5 printf "%s\n" "$ac_cv_search_updwtmp" >&6; } ac_res=$ac_cv_search_updwtmp if test "$ac_res" != no then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi ac_fn_c_check_func "$LINENO" "fmt_scaled" "ac_cv_func_fmt_scaled" if test "x$ac_cv_func_fmt_scaled" = xyes then : printf "%s\n" "#define HAVE_FMT_SCALED 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "scan_scaled" "ac_cv_func_scan_scaled" if test "x$ac_cv_func_scan_scaled" = xyes then : printf "%s\n" "#define HAVE_SCAN_SCALED 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "login" "ac_cv_func_login" if test "x$ac_cv_func_login" = xyes then : printf "%s\n" "#define HAVE_LOGIN 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "logout" "ac_cv_func_logout" if test "x$ac_cv_func_logout" = xyes then : printf "%s\n" "#define HAVE_LOGOUT 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "openpty" "ac_cv_func_openpty" if test "x$ac_cv_func_openpty" = xyes then : printf "%s\n" "#define HAVE_OPENPTY 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "updwtmp" "ac_cv_func_updwtmp" if test "x$ac_cv_func_updwtmp" = xyes then : printf "%s\n" "#define HAVE_UPDWTMP 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "logwtmp" "ac_cv_func_logwtmp" if test "x$ac_cv_func_logwtmp" = xyes then : printf "%s\n" "#define HAVE_LOGWTMP 1" >>confdefs.h fi # On some platforms, inet_ntop and gethostbyname may be found in libresolv # or libnsl. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing inet_ntop" >&5 printf %s "checking for library containing inet_ntop... " >&6; } if test ${ac_cv_search_inet_ntop+y} then : printf %s "(cached) " >&6 else $as_nop ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char inet_ntop (); int main (void) { return inet_ntop (); ; return 0; } _ACEOF for ac_lib in '' resolv nsl do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO" then : ac_cv_search_inet_ntop=$ac_res fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext if test ${ac_cv_search_inet_ntop+y} then : break fi done if test ${ac_cv_search_inet_ntop+y} then : else $as_nop ac_cv_search_inet_ntop=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_inet_ntop" >&5 printf "%s\n" "$ac_cv_search_inet_ntop" >&6; } ac_res=$ac_cv_search_inet_ntop if test "$ac_res" != no then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing gethostbyname" >&5 printf %s "checking for library containing gethostbyname... " >&6; } if test ${ac_cv_search_gethostbyname+y} then : printf %s "(cached) " >&6 else $as_nop ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char gethostbyname (); int main (void) { return gethostbyname (); ; return 0; } _ACEOF for ac_lib in '' resolv nsl do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO" then : ac_cv_search_gethostbyname=$ac_res fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext if test ${ac_cv_search_gethostbyname+y} then : break fi done if test ${ac_cv_search_gethostbyname+y} then : else $as_nop ac_cv_search_gethostbyname=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_gethostbyname" >&5 printf "%s\n" "$ac_cv_search_gethostbyname" >&6; } ac_res=$ac_cv_search_gethostbyname if test "$ac_res" != no then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi # Some Linux distribtions ship the BSD libc hashing functions in # separate libraries. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing SHA256Update" >&5 printf %s "checking for library containing SHA256Update... " >&6; } if test ${ac_cv_search_SHA256Update+y} then : printf %s "(cached) " >&6 else $as_nop ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char SHA256Update (); int main (void) { return SHA256Update (); ; return 0; } _ACEOF for ac_lib in '' md bsd do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO" then : ac_cv_search_SHA256Update=$ac_res fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext if test ${ac_cv_search_SHA256Update+y} then : break fi done if test ${ac_cv_search_SHA256Update+y} then : else $as_nop ac_cv_search_SHA256Update=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_SHA256Update" >&5 printf "%s\n" "$ac_cv_search_SHA256Update" >&6; } ac_res=$ac_cv_search_SHA256Update if test "$ac_res" != no then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi # "Particular Function Checks" # see https://www.gnu.org/software/autoconf/manual/autoconf-2.69/html_node/Particular-Functions.html for ac_func in strftime do : ac_fn_c_check_func "$LINENO" "strftime" "ac_cv_func_strftime" if test "x$ac_cv_func_strftime" = xyes then : printf "%s\n" "#define HAVE_STRFTIME 1" >>confdefs.h else $as_nop # strftime is in -lintl on SCO UNIX. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for strftime in -lintl" >&5 printf %s "checking for strftime in -lintl... " >&6; } if test ${ac_cv_lib_intl_strftime+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lintl $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char strftime (); int main (void) { return strftime (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_intl_strftime=yes else $as_nop ac_cv_lib_intl_strftime=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_intl_strftime" >&5 printf "%s\n" "$ac_cv_lib_intl_strftime" >&6; } if test "x$ac_cv_lib_intl_strftime" = xyes then : printf "%s\n" "#define HAVE_STRFTIME 1" >>confdefs.h LIBS="-lintl $LIBS" fi fi done { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for GNU libc compatible malloc" >&5 printf %s "checking for GNU libc compatible malloc... " >&6; } if test ${ac_cv_func_malloc_0_nonnull+y} then : printf %s "(cached) " >&6 else $as_nop if test "$cross_compiling" = yes then : case "$host_os" in # (( # Guess yes on platforms where we know the result. *-gnu* | freebsd* | netbsd* | openbsd* | bitrig* \ | hpux* | solaris* | cygwin* | mingw* | msys* ) ac_cv_func_malloc_0_nonnull=yes ;; # If we don't know, assume the worst. *) ac_cv_func_malloc_0_nonnull=no ;; esac else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { void *p = malloc (0); int result = !p; free (p); return result; ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO" then : ac_cv_func_malloc_0_nonnull=yes else $as_nop ac_cv_func_malloc_0_nonnull=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_malloc_0_nonnull" >&5 printf "%s\n" "$ac_cv_func_malloc_0_nonnull" >&6; } if test $ac_cv_func_malloc_0_nonnull = yes then : printf "%s\n" "#define HAVE_MALLOC 1" >>confdefs.h else $as_nop printf "%s\n" "#define HAVE_MALLOC 0" >>confdefs.h case " $LIBOBJS " in *" malloc.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS malloc.$ac_objext" ;; esac printf "%s\n" "#define malloc rpl_malloc" >>confdefs.h fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for GNU libc compatible realloc" >&5 printf %s "checking for GNU libc compatible realloc... " >&6; } if test ${ac_cv_func_realloc_0_nonnull+y} then : printf %s "(cached) " >&6 else $as_nop if test "$cross_compiling" = yes then : case "$host_os" in # (( # Guess yes on platforms where we know the result. *-gnu* | freebsd* | netbsd* | openbsd* | bitrig* \ | hpux* | solaris* | cygwin* | mingw* | msys* ) ac_cv_func_realloc_0_nonnull=yes ;; # If we don't know, assume the worst. *) ac_cv_func_realloc_0_nonnull=no ;; esac else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { void *p = realloc (0, 0); int result = !p; free (p); return result; ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO" then : ac_cv_func_realloc_0_nonnull=yes else $as_nop ac_cv_func_realloc_0_nonnull=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_realloc_0_nonnull" >&5 printf "%s\n" "$ac_cv_func_realloc_0_nonnull" >&6; } if test $ac_cv_func_realloc_0_nonnull = yes then : printf "%s\n" "#define HAVE_REALLOC 1" >>confdefs.h else $as_nop printf "%s\n" "#define HAVE_REALLOC 0" >>confdefs.h case " $LIBOBJS " in *" realloc.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS realloc.$ac_objext" ;; esac printf "%s\n" "#define realloc rpl_realloc" >>confdefs.h fi # autoconf doesn't have AC_FUNC_CALLOC so fake it if malloc returns NULL; { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if calloc(0, N) returns non-null" >&5 printf %s "checking if calloc(0, N) returns non-null... " >&6; } if test "$cross_compiling" = yes then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cross compiling: assuming same as malloc" >&5 printf "%s\n" "$as_me: WARNING: cross compiling: assuming same as malloc" >&2;} func_calloc_0_nonnull="$ac_cv_func_malloc_0_nonnull" else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { void *p = calloc(0, 1); exit(p == NULL); ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO" then : func_calloc_0_nonnull=yes else $as_nop func_calloc_0_nonnull=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $func_calloc_0_nonnull" >&5 printf "%s\n" "$func_calloc_0_nonnull" >&6; } if test "x$func_calloc_0_nonnull" = "xyes"; then printf "%s\n" "#define HAVE_CALLOC 1" >>confdefs.h else printf "%s\n" "#define HAVE_CALLOC 0" >>confdefs.h printf "%s\n" "#define calloc rpl_calloc" >>confdefs.h fi # Check for ALTDIRFUNC glob() extension { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for GLOB_ALTDIRFUNC support" >&5 printf %s "checking for GLOB_ALTDIRFUNC support... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #ifdef GLOB_ALTDIRFUNC FOUNDIT #endif _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "FOUNDIT" >/dev/null 2>&1 then : printf "%s\n" "#define GLOB_HAS_ALTDIRFUNC 1" >>confdefs.h { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi rm -rf conftest* # Check for g.gl_matchc glob() extension { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for gl_matchc field in glob_t" >&5 printf %s "checking for gl_matchc field in glob_t... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { glob_t g; g.gl_matchc = 1; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : printf "%s\n" "#define GLOB_HAS_GL_MATCHC 1" >>confdefs.h { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext # Check for g.gl_statv glob() extension { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for gl_statv and GLOB_KEEPSTAT extensions for glob" >&5 printf %s "checking for gl_statv and GLOB_KEEPSTAT extensions for glob... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { #ifndef GLOB_KEEPSTAT #error "glob does not support GLOB_KEEPSTAT extension" #endif glob_t g; g.gl_statv = NULL; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : printf "%s\n" "#define GLOB_HAS_GL_STATV 1" >>confdefs.h { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ac_fn_check_decl "$LINENO" "GLOB_NOMATCH" "ac_cv_have_decl_GLOB_NOMATCH" "#include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_GLOB_NOMATCH" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_GLOB_NOMATCH $ac_have_decl" >>confdefs.h ac_fn_check_decl "$LINENO" "VIS_ALL" "ac_cv_have_decl_VIS_ALL" "#include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_VIS_ALL" = xyes then : else $as_nop printf "%s\n" "#define BROKEN_STRNVIS 1" >>confdefs.h fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether struct dirent allocates space for d_name" >&5 printf %s "checking whether struct dirent allocates space for d_name... " >&6; } if test "$cross_compiling" = yes then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cross compiling: assuming BROKEN_ONE_BYTE_DIRENT_D_NAME" >&5 printf "%s\n" "$as_me: WARNING: cross compiling: assuming BROKEN_ONE_BYTE_DIRENT_D_NAME" >&2;} printf "%s\n" "#define BROKEN_ONE_BYTE_DIRENT_D_NAME 1" >>confdefs.h else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include int main (void) { struct dirent d; exit(sizeof(d.d_name)<=sizeof(char)); ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } printf "%s\n" "#define BROKEN_ONE_BYTE_DIRENT_D_NAME 1" >>confdefs.h fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for /proc/pid/fd directory" >&5 printf %s "checking for /proc/pid/fd directory... " >&6; } if test -d "/proc/$$/fd" ; then printf "%s\n" "#define HAVE_PROC_PID 1" >>confdefs.h { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi # Check whether user wants to use ldns LDNS_MSG="no" # Check whether --with-ldns was given. if test ${with_ldns+y} then : withval=$with_ldns; ldns="" if test "x$withval" = "xyes" ; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}ldns-config", so it can be a program name with args. set dummy ${ac_tool_prefix}ldns-config; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_LDNSCONFIG+y} then : printf %s "(cached) " >&6 else $as_nop case $LDNSCONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_LDNSCONFIG="$LDNSCONFIG" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_path_LDNSCONFIG="$as_dir$ac_word$ac_exec_ext" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi LDNSCONFIG=$ac_cv_path_LDNSCONFIG if test -n "$LDNSCONFIG"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $LDNSCONFIG" >&5 printf "%s\n" "$LDNSCONFIG" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_path_LDNSCONFIG"; then ac_pt_LDNSCONFIG=$LDNSCONFIG # Extract the first word of "ldns-config", so it can be a program name with args. set dummy ldns-config; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_ac_pt_LDNSCONFIG+y} then : printf %s "(cached) " >&6 else $as_nop case $ac_pt_LDNSCONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_ac_pt_LDNSCONFIG="$ac_pt_LDNSCONFIG" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_path_ac_pt_LDNSCONFIG="$as_dir$ac_word$ac_exec_ext" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi ac_pt_LDNSCONFIG=$ac_cv_path_ac_pt_LDNSCONFIG if test -n "$ac_pt_LDNSCONFIG"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_pt_LDNSCONFIG" >&5 printf "%s\n" "$ac_pt_LDNSCONFIG" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_pt_LDNSCONFIG" = x; then LDNSCONFIG="no" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac LDNSCONFIG=$ac_pt_LDNSCONFIG fi else LDNSCONFIG="$ac_cv_path_LDNSCONFIG" fi if test "x$LDNSCONFIG" = "xno"; then LIBS="-lldns $LIBS" ldns=yes else LIBS="$LIBS `$LDNSCONFIG --libs`" CPPFLAGS="$CPPFLAGS `$LDNSCONFIG --cflags`" ldns=yes fi elif test "x$withval" != "xno" ; then CPPFLAGS="$CPPFLAGS -I${withval}/include" LDFLAGS="$LDFLAGS -L${withval}/lib" LIBS="-lldns $LIBS" ldns=yes fi # Verify that it works. if test "x$ldns" = "xyes" ; then printf "%s\n" "#define HAVE_LDNS 1" >>confdefs.h LDNS_MSG="yes" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for ldns support" >&5 printf %s "checking for ldns support... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #ifdef HAVE_STDINT_H # include #endif #include int main(void) { ldns_status status = ldns_verify_trusted(NULL, NULL, NULL, NULL); status=LDNS_STATUS_OK; exit(0); } _ACEOF if ac_fn_c_try_link "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } as_fn_error $? "** Incomplete or missing ldns libraries." "$LINENO" 5 fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext fi fi # Check whether user wants libedit support LIBEDIT_MSG="no" # Check whether --with-libedit was given. if test ${with_libedit+y} then : withval=$with_libedit; if test "x$withval" != "xno" ; then if test "x$withval" = "xyes" ; then if test "x$PKGCONFIG" != "xno"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $PKGCONFIG knows about libedit" >&5 printf %s "checking if $PKGCONFIG knows about libedit... " >&6; } if "$PKGCONFIG" libedit; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } use_pkgconfig_for_libedit=yes else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi else CPPFLAGS="$CPPFLAGS -I${withval}/include" if test -n "${rpath_opt}"; then LDFLAGS="-L${withval}/lib ${rpath_opt}${withval}/lib ${LDFLAGS}" else LDFLAGS="-L${withval}/lib ${LDFLAGS}" fi fi if test "x$use_pkgconfig_for_libedit" = "xyes"; then LIBEDIT=`$PKGCONFIG --libs libedit` CPPFLAGS="$CPPFLAGS `$PKGCONFIG --cflags libedit`" else LIBEDIT="-ledit -lcurses" fi OTHERLIBS=`echo $LIBEDIT | sed 's/-ledit//'` { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for el_init in -ledit" >&5 printf %s "checking for el_init in -ledit... " >&6; } if test ${ac_cv_lib_edit_el_init+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-ledit $OTHERLIBS $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char el_init (); int main (void) { return el_init (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_edit_el_init=yes else $as_nop ac_cv_lib_edit_el_init=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_edit_el_init" >&5 printf "%s\n" "$ac_cv_lib_edit_el_init" >&6; } if test "x$ac_cv_lib_edit_el_init" = xyes then : printf "%s\n" "#define USE_LIBEDIT 1" >>confdefs.h LIBEDIT_MSG="yes" else $as_nop as_fn_error $? "libedit not found" "$LINENO" 5 fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if libedit version is compatible" >&5 printf %s "checking if libedit version is compatible... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main (void) { int i = H_SETSIZE; el_init("", NULL, NULL, NULL); exit(0); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } as_fn_error $? "libedit version is not compatible" "$LINENO" 5 fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi AUDIT_MODULE=none # Check whether --with-audit was given. if test ${with_audit+y} then : withval=$with_audit; { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for supported audit module" >&5 printf %s "checking for supported audit module... " >&6; } case "$withval" in bsm) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: bsm" >&5 printf "%s\n" "bsm" >&6; } AUDIT_MODULE=bsm for ac_header in bsm/audit.h do : ac_fn_c_check_header_compile "$LINENO" "bsm/audit.h" "ac_cv_header_bsm_audit_h" " #ifdef HAVE_TIME_H # include #endif " if test "x$ac_cv_header_bsm_audit_h" = xyes then : printf "%s\n" "#define HAVE_BSM_AUDIT_H 1" >>confdefs.h else $as_nop as_fn_error $? "BSM enabled and bsm/audit.h not found" "$LINENO" 5 fi done { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for getaudit in -lbsm" >&5 printf %s "checking for getaudit in -lbsm... " >&6; } if test ${ac_cv_lib_bsm_getaudit+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lbsm $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char getaudit (); int main (void) { return getaudit (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_bsm_getaudit=yes else $as_nop ac_cv_lib_bsm_getaudit=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_bsm_getaudit" >&5 printf "%s\n" "$ac_cv_lib_bsm_getaudit" >&6; } if test "x$ac_cv_lib_bsm_getaudit" = xyes then : printf "%s\n" "#define HAVE_LIBBSM 1" >>confdefs.h LIBS="-lbsm $LIBS" else $as_nop as_fn_error $? "BSM enabled and required library not found" "$LINENO" 5 fi for ac_func in getaudit do : ac_fn_c_check_func "$LINENO" "getaudit" "ac_cv_func_getaudit" if test "x$ac_cv_func_getaudit" = xyes then : printf "%s\n" "#define HAVE_GETAUDIT 1" >>confdefs.h else $as_nop as_fn_error $? "BSM enabled and required function not found" "$LINENO" 5 fi done # These are optional ac_fn_c_check_func "$LINENO" "getaudit_addr" "ac_cv_func_getaudit_addr" if test "x$ac_cv_func_getaudit_addr" = xyes then : printf "%s\n" "#define HAVE_GETAUDIT_ADDR 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "aug_get_machine" "ac_cv_func_aug_get_machine" if test "x$ac_cv_func_aug_get_machine" = xyes then : printf "%s\n" "#define HAVE_AUG_GET_MACHINE 1" >>confdefs.h fi printf "%s\n" "#define USE_BSM_AUDIT 1" >>confdefs.h if test "$sol2ver" -ge 11; then SSHDLIBS="$SSHDLIBS -lscf" printf "%s\n" "#define BROKEN_BSM_API 1" >>confdefs.h fi ;; linux) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: linux" >&5 printf "%s\n" "linux" >&6; } AUDIT_MODULE=linux ac_fn_c_check_header_compile "$LINENO" "libaudit.h" "ac_cv_header_libaudit_h" "$ac_includes_default" if test "x$ac_cv_header_libaudit_h" = xyes then : printf "%s\n" "#define HAVE_LIBAUDIT_H 1" >>confdefs.h fi SSHDLIBS="$SSHDLIBS -laudit" printf "%s\n" "#define USE_LINUX_AUDIT 1" >>confdefs.h ;; debug) AUDIT_MODULE=debug { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: debug" >&5 printf "%s\n" "debug" >&6; } printf "%s\n" "#define SSH_AUDIT_EVENTS 1" >>confdefs.h ;; no) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } ;; *) as_fn_error $? "Unknown audit module $withval" "$LINENO" 5 ;; esac fi # Check whether --with-pie was given. if test ${with_pie+y} then : withval=$with_pie; if test "x$withval" = "xno"; then use_pie=no fi if test "x$withval" = "xyes"; then use_pie=yes fi fi if test "x$use_pie" = "x"; then use_pie=no fi if test "x$use_toolchain_hardening" != "x1" && test "x$use_pie" = "xauto"; then # Turn off automatic PIE when toolchain hardening is off. use_pie=no fi if test "x$use_pie" = "xauto"; then # Automatic PIE requires gcc >= 4.x { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for gcc >= 4.x" >&5 printf %s "checking for gcc >= 4.x... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #if !defined(__GNUC__) || __GNUC__ < 4 #error gcc is too old #endif _ACEOF if ac_fn_c_try_compile "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } use_pie=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi if test "x$use_pie" != "xno"; then SAVED_CFLAGS="$CFLAGS" SAVED_LDFLAGS="$LDFLAGS" { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $CC supports compile flag -fPIE" >&5 printf %s "checking if $CC supports compile flag -fPIE... " >&6; } saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $WERROR -fPIE" _define_flag="" test "x$_define_flag" = "x" && _define_flag="-fPIE" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include /* Trivial function to help test for -fzero-call-used-regs */ void f(int n) {} int main(int argc, char **argv) { (void)argv; /* Some math to catch -ftrapv problems in the toolchain */ int i = 123 * argc, j = 456 + argc, k = 789 - argc; float l = i * 2.1; double m = l / 0.5; long long int n = argc * 12345LL, o = 12345LL * (long long int)argc; f(0); printf("%d %d %d %f %f %lld %lld\n", i, j, k, l, m, n, o); /* * Test fallthrough behaviour. clang 10's -Wimplicit-fallthrough does * not understand comments and we don't use the "fallthrough" attribute * that it's looking for. */ switch(i){ case 0: j += i; /* FALLTHROUGH */ default: j += k; } exit(0); } _ACEOF if ac_fn_c_try_compile "$LINENO" then : if $ac_cv_path_EGREP -i "unrecognized option|warning.*ignored" conftest.err >/dev/null then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } CFLAGS="$saved_CFLAGS" else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } CFLAGS="$saved_CFLAGS $_define_flag" fi else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } CFLAGS="$saved_CFLAGS" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext } { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $LD supports link flag -pie" >&5 printf %s "checking if $LD supports link flag -pie... " >&6; } saved_LDFLAGS="$LDFLAGS" LDFLAGS="$LDFLAGS $WERROR -pie" _define_flag="" test "x$_define_flag" = "x" && _define_flag="-pie" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main(int argc, char **argv) { (void)argv; /* Some math to catch -ftrapv problems in the toolchain */ int i = 123 * argc, j = 456 + argc, k = 789 - argc; float l = i * 2.1; double m = l / 0.5; long long int n = argc * 12345LL, o = 12345LL * (long long int)argc; long long p = n * o; printf("%d %d %d %f %f %lld %lld %lld\n", i, j, k, l, m, n, o, p); exit(0); } _ACEOF if ac_fn_c_try_link "$LINENO" then : if $ac_cv_path_EGREP -i "unrecognized option|warning.*ignored" conftest.err >/dev/null then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } LDFLAGS="$saved_LDFLAGS" else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } LDFLAGS="$saved_LDFLAGS $_define_flag" fi else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } LDFLAGS="$saved_LDFLAGS" fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext } # We use both -fPIE and -pie or neither. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether both -fPIE and -pie are supported" >&5 printf %s "checking whether both -fPIE and -pie are supported... " >&6; } if echo "x $CFLAGS" | grep ' -fPIE' >/dev/null 2>&1 && \ echo "x $LDFLAGS" | grep ' -pie' >/dev/null 2>&1 ; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } CFLAGS="$SAVED_CFLAGS" LDFLAGS="$SAVED_LDFLAGS" fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether -fPIC is accepted" >&5 printf %s "checking whether -fPIC is accepted... " >&6; } SAVED_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -fPIC" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { exit(0); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } PICFLAG="-fPIC"; else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } PICFLAG=""; fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS="$SAVED_CFLAGS" ac_fn_c_check_func "$LINENO" "Blowfish_initstate" "ac_cv_func_Blowfish_initstate" if test "x$ac_cv_func_Blowfish_initstate" = xyes then : printf "%s\n" "#define HAVE_BLOWFISH_INITSTATE 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "Blowfish_expandstate" "ac_cv_func_Blowfish_expandstate" if test "x$ac_cv_func_Blowfish_expandstate" = xyes then : printf "%s\n" "#define HAVE_BLOWFISH_EXPANDSTATE 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "Blowfish_expand0state" "ac_cv_func_Blowfish_expand0state" if test "x$ac_cv_func_Blowfish_expand0state" = xyes then : printf "%s\n" "#define HAVE_BLOWFISH_EXPAND0STATE 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "Blowfish_stream2word" "ac_cv_func_Blowfish_stream2word" if test "x$ac_cv_func_Blowfish_stream2word" = xyes then : printf "%s\n" "#define HAVE_BLOWFISH_STREAM2WORD 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "SHA256Update" "ac_cv_func_SHA256Update" if test "x$ac_cv_func_SHA256Update" = xyes then : printf "%s\n" "#define HAVE_SHA256UPDATE 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "SHA384Update" "ac_cv_func_SHA384Update" if test "x$ac_cv_func_SHA384Update" = xyes then : printf "%s\n" "#define HAVE_SHA384UPDATE 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "SHA512Update" "ac_cv_func_SHA512Update" if test "x$ac_cv_func_SHA512Update" = xyes then : printf "%s\n" "#define HAVE_SHA512UPDATE 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "asprintf" "ac_cv_func_asprintf" if test "x$ac_cv_func_asprintf" = xyes then : printf "%s\n" "#define HAVE_ASPRINTF 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "b64_ntop" "ac_cv_func_b64_ntop" if test "x$ac_cv_func_b64_ntop" = xyes then : printf "%s\n" "#define HAVE_B64_NTOP 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "__b64_ntop" "ac_cv_func___b64_ntop" if test "x$ac_cv_func___b64_ntop" = xyes then : printf "%s\n" "#define HAVE___B64_NTOP 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "b64_pton" "ac_cv_func_b64_pton" if test "x$ac_cv_func_b64_pton" = xyes then : printf "%s\n" "#define HAVE_B64_PTON 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "__b64_pton" "ac_cv_func___b64_pton" if test "x$ac_cv_func___b64_pton" = xyes then : printf "%s\n" "#define HAVE___B64_PTON 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "bcopy" "ac_cv_func_bcopy" if test "x$ac_cv_func_bcopy" = xyes then : printf "%s\n" "#define HAVE_BCOPY 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "bcrypt_pbkdf" "ac_cv_func_bcrypt_pbkdf" if test "x$ac_cv_func_bcrypt_pbkdf" = xyes then : printf "%s\n" "#define HAVE_BCRYPT_PBKDF 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "bindresvport_sa" "ac_cv_func_bindresvport_sa" if test "x$ac_cv_func_bindresvport_sa" = xyes then : printf "%s\n" "#define HAVE_BINDRESVPORT_SA 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "blf_enc" "ac_cv_func_blf_enc" if test "x$ac_cv_func_blf_enc" = xyes then : printf "%s\n" "#define HAVE_BLF_ENC 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "bzero" "ac_cv_func_bzero" if test "x$ac_cv_func_bzero" = xyes then : printf "%s\n" "#define HAVE_BZERO 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "cap_rights_limit" "ac_cv_func_cap_rights_limit" if test "x$ac_cv_func_cap_rights_limit" = xyes then : printf "%s\n" "#define HAVE_CAP_RIGHTS_LIMIT 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "clock" "ac_cv_func_clock" if test "x$ac_cv_func_clock" = xyes then : printf "%s\n" "#define HAVE_CLOCK 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "closefrom" "ac_cv_func_closefrom" if test "x$ac_cv_func_closefrom" = xyes then : printf "%s\n" "#define HAVE_CLOSEFROM 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "close_range" "ac_cv_func_close_range" if test "x$ac_cv_func_close_range" = xyes then : printf "%s\n" "#define HAVE_CLOSE_RANGE 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "dirfd" "ac_cv_func_dirfd" if test "x$ac_cv_func_dirfd" = xyes then : printf "%s\n" "#define HAVE_DIRFD 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "endgrent" "ac_cv_func_endgrent" if test "x$ac_cv_func_endgrent" = xyes then : printf "%s\n" "#define HAVE_ENDGRENT 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "err" "ac_cv_func_err" if test "x$ac_cv_func_err" = xyes then : printf "%s\n" "#define HAVE_ERR 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "errx" "ac_cv_func_errx" if test "x$ac_cv_func_errx" = xyes then : printf "%s\n" "#define HAVE_ERRX 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "explicit_bzero" "ac_cv_func_explicit_bzero" if test "x$ac_cv_func_explicit_bzero" = xyes then : printf "%s\n" "#define HAVE_EXPLICIT_BZERO 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "explicit_memset" "ac_cv_func_explicit_memset" if test "x$ac_cv_func_explicit_memset" = xyes then : printf "%s\n" "#define HAVE_EXPLICIT_MEMSET 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "fchmod" "ac_cv_func_fchmod" if test "x$ac_cv_func_fchmod" = xyes then : printf "%s\n" "#define HAVE_FCHMOD 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "fchmodat" "ac_cv_func_fchmodat" if test "x$ac_cv_func_fchmodat" = xyes then : printf "%s\n" "#define HAVE_FCHMODAT 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "fchown" "ac_cv_func_fchown" if test "x$ac_cv_func_fchown" = xyes then : printf "%s\n" "#define HAVE_FCHOWN 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "fchownat" "ac_cv_func_fchownat" if test "x$ac_cv_func_fchownat" = xyes then : printf "%s\n" "#define HAVE_FCHOWNAT 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "flock" "ac_cv_func_flock" if test "x$ac_cv_func_flock" = xyes then : printf "%s\n" "#define HAVE_FLOCK 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "fnmatch" "ac_cv_func_fnmatch" if test "x$ac_cv_func_fnmatch" = xyes then : printf "%s\n" "#define HAVE_FNMATCH 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "freeaddrinfo" "ac_cv_func_freeaddrinfo" if test "x$ac_cv_func_freeaddrinfo" = xyes then : printf "%s\n" "#define HAVE_FREEADDRINFO 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "freezero" "ac_cv_func_freezero" if test "x$ac_cv_func_freezero" = xyes then : printf "%s\n" "#define HAVE_FREEZERO 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "fstatfs" "ac_cv_func_fstatfs" if test "x$ac_cv_func_fstatfs" = xyes then : printf "%s\n" "#define HAVE_FSTATFS 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "fstatvfs" "ac_cv_func_fstatvfs" if test "x$ac_cv_func_fstatvfs" = xyes then : printf "%s\n" "#define HAVE_FSTATVFS 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "futimes" "ac_cv_func_futimes" if test "x$ac_cv_func_futimes" = xyes then : printf "%s\n" "#define HAVE_FUTIMES 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "getaddrinfo" "ac_cv_func_getaddrinfo" if test "x$ac_cv_func_getaddrinfo" = xyes then : printf "%s\n" "#define HAVE_GETADDRINFO 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "getcwd" "ac_cv_func_getcwd" if test "x$ac_cv_func_getcwd" = xyes then : printf "%s\n" "#define HAVE_GETCWD 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "getentropy" "ac_cv_func_getentropy" if test "x$ac_cv_func_getentropy" = xyes then : printf "%s\n" "#define HAVE_GETENTROPY 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "getgrouplist" "ac_cv_func_getgrouplist" if test "x$ac_cv_func_getgrouplist" = xyes then : printf "%s\n" "#define HAVE_GETGROUPLIST 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "getline" "ac_cv_func_getline" if test "x$ac_cv_func_getline" = xyes then : printf "%s\n" "#define HAVE_GETLINE 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "getnameinfo" "ac_cv_func_getnameinfo" if test "x$ac_cv_func_getnameinfo" = xyes then : printf "%s\n" "#define HAVE_GETNAMEINFO 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "getopt" "ac_cv_func_getopt" if test "x$ac_cv_func_getopt" = xyes then : printf "%s\n" "#define HAVE_GETOPT 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "getpagesize" "ac_cv_func_getpagesize" if test "x$ac_cv_func_getpagesize" = xyes then : printf "%s\n" "#define HAVE_GETPAGESIZE 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "getpeereid" "ac_cv_func_getpeereid" if test "x$ac_cv_func_getpeereid" = xyes then : printf "%s\n" "#define HAVE_GETPEEREID 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "getpeerucred" "ac_cv_func_getpeerucred" if test "x$ac_cv_func_getpeerucred" = xyes then : printf "%s\n" "#define HAVE_GETPEERUCRED 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "getpgid" "ac_cv_func_getpgid" if test "x$ac_cv_func_getpgid" = xyes then : printf "%s\n" "#define HAVE_GETPGID 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "_getpty" "ac_cv_func__getpty" if test "x$ac_cv_func__getpty" = xyes then : printf "%s\n" "#define HAVE__GETPTY 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "getrlimit" "ac_cv_func_getrlimit" if test "x$ac_cv_func_getrlimit" = xyes then : printf "%s\n" "#define HAVE_GETRLIMIT 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "getrandom" "ac_cv_func_getrandom" if test "x$ac_cv_func_getrandom" = xyes then : printf "%s\n" "#define HAVE_GETRANDOM 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "getsid" "ac_cv_func_getsid" if test "x$ac_cv_func_getsid" = xyes then : printf "%s\n" "#define HAVE_GETSID 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "getttyent" "ac_cv_func_getttyent" if test "x$ac_cv_func_getttyent" = xyes then : printf "%s\n" "#define HAVE_GETTTYENT 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "glob" "ac_cv_func_glob" if test "x$ac_cv_func_glob" = xyes then : printf "%s\n" "#define HAVE_GLOB 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "group_from_gid" "ac_cv_func_group_from_gid" if test "x$ac_cv_func_group_from_gid" = xyes then : printf "%s\n" "#define HAVE_GROUP_FROM_GID 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "inet_aton" "ac_cv_func_inet_aton" if test "x$ac_cv_func_inet_aton" = xyes then : printf "%s\n" "#define HAVE_INET_ATON 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "inet_ntoa" "ac_cv_func_inet_ntoa" if test "x$ac_cv_func_inet_ntoa" = xyes then : printf "%s\n" "#define HAVE_INET_NTOA 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "inet_ntop" "ac_cv_func_inet_ntop" if test "x$ac_cv_func_inet_ntop" = xyes then : printf "%s\n" "#define HAVE_INET_NTOP 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "innetgr" "ac_cv_func_innetgr" if test "x$ac_cv_func_innetgr" = xyes then : printf "%s\n" "#define HAVE_INNETGR 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "killpg" "ac_cv_func_killpg" if test "x$ac_cv_func_killpg" = xyes then : printf "%s\n" "#define HAVE_KILLPG 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "llabs" "ac_cv_func_llabs" if test "x$ac_cv_func_llabs" = xyes then : printf "%s\n" "#define HAVE_LLABS 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "localtime_r" "ac_cv_func_localtime_r" if test "x$ac_cv_func_localtime_r" = xyes then : printf "%s\n" "#define HAVE_LOCALTIME_R 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "login_getcapbool" "ac_cv_func_login_getcapbool" if test "x$ac_cv_func_login_getcapbool" = xyes then : printf "%s\n" "#define HAVE_LOGIN_GETCAPBOOL 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "login_getpwclass" "ac_cv_func_login_getpwclass" if test "x$ac_cv_func_login_getpwclass" = xyes then : printf "%s\n" "#define HAVE_LOGIN_GETPWCLASS 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "memmem" "ac_cv_func_memmem" if test "x$ac_cv_func_memmem" = xyes then : printf "%s\n" "#define HAVE_MEMMEM 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "memmove" "ac_cv_func_memmove" if test "x$ac_cv_func_memmove" = xyes then : printf "%s\n" "#define HAVE_MEMMOVE 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "memset_s" "ac_cv_func_memset_s" if test "x$ac_cv_func_memset_s" = xyes then : printf "%s\n" "#define HAVE_MEMSET_S 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "mkdtemp" "ac_cv_func_mkdtemp" if test "x$ac_cv_func_mkdtemp" = xyes then : printf "%s\n" "#define HAVE_MKDTEMP 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "ngetaddrinfo" "ac_cv_func_ngetaddrinfo" if test "x$ac_cv_func_ngetaddrinfo" = xyes then : printf "%s\n" "#define HAVE_NGETADDRINFO 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "nsleep" "ac_cv_func_nsleep" if test "x$ac_cv_func_nsleep" = xyes then : printf "%s\n" "#define HAVE_NSLEEP 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "ogetaddrinfo" "ac_cv_func_ogetaddrinfo" if test "x$ac_cv_func_ogetaddrinfo" = xyes then : printf "%s\n" "#define HAVE_OGETADDRINFO 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "openlog_r" "ac_cv_func_openlog_r" if test "x$ac_cv_func_openlog_r" = xyes then : printf "%s\n" "#define HAVE_OPENLOG_R 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "pledge" "ac_cv_func_pledge" if test "x$ac_cv_func_pledge" = xyes then : printf "%s\n" "#define HAVE_PLEDGE 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "poll" "ac_cv_func_poll" if test "x$ac_cv_func_poll" = xyes then : printf "%s\n" "#define HAVE_POLL 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "ppoll" "ac_cv_func_ppoll" if test "x$ac_cv_func_ppoll" = xyes then : printf "%s\n" "#define HAVE_PPOLL 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "prctl" "ac_cv_func_prctl" if test "x$ac_cv_func_prctl" = xyes then : printf "%s\n" "#define HAVE_PRCTL 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "procctl" "ac_cv_func_procctl" if test "x$ac_cv_func_procctl" = xyes then : printf "%s\n" "#define HAVE_PROCCTL 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "pselect" "ac_cv_func_pselect" if test "x$ac_cv_func_pselect" = xyes then : printf "%s\n" "#define HAVE_PSELECT 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "pstat" "ac_cv_func_pstat" if test "x$ac_cv_func_pstat" = xyes then : printf "%s\n" "#define HAVE_PSTAT 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "raise" "ac_cv_func_raise" if test "x$ac_cv_func_raise" = xyes then : printf "%s\n" "#define HAVE_RAISE 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "readpassphrase" "ac_cv_func_readpassphrase" if test "x$ac_cv_func_readpassphrase" = xyes then : printf "%s\n" "#define HAVE_READPASSPHRASE 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "reallocarray" "ac_cv_func_reallocarray" if test "x$ac_cv_func_reallocarray" = xyes then : printf "%s\n" "#define HAVE_REALLOCARRAY 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "realpath" "ac_cv_func_realpath" if test "x$ac_cv_func_realpath" = xyes then : printf "%s\n" "#define HAVE_REALPATH 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "recvmsg" "ac_cv_func_recvmsg" if test "x$ac_cv_func_recvmsg" = xyes then : printf "%s\n" "#define HAVE_RECVMSG 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "recallocarray" "ac_cv_func_recallocarray" if test "x$ac_cv_func_recallocarray" = xyes then : printf "%s\n" "#define HAVE_RECALLOCARRAY 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "rresvport_af" "ac_cv_func_rresvport_af" if test "x$ac_cv_func_rresvport_af" = xyes then : printf "%s\n" "#define HAVE_RRESVPORT_AF 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "sendmsg" "ac_cv_func_sendmsg" if test "x$ac_cv_func_sendmsg" = xyes then : printf "%s\n" "#define HAVE_SENDMSG 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "setdtablesize" "ac_cv_func_setdtablesize" if test "x$ac_cv_func_setdtablesize" = xyes then : printf "%s\n" "#define HAVE_SETDTABLESIZE 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "setegid" "ac_cv_func_setegid" if test "x$ac_cv_func_setegid" = xyes then : printf "%s\n" "#define HAVE_SETEGID 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "setenv" "ac_cv_func_setenv" if test "x$ac_cv_func_setenv" = xyes then : printf "%s\n" "#define HAVE_SETENV 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "seteuid" "ac_cv_func_seteuid" if test "x$ac_cv_func_seteuid" = xyes then : printf "%s\n" "#define HAVE_SETEUID 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "setgroupent" "ac_cv_func_setgroupent" if test "x$ac_cv_func_setgroupent" = xyes then : printf "%s\n" "#define HAVE_SETGROUPENT 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "setgroups" "ac_cv_func_setgroups" if test "x$ac_cv_func_setgroups" = xyes then : printf "%s\n" "#define HAVE_SETGROUPS 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "setlinebuf" "ac_cv_func_setlinebuf" if test "x$ac_cv_func_setlinebuf" = xyes then : printf "%s\n" "#define HAVE_SETLINEBUF 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "setlogin" "ac_cv_func_setlogin" if test "x$ac_cv_func_setlogin" = xyes then : printf "%s\n" "#define HAVE_SETLOGIN 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "setpassent" "ac_cv_func_setpassent" if test "x$ac_cv_func_setpassent" = xyes then : printf "%s\n" "#define HAVE_SETPASSENT 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "setpcred" "ac_cv_func_setpcred" if test "x$ac_cv_func_setpcred" = xyes then : printf "%s\n" "#define HAVE_SETPCRED 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "setproctitle" "ac_cv_func_setproctitle" if test "x$ac_cv_func_setproctitle" = xyes then : printf "%s\n" "#define HAVE_SETPROCTITLE 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "setregid" "ac_cv_func_setregid" if test "x$ac_cv_func_setregid" = xyes then : printf "%s\n" "#define HAVE_SETREGID 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "setreuid" "ac_cv_func_setreuid" if test "x$ac_cv_func_setreuid" = xyes then : printf "%s\n" "#define HAVE_SETREUID 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "setrlimit" "ac_cv_func_setrlimit" if test "x$ac_cv_func_setrlimit" = xyes then : printf "%s\n" "#define HAVE_SETRLIMIT 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "setsid" "ac_cv_func_setsid" if test "x$ac_cv_func_setsid" = xyes then : printf "%s\n" "#define HAVE_SETSID 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "setvbuf" "ac_cv_func_setvbuf" if test "x$ac_cv_func_setvbuf" = xyes then : printf "%s\n" "#define HAVE_SETVBUF 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "sigaction" "ac_cv_func_sigaction" if test "x$ac_cv_func_sigaction" = xyes then : printf "%s\n" "#define HAVE_SIGACTION 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "sigvec" "ac_cv_func_sigvec" if test "x$ac_cv_func_sigvec" = xyes then : printf "%s\n" "#define HAVE_SIGVEC 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "snprintf" "ac_cv_func_snprintf" if test "x$ac_cv_func_snprintf" = xyes then : printf "%s\n" "#define HAVE_SNPRINTF 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "socketpair" "ac_cv_func_socketpair" if test "x$ac_cv_func_socketpair" = xyes then : printf "%s\n" "#define HAVE_SOCKETPAIR 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "statfs" "ac_cv_func_statfs" if test "x$ac_cv_func_statfs" = xyes then : printf "%s\n" "#define HAVE_STATFS 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "statvfs" "ac_cv_func_statvfs" if test "x$ac_cv_func_statvfs" = xyes then : printf "%s\n" "#define HAVE_STATVFS 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "strcasestr" "ac_cv_func_strcasestr" if test "x$ac_cv_func_strcasestr" = xyes then : printf "%s\n" "#define HAVE_STRCASESTR 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "strdup" "ac_cv_func_strdup" if test "x$ac_cv_func_strdup" = xyes then : printf "%s\n" "#define HAVE_STRDUP 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "strerror" "ac_cv_func_strerror" if test "x$ac_cv_func_strerror" = xyes then : printf "%s\n" "#define HAVE_STRERROR 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "strlcat" "ac_cv_func_strlcat" if test "x$ac_cv_func_strlcat" = xyes then : printf "%s\n" "#define HAVE_STRLCAT 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "strlcpy" "ac_cv_func_strlcpy" if test "x$ac_cv_func_strlcpy" = xyes then : printf "%s\n" "#define HAVE_STRLCPY 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "strmode" "ac_cv_func_strmode" if test "x$ac_cv_func_strmode" = xyes then : printf "%s\n" "#define HAVE_STRMODE 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "strndup" "ac_cv_func_strndup" if test "x$ac_cv_func_strndup" = xyes then : printf "%s\n" "#define HAVE_STRNDUP 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "strnlen" "ac_cv_func_strnlen" if test "x$ac_cv_func_strnlen" = xyes then : printf "%s\n" "#define HAVE_STRNLEN 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "strnvis" "ac_cv_func_strnvis" if test "x$ac_cv_func_strnvis" = xyes then : printf "%s\n" "#define HAVE_STRNVIS 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "strptime" "ac_cv_func_strptime" if test "x$ac_cv_func_strptime" = xyes then : printf "%s\n" "#define HAVE_STRPTIME 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "strsignal" "ac_cv_func_strsignal" if test "x$ac_cv_func_strsignal" = xyes then : printf "%s\n" "#define HAVE_STRSIGNAL 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "strtonum" "ac_cv_func_strtonum" if test "x$ac_cv_func_strtonum" = xyes then : printf "%s\n" "#define HAVE_STRTONUM 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "strtoll" "ac_cv_func_strtoll" if test "x$ac_cv_func_strtoll" = xyes then : printf "%s\n" "#define HAVE_STRTOLL 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "strtoul" "ac_cv_func_strtoul" if test "x$ac_cv_func_strtoul" = xyes then : printf "%s\n" "#define HAVE_STRTOUL 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "strtoull" "ac_cv_func_strtoull" if test "x$ac_cv_func_strtoull" = xyes then : printf "%s\n" "#define HAVE_STRTOULL 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "swap32" "ac_cv_func_swap32" if test "x$ac_cv_func_swap32" = xyes then : printf "%s\n" "#define HAVE_SWAP32 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "sysconf" "ac_cv_func_sysconf" if test "x$ac_cv_func_sysconf" = xyes then : printf "%s\n" "#define HAVE_SYSCONF 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "tcgetpgrp" "ac_cv_func_tcgetpgrp" if test "x$ac_cv_func_tcgetpgrp" = xyes then : printf "%s\n" "#define HAVE_TCGETPGRP 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "timegm" "ac_cv_func_timegm" if test "x$ac_cv_func_timegm" = xyes then : printf "%s\n" "#define HAVE_TIMEGM 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "timingsafe_bcmp" "ac_cv_func_timingsafe_bcmp" if test "x$ac_cv_func_timingsafe_bcmp" = xyes then : printf "%s\n" "#define HAVE_TIMINGSAFE_BCMP 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "truncate" "ac_cv_func_truncate" if test "x$ac_cv_func_truncate" = xyes then : printf "%s\n" "#define HAVE_TRUNCATE 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "unsetenv" "ac_cv_func_unsetenv" if test "x$ac_cv_func_unsetenv" = xyes then : printf "%s\n" "#define HAVE_UNSETENV 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "updwtmpx" "ac_cv_func_updwtmpx" if test "x$ac_cv_func_updwtmpx" = xyes then : printf "%s\n" "#define HAVE_UPDWTMPX 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "utimensat" "ac_cv_func_utimensat" if test "x$ac_cv_func_utimensat" = xyes then : printf "%s\n" "#define HAVE_UTIMENSAT 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "user_from_uid" "ac_cv_func_user_from_uid" if test "x$ac_cv_func_user_from_uid" = xyes then : printf "%s\n" "#define HAVE_USER_FROM_UID 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "usleep" "ac_cv_func_usleep" if test "x$ac_cv_func_usleep" = xyes then : printf "%s\n" "#define HAVE_USLEEP 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "vasprintf" "ac_cv_func_vasprintf" if test "x$ac_cv_func_vasprintf" = xyes then : printf "%s\n" "#define HAVE_VASPRINTF 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "vsnprintf" "ac_cv_func_vsnprintf" if test "x$ac_cv_func_vsnprintf" = xyes then : printf "%s\n" "#define HAVE_VSNPRINTF 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "waitpid" "ac_cv_func_waitpid" if test "x$ac_cv_func_waitpid" = xyes then : printf "%s\n" "#define HAVE_WAITPID 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "warn" "ac_cv_func_warn" if test "x$ac_cv_func_warn" = xyes then : printf "%s\n" "#define HAVE_WARN 1" >>confdefs.h fi ac_fn_check_decl "$LINENO" "bzero" "ac_cv_have_decl_bzero" "$ac_includes_default" "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_bzero" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_BZERO $ac_have_decl" >>confdefs.h ac_fn_check_decl "$LINENO" "memmem" "ac_cv_have_decl_memmem" "$ac_includes_default" "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_memmem" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_MEMMEM $ac_have_decl" >>confdefs.h ac_fn_c_check_func "$LINENO" "mblen" "ac_cv_func_mblen" if test "x$ac_cv_func_mblen" = xyes then : printf "%s\n" "#define HAVE_MBLEN 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "mbtowc" "ac_cv_func_mbtowc" if test "x$ac_cv_func_mbtowc" = xyes then : printf "%s\n" "#define HAVE_MBTOWC 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "nl_langinfo" "ac_cv_func_nl_langinfo" if test "x$ac_cv_func_nl_langinfo" = xyes then : printf "%s\n" "#define HAVE_NL_LANGINFO 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "wcwidth" "ac_cv_func_wcwidth" if test "x$ac_cv_func_wcwidth" = xyes then : printf "%s\n" "#define HAVE_WCWIDTH 1" >>confdefs.h fi TEST_SSH_UTF8=${TEST_SSH_UTF8:=yes} { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for utf8 locale support" >&5 printf %s "checking for utf8 locale support... " >&6; } if test "$cross_compiling" = yes then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cross compiling: assuming yes" >&5 printf "%s\n" "$as_me: WARNING: cross compiling: assuming yes" >&2;} else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main (void) { char *loc = setlocale(LC_CTYPE, "en_US.UTF-8"); if (loc != NULL) exit(0); exit(1); ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } TEST_SSH_UTF8=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { return (isblank('a')); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : printf "%s\n" "#define HAVE_ISBLANK 1" >>confdefs.h fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext disable_pkcs11= # Check whether --enable-pkcs11 was given. if test ${enable_pkcs11+y} then : enableval=$enable_pkcs11; if test "x$enableval" = "xno" ; then disable_pkcs11=1 fi fi disable_sk= # Check whether --enable-security-key was given. if test ${enable_security_key+y} then : enableval=$enable_security_key; if test "x$enableval" = "xno" ; then disable_sk=1 fi fi enable_sk_internal= # Check whether --with-security-key-builtin was given. if test ${with_security_key_builtin+y} then : withval=$with_security_key_builtin; enable_sk_internal=$withval fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing dlopen" >&5 printf %s "checking for library containing dlopen... " >&6; } if test ${ac_cv_search_dlopen+y} then : printf %s "(cached) " >&6 else $as_nop ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char dlopen (); int main (void) { return dlopen (); ; return 0; } _ACEOF for ac_lib in '' dl do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO" then : ac_cv_search_dlopen=$ac_res fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext if test ${ac_cv_search_dlopen+y} then : break fi done if test ${ac_cv_search_dlopen+y} then : else $as_nop ac_cv_search_dlopen=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_dlopen" >&5 printf "%s\n" "$ac_cv_search_dlopen" >&6; } ac_res=$ac_cv_search_dlopen if test "$ac_res" != no then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi ac_fn_c_check_func "$LINENO" "dlopen" "ac_cv_func_dlopen" if test "x$ac_cv_func_dlopen" = xyes then : printf "%s\n" "#define HAVE_DLOPEN 1" >>confdefs.h fi ac_fn_check_decl "$LINENO" "RTLD_NOW" "ac_cv_have_decl_RTLD_NOW" "#include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_RTLD_NOW" = xyes then : fi # IRIX has a const char return value for gai_strerror() for ac_func in gai_strerror do : ac_fn_c_check_func "$LINENO" "gai_strerror" "ac_cv_func_gai_strerror" if test "x$ac_cv_func_gai_strerror" = xyes then : printf "%s\n" "#define HAVE_GAI_STRERROR 1" >>confdefs.h printf "%s\n" "#define HAVE_GAI_STRERROR 1" >>confdefs.h cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include const char *gai_strerror(int); int main (void) { char *str; str = gai_strerror(0); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : printf "%s\n" "#define HAVE_CONST_GAI_STRERROR_PROTO 1" >>confdefs.h fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi done { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing nanosleep" >&5 printf %s "checking for library containing nanosleep... " >&6; } if test ${ac_cv_search_nanosleep+y} then : printf %s "(cached) " >&6 else $as_nop ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char nanosleep (); int main (void) { return nanosleep (); ; return 0; } _ACEOF for ac_lib in '' rt posix4 do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO" then : ac_cv_search_nanosleep=$ac_res fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext if test ${ac_cv_search_nanosleep+y} then : break fi done if test ${ac_cv_search_nanosleep+y} then : else $as_nop ac_cv_search_nanosleep=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_nanosleep" >&5 printf "%s\n" "$ac_cv_search_nanosleep" >&6; } ac_res=$ac_cv_search_nanosleep if test "$ac_res" != no then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" printf "%s\n" "#define HAVE_NANOSLEEP 1" >>confdefs.h fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing clock_gettime" >&5 printf %s "checking for library containing clock_gettime... " >&6; } if test ${ac_cv_search_clock_gettime+y} then : printf %s "(cached) " >&6 else $as_nop ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char clock_gettime (); int main (void) { return clock_gettime (); ; return 0; } _ACEOF for ac_lib in '' rt do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO" then : ac_cv_search_clock_gettime=$ac_res fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext if test ${ac_cv_search_clock_gettime+y} then : break fi done if test ${ac_cv_search_clock_gettime+y} then : else $as_nop ac_cv_search_clock_gettime=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_clock_gettime" >&5 printf "%s\n" "$ac_cv_search_clock_gettime" >&6; } ac_res=$ac_cv_search_clock_gettime if test "$ac_res" != no then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" printf "%s\n" "#define HAVE_CLOCK_GETTIME 1" >>confdefs.h fi ac_fn_check_decl "$LINENO" "localtime_r" "ac_cv_have_decl_localtime_r" " #include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_localtime_r" = xyes then : else $as_nop saved_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS -D_REENTRANT" unset ac_cv_have_decl_localtime_r ac_fn_check_decl "$LINENO" "localtime_r" "ac_cv_have_decl_localtime_r" " #include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_localtime_r" = xyes then : else $as_nop CPPFLAGS="$saved_CPPFLAGS" fi fi ac_fn_check_decl "$LINENO" "strsep" "ac_cv_have_decl_strsep" " #ifdef HAVE_STRING_H # include #endif " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_strsep" = xyes then : ac_fn_c_check_func "$LINENO" "strsep" "ac_cv_func_strsep" if test "x$ac_cv_func_strsep" = xyes then : printf "%s\n" "#define HAVE_STRSEP 1" >>confdefs.h fi fi ac_fn_check_decl "$LINENO" "tcsendbreak" "ac_cv_have_decl_tcsendbreak" "#include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_tcsendbreak" = xyes then : printf "%s\n" "#define HAVE_TCSENDBREAK 1" >>confdefs.h else $as_nop ac_fn_c_check_func "$LINENO" "tcsendbreak" "ac_cv_func_tcsendbreak" if test "x$ac_cv_func_tcsendbreak" = xyes then : printf "%s\n" "#define HAVE_TCSENDBREAK 1" >>confdefs.h fi fi ac_fn_check_decl "$LINENO" "h_errno" "ac_cv_have_decl_h_errno" "#include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_h_errno" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_H_ERRNO $ac_have_decl" >>confdefs.h ac_fn_check_decl "$LINENO" "SHUT_RD" "ac_cv_have_decl_SHUT_RD" " #include #include #include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_SHUT_RD" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_SHUT_RD $ac_have_decl" >>confdefs.h ac_fn_check_decl "$LINENO" "getpeereid" "ac_cv_have_decl_getpeereid" " #include #include #include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_getpeereid" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_GETPEEREID $ac_have_decl" >>confdefs.h ac_fn_check_decl "$LINENO" "O_NONBLOCK" "ac_cv_have_decl_O_NONBLOCK" " #include #ifdef HAVE_SYS_STAT_H # include #endif #ifdef HAVE_FCNTL_H # include #endif " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_O_NONBLOCK" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_O_NONBLOCK $ac_have_decl" >>confdefs.h ac_fn_check_decl "$LINENO" "ftruncate" "ac_cv_have_decl_ftruncate" " #include #include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_ftruncate" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_FTRUNCATE $ac_have_decl" >>confdefs.h ac_fn_check_decl "$LINENO" "getentropy" "ac_cv_have_decl_getentropy" " #include #include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_getentropy" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_GETENTROPY $ac_have_decl" >>confdefs.h ac_fn_check_decl "$LINENO" "readv" "ac_cv_have_decl_readv" " #include #include #include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_readv" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_READV $ac_have_decl" >>confdefs.h ac_fn_check_decl "$LINENO" "writev" "ac_cv_have_decl_writev" " #include #include #include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_writev" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_WRITEV $ac_have_decl" >>confdefs.h ac_fn_check_decl "$LINENO" "MAXSYMLINKS" "ac_cv_have_decl_MAXSYMLINKS" " #include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_MAXSYMLINKS" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_MAXSYMLINKS $ac_have_decl" >>confdefs.h ac_fn_check_decl "$LINENO" "offsetof" "ac_cv_have_decl_offsetof" " #include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_offsetof" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_OFFSETOF $ac_have_decl" >>confdefs.h # extra bits for select(2) ac_fn_check_decl "$LINENO" "howmany" "ac_cv_have_decl_howmany" " #include #include #ifdef HAVE_SYS_SYSMACROS_H #include #endif #ifdef HAVE_SYS_SELECT_H #include #endif #ifdef HAVE_SYS_TIME_H #include #endif #ifdef HAVE_UNISTD_H #include #endif " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_howmany" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_HOWMANY $ac_have_decl" >>confdefs.h ac_fn_check_decl "$LINENO" "NFDBITS" "ac_cv_have_decl_NFDBITS" " #include #include #ifdef HAVE_SYS_SYSMACROS_H #include #endif #ifdef HAVE_SYS_SELECT_H #include #endif #ifdef HAVE_SYS_TIME_H #include #endif #ifdef HAVE_UNISTD_H #include #endif " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_NFDBITS" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_NFDBITS $ac_have_decl" >>confdefs.h ac_fn_c_check_type "$LINENO" "fd_mask" "ac_cv_type_fd_mask" " #include #include #ifdef HAVE_SYS_SELECT_H #include #endif #ifdef HAVE_SYS_TIME_H #include #endif #ifdef HAVE_UNISTD_H #include #endif " if test "x$ac_cv_type_fd_mask" = xyes then : printf "%s\n" "#define HAVE_FD_MASK 1" >>confdefs.h fi for ac_func in setresuid do : ac_fn_c_check_func "$LINENO" "setresuid" "ac_cv_func_setresuid" if test "x$ac_cv_func_setresuid" = xyes then : printf "%s\n" "#define HAVE_SETRESUID 1" >>confdefs.h { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if setresuid seems to work" >&5 printf %s "checking if setresuid seems to work... " >&6; } if test "$cross_compiling" = yes then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cross compiling: not checking setresuid" >&5 printf "%s\n" "$as_me: WARNING: cross compiling: not checking setresuid" >&2;} else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include int main (void) { errno=0; setresuid(0,0,0); if (errno==ENOSYS) exit(1); else exit(0); ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else $as_nop printf "%s\n" "#define BROKEN_SETRESUID 1" >>confdefs.h { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: not implemented" >&5 printf "%s\n" "not implemented" >&6; } fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi done for ac_func in setresgid do : ac_fn_c_check_func "$LINENO" "setresgid" "ac_cv_func_setresgid" if test "x$ac_cv_func_setresgid" = xyes then : printf "%s\n" "#define HAVE_SETRESGID 1" >>confdefs.h { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if setresgid seems to work" >&5 printf %s "checking if setresgid seems to work... " >&6; } if test "$cross_compiling" = yes then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cross compiling: not checking setresuid" >&5 printf "%s\n" "$as_me: WARNING: cross compiling: not checking setresuid" >&2;} else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include int main (void) { errno=0; setresgid(0,0,0); if (errno==ENOSYS) exit(1); else exit(0); ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else $as_nop printf "%s\n" "#define BROKEN_SETRESGID 1" >>confdefs.h { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: not implemented" >&5 printf "%s\n" "not implemented" >&6; } fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi done { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for working fflush(NULL)" >&5 printf %s "checking for working fflush(NULL)... " >&6; } if test "$cross_compiling" = yes then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cross compiling: assuming working" >&5 printf "%s\n" "$as_me: WARNING: cross compiling: assuming working" >&2;} else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main (void) { fflush(NULL); exit(0); ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } printf "%s\n" "#define FFLUSH_NULL_BUG 1" >>confdefs.h fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi ac_fn_c_check_func "$LINENO" "gettimeofday" "ac_cv_func_gettimeofday" if test "x$ac_cv_func_gettimeofday" = xyes then : printf "%s\n" "#define HAVE_GETTIMEOFDAY 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "time" "ac_cv_func_time" if test "x$ac_cv_func_time" = xyes then : printf "%s\n" "#define HAVE_TIME 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "endutent" "ac_cv_func_endutent" if test "x$ac_cv_func_endutent" = xyes then : printf "%s\n" "#define HAVE_ENDUTENT 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "getutent" "ac_cv_func_getutent" if test "x$ac_cv_func_getutent" = xyes then : printf "%s\n" "#define HAVE_GETUTENT 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "getutid" "ac_cv_func_getutid" if test "x$ac_cv_func_getutid" = xyes then : printf "%s\n" "#define HAVE_GETUTID 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "getutline" "ac_cv_func_getutline" if test "x$ac_cv_func_getutline" = xyes then : printf "%s\n" "#define HAVE_GETUTLINE 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "pututline" "ac_cv_func_pututline" if test "x$ac_cv_func_pututline" = xyes then : printf "%s\n" "#define HAVE_PUTUTLINE 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "setutent" "ac_cv_func_setutent" if test "x$ac_cv_func_setutent" = xyes then : printf "%s\n" "#define HAVE_SETUTENT 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "utmpname" "ac_cv_func_utmpname" if test "x$ac_cv_func_utmpname" = xyes then : printf "%s\n" "#define HAVE_UTMPNAME 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "endutxent" "ac_cv_func_endutxent" if test "x$ac_cv_func_endutxent" = xyes then : printf "%s\n" "#define HAVE_ENDUTXENT 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "getutxent" "ac_cv_func_getutxent" if test "x$ac_cv_func_getutxent" = xyes then : printf "%s\n" "#define HAVE_GETUTXENT 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "getutxid" "ac_cv_func_getutxid" if test "x$ac_cv_func_getutxid" = xyes then : printf "%s\n" "#define HAVE_GETUTXID 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "getutxline" "ac_cv_func_getutxline" if test "x$ac_cv_func_getutxline" = xyes then : printf "%s\n" "#define HAVE_GETUTXLINE 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "getutxuser" "ac_cv_func_getutxuser" if test "x$ac_cv_func_getutxuser" = xyes then : printf "%s\n" "#define HAVE_GETUTXUSER 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "pututxline" "ac_cv_func_pututxline" if test "x$ac_cv_func_pututxline" = xyes then : printf "%s\n" "#define HAVE_PUTUTXLINE 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "setutxdb" "ac_cv_func_setutxdb" if test "x$ac_cv_func_setutxdb" = xyes then : printf "%s\n" "#define HAVE_SETUTXDB 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "setutxent" "ac_cv_func_setutxent" if test "x$ac_cv_func_setutxent" = xyes then : printf "%s\n" "#define HAVE_SETUTXENT 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "utmpxname" "ac_cv_func_utmpxname" if test "x$ac_cv_func_utmpxname" = xyes then : printf "%s\n" "#define HAVE_UTMPXNAME 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "getlastlogxbyname" "ac_cv_func_getlastlogxbyname" if test "x$ac_cv_func_getlastlogxbyname" = xyes then : printf "%s\n" "#define HAVE_GETLASTLOGXBYNAME 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "daemon" "ac_cv_func_daemon" if test "x$ac_cv_func_daemon" = xyes then : printf "%s\n" "#define HAVE_DAEMON 1" >>confdefs.h else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for daemon in -lbsd" >&5 printf %s "checking for daemon in -lbsd... " >&6; } if test ${ac_cv_lib_bsd_daemon+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lbsd $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char daemon (); int main (void) { return daemon (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_bsd_daemon=yes else $as_nop ac_cv_lib_bsd_daemon=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_bsd_daemon" >&5 printf "%s\n" "$ac_cv_lib_bsd_daemon" >&6; } if test "x$ac_cv_lib_bsd_daemon" = xyes then : LIBS="$LIBS -lbsd"; printf "%s\n" "#define HAVE_DAEMON 1" >>confdefs.h fi fi ac_fn_c_check_func "$LINENO" "getpagesize" "ac_cv_func_getpagesize" if test "x$ac_cv_func_getpagesize" = xyes then : printf "%s\n" "#define HAVE_GETPAGESIZE 1" >>confdefs.h else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for getpagesize in -lucb" >&5 printf %s "checking for getpagesize in -lucb... " >&6; } if test ${ac_cv_lib_ucb_getpagesize+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lucb $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char getpagesize (); int main (void) { return getpagesize (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_ucb_getpagesize=yes else $as_nop ac_cv_lib_ucb_getpagesize=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ucb_getpagesize" >&5 printf "%s\n" "$ac_cv_lib_ucb_getpagesize" >&6; } if test "x$ac_cv_lib_ucb_getpagesize" = xyes then : LIBS="$LIBS -lucb"; printf "%s\n" "#define HAVE_GETPAGESIZE 1" >>confdefs.h fi fi # Check for broken snprintf if test "x$ac_cv_func_snprintf" = "xyes" ; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether snprintf correctly terminates long strings" >&5 printf %s "checking whether snprintf correctly terminates long strings... " >&6; } if test "$cross_compiling" = yes then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cross compiling: Assuming working snprintf()" >&5 printf "%s\n" "$as_me: WARNING: cross compiling: Assuming working snprintf()" >&2;} else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main (void) { char b[5]; snprintf(b,5,"123456789"); exit(b[4]!='\0'); ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } printf "%s\n" "#define BROKEN_SNPRINTF 1" >>confdefs.h { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: ****** Your snprintf() function is broken, complain to your vendor" >&5 printf "%s\n" "$as_me: WARNING: ****** Your snprintf() function is broken, complain to your vendor" >&2;} fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi if test "x$ac_cv_func_snprintf" = "xyes" ; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether snprintf understands %zu" >&5 printf %s "checking whether snprintf understands %zu... " >&6; } if test "$cross_compiling" = yes then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cross compiling: Assuming working snprintf()" >&5 printf "%s\n" "$as_me: WARNING: cross compiling: Assuming working snprintf()" >&2;} else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include int main (void) { size_t a = 1, b = 2; char z[128]; snprintf(z, sizeof z, "%zu%zu", a, b); exit(strcmp(z, "12")); ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } printf "%s\n" "#define BROKEN_SNPRINTF 1" >>confdefs.h fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi # We depend on vsnprintf returning the right thing on overflow: the # number of characters it tried to create (as per SUSv3) if test "x$ac_cv_func_vsnprintf" = "xyes" ; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether vsnprintf returns correct values on overflow" >&5 printf %s "checking whether vsnprintf returns correct values on overflow... " >&6; } if test "$cross_compiling" = yes then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cross compiling: Assuming working vsnprintf()" >&5 printf "%s\n" "$as_me: WARNING: cross compiling: Assuming working vsnprintf()" >&2;} else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include int x_snprintf(char *str, size_t count, const char *fmt, ...) { size_t ret; va_list ap; va_start(ap, fmt); ret = vsnprintf(str, count, fmt, ap); va_end(ap); return ret; } int main (void) { char x[1]; if (x_snprintf(x, 1, "%s %d", "hello", 12345) != 11) return 1; if (x_snprintf(NULL, 0, "%s %d", "hello", 12345) != 11) return 1; return 0; ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } printf "%s\n" "#define BROKEN_SNPRINTF 1" >>confdefs.h { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: ****** Your vsnprintf() function is broken, complain to your vendor" >&5 printf "%s\n" "$as_me: WARNING: ****** Your vsnprintf() function is broken, complain to your vendor" >&2;} fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi # On systems where [v]snprintf is broken, but is declared in stdio, # check that the fmt argument is const char * or just char *. # This is only useful for when BROKEN_SNPRINTF { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether snprintf can declare const char *fmt" >&5 printf %s "checking whether snprintf can declare const char *fmt... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int snprintf(char *a, size_t b, const char *c, ...) { return 0; } int main (void) { snprintf(0, 0, 0); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } printf "%s\n" "#define SNPRINTF_CONST const" >>confdefs.h else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } printf "%s\n" "#define SNPRINTF_CONST /* not const */" >>confdefs.h fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext # Check for missing getpeereid (or equiv) support NO_PEERCHECK="" if test "x$ac_cv_func_getpeereid" != "xyes" -a "x$ac_cv_func_getpeerucred" != "xyes"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether system supports SO_PEERCRED getsockopt" >&5 printf %s "checking whether system supports SO_PEERCRED getsockopt... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main (void) { int i = SO_PEERCRED; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } printf "%s\n" "#define HAVE_SO_PEERCRED 1" >>confdefs.h else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } NO_PEERCHECK=1 fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi if test ! -z "$check_for_openpty_ctty_bug"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if openpty correctly handles controlling tty" >&5 printf %s "checking if openpty correctly handles controlling tty... " >&6; } if test "$cross_compiling" = yes then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: cross-compiling, assuming yes" >&5 printf "%s\n" "cross-compiling, assuming yes" >&6; } else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #ifdef HAVE_PTY_H # include #endif #include #include #include int main (void) { pid_t pid; int fd, ptyfd, ttyfd, status; pid = fork(); if (pid < 0) { /* failed */ exit(1); } else if (pid > 0) { /* parent */ waitpid(pid, &status, 0); if (WIFEXITED(status)) exit(WEXITSTATUS(status)); else exit(2); } else { /* child */ close(0); close(1); close(2); setsid(); openpty(&ptyfd, &ttyfd, NULL, NULL, NULL); fd = open("/dev/tty", O_RDWR | O_NOCTTY); if (fd >= 0) exit(3); /* Acquired ctty: broken */ else exit(0); /* Did not acquire ctty: OK */ } ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } printf "%s\n" "#define SSHD_ACQUIRES_CTTY 1" >>confdefs.h fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi if test "x$ac_cv_func_getaddrinfo" = "xyes" && \ test "x$check_for_hpux_broken_getaddrinfo" = "x1"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if getaddrinfo seems to work" >&5 printf %s "checking if getaddrinfo seems to work... " >&6; } if test "$cross_compiling" = yes then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: cross-compiling, assuming yes" >&5 printf "%s\n" "cross-compiling, assuming yes" >&6; } else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include #include #include #define TEST_PORT "2222" int main (void) { int err, sock; struct addrinfo *gai_ai, *ai, hints; char ntop[NI_MAXHOST], strport[NI_MAXSERV], *name = NULL; memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; err = getaddrinfo(name, TEST_PORT, &hints, &gai_ai); if (err != 0) { fprintf(stderr, "getaddrinfo failed (%s)", gai_strerror(err)); exit(1); } for (ai = gai_ai; ai != NULL; ai = ai->ai_next) { if (ai->ai_family != AF_INET6) continue; err = getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV); if (err != 0) { if (err == EAI_SYSTEM) perror("getnameinfo EAI_SYSTEM"); else fprintf(stderr, "getnameinfo failed: %s\n", gai_strerror(err)); exit(2); } sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (sock < 0) perror("socket"); if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { if (errno == EBADF) exit(3); } } exit(0); ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } printf "%s\n" "#define BROKEN_GETADDRINFO 1" >>confdefs.h fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi if test "x$ac_cv_func_getaddrinfo" = "xyes" && \ test "x$check_for_aix_broken_getaddrinfo" = "x1"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if getaddrinfo seems to work" >&5 printf %s "checking if getaddrinfo seems to work... " >&6; } if test "$cross_compiling" = yes then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: cross-compiling, assuming no" >&5 printf "%s\n" "cross-compiling, assuming no" >&6; } else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include #include #include #define TEST_PORT "2222" int main (void) { int err, sock; struct addrinfo *gai_ai, *ai, hints; char ntop[NI_MAXHOST], strport[NI_MAXSERV], *name = NULL; memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; err = getaddrinfo(name, TEST_PORT, &hints, &gai_ai); if (err != 0) { fprintf(stderr, "getaddrinfo failed (%s)", gai_strerror(err)); exit(1); } for (ai = gai_ai; ai != NULL; ai = ai->ai_next) { if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) continue; err = getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV); if (ai->ai_family == AF_INET && err != 0) { perror("getnameinfo"); exit(2); } } exit(0); ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } printf "%s\n" "#define AIX_GETNAMEINFO_HACK 1" >>confdefs.h else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } printf "%s\n" "#define BROKEN_GETADDRINFO 1" >>confdefs.h fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi if test "x$ac_cv_func_getaddrinfo" = "xyes"; then ac_fn_check_decl "$LINENO" "AI_NUMERICSERV" "ac_cv_have_decl_AI_NUMERICSERV" "#include #include #include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_AI_NUMERICSERV" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_AI_NUMERICSERV $ac_have_decl" >>confdefs.h fi if test "x$check_for_conflicting_getspnam" = "x1"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for conflicting getspnam in shadow.h" >&5 printf %s "checking for conflicting getspnam in shadow.h... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main (void) { exit(0); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } printf "%s\n" "#define GETSPNAM_CONFLICTING_DEFS 1" >>confdefs.h fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi if test "x$ac_cv_func_strnvis" = "xyes"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for working strnvis" >&5 printf %s "checking for working strnvis... " >&6; } if test "$cross_compiling" = yes then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cross compiling: assuming broken" >&5 printf "%s\n" "$as_me: WARNING: cross compiling: assuming broken" >&2;} printf "%s\n" "#define BROKEN_STRNVIS 1" >>confdefs.h else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include #include static void sighandler(int sig) { _exit(1); } int main (void) { char dst[16]; signal(SIGSEGV, sighandler); if (strnvis(dst, "src", 4, 0) && strcmp(dst, "src") == 0) exit(0); exit(1) ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } printf "%s\n" "#define BROKEN_STRNVIS 1" >>confdefs.h fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if SA_RESTARTed signals interrupt select()" >&5 printf %s "checking if SA_RESTARTed signals interrupt select()... " >&6; } if test "$cross_compiling" = yes then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cross compiling: assuming yes" >&5 printf "%s\n" "$as_me: WARNING: cross compiling: assuming yes" >&2;} else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef HAVE_SYS_SELECT # include #endif #include #include #include #include #include static void sighandler(int sig) { } int main (void) { int r; pid_t pid; struct sigaction sa; sa.sa_handler = sighandler; sa.sa_flags = SA_RESTART; (void)sigaction(SIGTERM, &sa, NULL); if ((pid = fork()) == 0) { /* child */ pid = getppid(); sleep(1); kill(pid, SIGTERM); sleep(1); if (getppid() == pid) /* if parent did not exit, shoot it */ kill(pid, SIGKILL); exit(0); } else { /* parent */ r = select(0, NULL, NULL, NULL, NULL); } exit(r == -1 ? 0 : 1); ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } printf "%s\n" "#define NO_SA_RESTART 1" >>confdefs.h fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi for ac_func in getpgrp do : ac_fn_c_check_func "$LINENO" "getpgrp" "ac_cv_func_getpgrp" if test "x$ac_cv_func_getpgrp" = xyes then : printf "%s\n" "#define HAVE_GETPGRP 1" >>confdefs.h { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if getpgrp accepts zero args" >&5 printf %s "checking if getpgrp accepts zero args... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default int main (void) { getpgrp(); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } printf "%s\n" "#define GETPGRP_VOID 1" >>confdefs.h else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } printf "%s\n" "#define GETPGRP_VOID 0" >>confdefs.h fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi done # Search for OpenSSL saved_CPPFLAGS="$CPPFLAGS" saved_LDFLAGS="$LDFLAGS" openssl_bin_PATH="$PATH" # Check whether --with-ssl-dir was given. if test ${with_ssl_dir+y} then : withval=$with_ssl_dir; if test "x$openssl" = "xno" ; then as_fn_error $? "cannot use --with-ssl-dir when OpenSSL disabled" "$LINENO" 5 fi if test "x$withval" != "xno" ; then case "$withval" in # Relative paths ./*|../*) withval="`pwd`/$withval" esac if test -d "$withval/lib"; then libcrypto_path="${withval}/lib" elif test -d "$withval/lib64"; then libcrypto_path="$withval/lib64" else # Built but not installed libcrypto_path="${withval}" fi if test -n "${rpath_opt}"; then LDFLAGS="-L${libcrypto_path} ${rpath_opt}${libcrypto_path} ${LDFLAGS}" else LDFLAGS="-L${libcrypto_path} ${LDFLAGS}" fi if test -d "$withval/include"; then CPPFLAGS="-I${withval}/include ${CPPFLAGS}" else CPPFLAGS="-I${withval} ${CPPFLAGS}" fi openssl_bin_PATH="${PATH}${PATH_SEPARATOR}${withval}/bin${PATH_SEPARATOR}${withval}/apps" fi fi for ac_prog in openssl do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_openssl_bin+y} then : printf %s "(cached) " >&6 else $as_nop case $openssl_bin in [\\/]* | ?:[\\/]*) ac_cv_path_openssl_bin="$openssl_bin" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $openssl_bin_PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_path_openssl_bin="$as_dir$ac_word$ac_exec_ext" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi openssl_bin=$ac_cv_path_openssl_bin if test -n "$openssl_bin"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $openssl_bin" >&5 printf "%s\n" "$openssl_bin" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -n "$openssl_bin" && break done OPENSSL_BIN=${openssl_bin} # Check whether --with-openssl-header-check was given. if test ${with_openssl_header_check+y} then : withval=$with_openssl_header_check; if test "x$withval" = "xno" ; then openssl_check_nonfatal=1 fi fi openssl_engine=no # Check whether --with-ssl-engine was given. if test ${with_ssl_engine+y} then : withval=$with_ssl_engine; if test "x$withval" != "xno" ; then if test "x$openssl" = "xno" ; then as_fn_error $? "cannot use --with-ssl-engine when OpenSSL disabled" "$LINENO" 5 fi openssl_engine=yes fi fi nocrypto_saved_LIBS="$LIBS" if test "x$openssl" = "xyes" ; then LIBS="-lcrypto $LIBS" CHANNELLIBS="-lcrypto $CHANNELLIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char RAND_add (); int main (void) { return RAND_add (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : else $as_nop as_fn_error $? "*** working libcrypto not found, check config.log" "$LINENO" 5 fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext ac_fn_c_check_header_compile "$LINENO" "openssl/opensslv.h" "ac_cv_header_openssl_opensslv_h" "$ac_includes_default" if test "x$ac_cv_header_openssl_opensslv_h" = xyes then : else $as_nop as_fn_error $? "*** OpenSSL headers missing - please install first or check config.log ***" "$LINENO" 5 fi # Determine OpenSSL header version { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking OpenSSL header version" >&5 printf %s "checking OpenSSL header version... " >&6; } if test "$cross_compiling" = yes then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cross compiling: not checking" >&5 printf "%s\n" "$as_me: WARNING: cross compiling: not checking" >&2;} else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include #define DATA "conftest.sslincver" int main (void) { FILE *fd; int rc; fd = fopen(DATA,"w"); if(fd == NULL) exit(1); if ((rc = fprintf(fd, "%08lx (%s)\n", (unsigned long)OPENSSL_VERSION_NUMBER, OPENSSL_VERSION_TEXT)) < 0) exit(1); exit(0); ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO" then : ssl_header_ver=`cat conftest.sslincver` { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ssl_header_ver" >&5 printf "%s\n" "$ssl_header_ver" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: not found" >&5 printf "%s\n" "not found" >&6; } as_fn_error $? "OpenSSL version header not found." "$LINENO" 5 fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi # Determining OpenSSL library version is version dependent. ac_fn_c_check_func "$LINENO" "OpenSSL_version" "ac_cv_func_OpenSSL_version" if test "x$ac_cv_func_OpenSSL_version" = xyes then : printf "%s\n" "#define HAVE_OPENSSL_VERSION 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "OpenSSL_version_num" "ac_cv_func_OpenSSL_version_num" if test "x$ac_cv_func_OpenSSL_version_num" = xyes then : printf "%s\n" "#define HAVE_OPENSSL_VERSION_NUM 1" >>confdefs.h fi # Determine OpenSSL library version { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking OpenSSL library version" >&5 printf %s "checking OpenSSL library version... " >&6; } if test "$cross_compiling" = yes then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cross compiling: not checking" >&5 printf "%s\n" "$as_me: WARNING: cross compiling: not checking" >&2;} else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include #include #define DATA "conftest.ssllibver" int main (void) { FILE *f; /* We need these legacy bits to warn for old libcrypto */ #ifndef OPENSSL_VERSION # define OPENSSL_VERSION SSLEAY_VERSION #endif #ifndef HAVE_OPENSSL_VERSION # define OpenSSL_version SSLeay_version #endif #ifndef HAVE_OPENSSL_VERSION_NUM # define OpenSSL_version_num SSLeay #endif if ((f = fopen(DATA, "w")) == NULL) exit(1); if (fprintf(f, "%08lx (%s)", (unsigned long)OpenSSL_version_num(), OpenSSL_version(OPENSSL_VERSION)) < 0) exit(1); #ifdef LIBRESSL_VERSION_NUMBER if (fprintf(f, " libressl-%08lx", LIBRESSL_VERSION_NUMBER) < 0) exit(1); #endif if (fputc('\n', f) == EOF || fclose(f) == EOF) exit(1); exit(0); ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO" then : sslver=`cat conftest.ssllibver` ssl_showver=`echo "$sslver" | sed 's/ libressl-.*//'` # Check version is supported. case "$sslver" in 100*|10100*) # 1.0.x, 1.1.0x as_fn_error $? "OpenSSL >= 1.1.1 required (have \"$ssl_showver\")" "$LINENO" 5 ;; 101*) ;; # 1.1.x 200*) # LibreSSL lver=`echo "$sslver" | sed 's/.*libressl-//'` case "$lver" in 2*|300*) # 2.x, 3.0.0 as_fn_error $? "LibreSSL >= 3.1.0 required (have \"$ssl_showver\")" "$LINENO" 5 ;; *) ;; # Assume all other versions are good. esac ;; 300*) # OpenSSL 3; we use the 1.1x API CPPFLAGS="$CPPFLAGS -DOPENSSL_API_COMPAT=0x10100000L" ;; 301*|302*) # OpenSSL development branch; request 1.1x API CPPFLAGS="$CPPFLAGS -DOPENSSL_API_COMPAT=0x10100000L" ;; *) as_fn_error $? "Unknown/unsupported OpenSSL version (\"$ssl_showver\")" "$LINENO" 5 ;; esac { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ssl_showver" >&5 printf "%s\n" "$ssl_showver" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: not found" >&5 printf "%s\n" "not found" >&6; } as_fn_error $? "OpenSSL library not found." "$LINENO" 5 fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi case "$host" in x86_64-*) case "$sslver" in 3000004*) as_fn_error $? "OpenSSL 3.0.4 has a potential RCE in its RSA implementation (CVE-2022-2274)" "$LINENO" 5 ;; esac esac # Sanity check OpenSSL headers { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether OpenSSL's headers match the library" >&5 printf %s "checking whether OpenSSL's headers match the library... " >&6; } if test "$cross_compiling" = yes then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cross compiling: not checking" >&5 printf "%s\n" "$as_me: WARNING: cross compiling: not checking" >&2;} else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include int main (void) { exit(OpenSSL_version_num() == OPENSSL_VERSION_NUMBER ? 0 : 1); ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } if test "x$openssl_check_nonfatal" = "x"; then as_fn_error $? "Your OpenSSL headers do not match your library. Check config.log for details. If you are sure your installation is consistent, you can disable the check by running \"./configure --without-openssl-header-check\". Also see contrib/findssl.sh for help identifying header/library mismatches. " "$LINENO" 5 else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: Your OpenSSL headers do not match your library. Check config.log for details. Also see contrib/findssl.sh for help identifying header/library mismatches." >&5 printf "%s\n" "$as_me: WARNING: Your OpenSSL headers do not match your library. Check config.log for details. Also see contrib/findssl.sh for help identifying header/library mismatches." >&2;} fi fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if programs using OpenSSL functions will link" >&5 printf %s "checking if programs using OpenSSL functions will link... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { ERR_load_crypto_strings(); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } LIBS="$LIBS -ldl" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if programs using OpenSSL need -ldl" >&5 printf %s "checking if programs using OpenSSL need -ldl... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { ERR_load_crypto_strings(); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } CHANNELLIBS="$CHANNELLIBS -ldl" else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext ac_fn_c_check_func "$LINENO" "BN_is_prime_ex" "ac_cv_func_BN_is_prime_ex" if test "x$ac_cv_func_BN_is_prime_ex" = xyes then : printf "%s\n" "#define HAVE_BN_IS_PRIME_EX 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "DES_crypt" "ac_cv_func_DES_crypt" if test "x$ac_cv_func_DES_crypt" = xyes then : printf "%s\n" "#define HAVE_DES_CRYPT 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "DSA_generate_parameters_ex" "ac_cv_func_DSA_generate_parameters_ex" if test "x$ac_cv_func_DSA_generate_parameters_ex" = xyes then : printf "%s\n" "#define HAVE_DSA_GENERATE_PARAMETERS_EX 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "EVP_DigestFinal_ex" "ac_cv_func_EVP_DigestFinal_ex" if test "x$ac_cv_func_EVP_DigestFinal_ex" = xyes then : printf "%s\n" "#define HAVE_EVP_DIGESTFINAL_EX 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "EVP_DigestInit_ex" "ac_cv_func_EVP_DigestInit_ex" if test "x$ac_cv_func_EVP_DigestInit_ex" = xyes then : printf "%s\n" "#define HAVE_EVP_DIGESTINIT_EX 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "EVP_MD_CTX_cleanup" "ac_cv_func_EVP_MD_CTX_cleanup" if test "x$ac_cv_func_EVP_MD_CTX_cleanup" = xyes then : printf "%s\n" "#define HAVE_EVP_MD_CTX_CLEANUP 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "EVP_MD_CTX_copy_ex" "ac_cv_func_EVP_MD_CTX_copy_ex" if test "x$ac_cv_func_EVP_MD_CTX_copy_ex" = xyes then : printf "%s\n" "#define HAVE_EVP_MD_CTX_COPY_EX 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "EVP_MD_CTX_init" "ac_cv_func_EVP_MD_CTX_init" if test "x$ac_cv_func_EVP_MD_CTX_init" = xyes then : printf "%s\n" "#define HAVE_EVP_MD_CTX_INIT 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "HMAC_CTX_init" "ac_cv_func_HMAC_CTX_init" if test "x$ac_cv_func_HMAC_CTX_init" = xyes then : printf "%s\n" "#define HAVE_HMAC_CTX_INIT 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "RSA_generate_key_ex" "ac_cv_func_RSA_generate_key_ex" if test "x$ac_cv_func_RSA_generate_key_ex" = xyes then : printf "%s\n" "#define HAVE_RSA_GENERATE_KEY_EX 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "RSA_get_default_method" "ac_cv_func_RSA_get_default_method" if test "x$ac_cv_func_RSA_get_default_method" = xyes then : printf "%s\n" "#define HAVE_RSA_GET_DEFAULT_METHOD 1" >>confdefs.h fi # OpenSSL_add_all_algorithms may be a macro. ac_fn_c_check_func "$LINENO" "OpenSSL_add_all_algorithms" "ac_cv_func_OpenSSL_add_all_algorithms" if test "x$ac_cv_func_OpenSSL_add_all_algorithms" = xyes then : printf "%s\n" "#define HAVE_OPENSSL_ADD_ALL_ALGORITHMS 1" >>confdefs.h else $as_nop ac_fn_check_decl "$LINENO" "OpenSSL_add_all_algorithms" "ac_cv_have_decl_OpenSSL_add_all_algorithms" "#include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_OpenSSL_add_all_algorithms" = xyes then : printf "%s\n" "#define HAVE_OPENSSL_ADD_ALL_ALGORITHMS 1" >>confdefs.h fi fi # LibreSSL/OpenSSL API differences ac_fn_c_check_func "$LINENO" "EVP_CIPHER_CTX_iv" "ac_cv_func_EVP_CIPHER_CTX_iv" if test "x$ac_cv_func_EVP_CIPHER_CTX_iv" = xyes then : printf "%s\n" "#define HAVE_EVP_CIPHER_CTX_IV 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "EVP_CIPHER_CTX_iv_noconst" "ac_cv_func_EVP_CIPHER_CTX_iv_noconst" if test "x$ac_cv_func_EVP_CIPHER_CTX_iv_noconst" = xyes then : printf "%s\n" "#define HAVE_EVP_CIPHER_CTX_IV_NOCONST 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "EVP_CIPHER_CTX_get_iv" "ac_cv_func_EVP_CIPHER_CTX_get_iv" if test "x$ac_cv_func_EVP_CIPHER_CTX_get_iv" = xyes then : printf "%s\n" "#define HAVE_EVP_CIPHER_CTX_GET_IV 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "EVP_CIPHER_CTX_get_updated_iv" "ac_cv_func_EVP_CIPHER_CTX_get_updated_iv" if test "x$ac_cv_func_EVP_CIPHER_CTX_get_updated_iv" = xyes then : printf "%s\n" "#define HAVE_EVP_CIPHER_CTX_GET_UPDATED_IV 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "EVP_CIPHER_CTX_set_iv" "ac_cv_func_EVP_CIPHER_CTX_set_iv" if test "x$ac_cv_func_EVP_CIPHER_CTX_set_iv" = xyes then : printf "%s\n" "#define HAVE_EVP_CIPHER_CTX_SET_IV 1" >>confdefs.h fi if test "x$openssl_engine" = "xyes" ; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for OpenSSL ENGINE support" >&5 printf %s "checking for OpenSSL ENGINE support... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { ENGINE_load_builtin_engines(); ENGINE_register_all_complete(); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } printf "%s\n" "#define USE_OPENSSL_ENGINE 1" >>confdefs.h else $as_nop as_fn_error $? "OpenSSL ENGINE support not found" "$LINENO" 5 fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi # Check for OpenSSL without EVP_aes_{192,256}_cbc { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether OpenSSL has crippled AES support" >&5 printf %s "checking whether OpenSSL has crippled AES support... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include int main (void) { exit(EVP_aes_192_cbc() == NULL || EVP_aes_256_cbc() == NULL); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } printf "%s\n" "#define OPENSSL_LOBOTOMISED_AES 1" >>confdefs.h fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if EVP_DigestUpdate returns an int" >&5 printf %s "checking if EVP_DigestUpdate returns an int... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include int main (void) { if(EVP_DigestUpdate(NULL, NULL,0)) exit(0); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } printf "%s\n" "#define OPENSSL_EVP_DIGESTUPDATE_VOID 1" >>confdefs.h fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext # Check for various EVP support in OpenSSL ac_fn_c_check_func "$LINENO" "EVP_sha256" "ac_cv_func_EVP_sha256" if test "x$ac_cv_func_EVP_sha256" = xyes then : printf "%s\n" "#define HAVE_EVP_SHA256 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "EVP_sha384" "ac_cv_func_EVP_sha384" if test "x$ac_cv_func_EVP_sha384" = xyes then : printf "%s\n" "#define HAVE_EVP_SHA384 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "EVP_sha512" "ac_cv_func_EVP_sha512" if test "x$ac_cv_func_EVP_sha512" = xyes then : printf "%s\n" "#define HAVE_EVP_SHA512 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "EVP_chacha20" "ac_cv_func_EVP_chacha20" if test "x$ac_cv_func_EVP_chacha20" = xyes then : printf "%s\n" "#define HAVE_EVP_CHACHA20 1" >>confdefs.h fi # Check complete ECC support in OpenSSL { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether OpenSSL has NID_X9_62_prime256v1" >&5 printf %s "checking whether OpenSSL has NID_X9_62_prime256v1... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include #include #include int main (void) { EC_KEY *e = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); const EVP_MD *m = EVP_sha256(); /* We need this too */ ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } enable_nistp256=1 else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether OpenSSL has NID_secp384r1" >&5 printf %s "checking whether OpenSSL has NID_secp384r1... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include #include #include int main (void) { EC_KEY *e = EC_KEY_new_by_curve_name(NID_secp384r1); const EVP_MD *m = EVP_sha384(); /* We need this too */ ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } enable_nistp384=1 else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether OpenSSL has NID_secp521r1" >&5 printf %s "checking whether OpenSSL has NID_secp521r1... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include #include #include int main (void) { EC_KEY *e = EC_KEY_new_by_curve_name(NID_secp521r1); const EVP_MD *m = EVP_sha512(); /* We need this too */ ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if OpenSSL's NID_secp521r1 is functional" >&5 printf %s "checking if OpenSSL's NID_secp521r1 is functional... " >&6; } if test "$cross_compiling" = yes then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cross-compiling: assuming yes" >&5 printf "%s\n" "$as_me: WARNING: cross-compiling: assuming yes" >&2;} enable_nistp521=1 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include #include #include #include int main (void) { EC_KEY *e = EC_KEY_new_by_curve_name(NID_secp521r1); const EVP_MD *m = EVP_sha512(); /* We need this too */ exit(e == NULL || m == NULL); ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } enable_nistp521=1 else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext if test x$enable_nistp256 = x1 || test x$enable_nistp384 = x1 || \ test x$enable_nistp521 = x1; then printf "%s\n" "#define OPENSSL_HAS_ECC 1" >>confdefs.h ac_fn_c_check_func "$LINENO" "EC_KEY_METHOD_new" "ac_cv_func_EC_KEY_METHOD_new" if test "x$ac_cv_func_EC_KEY_METHOD_new" = xyes then : printf "%s\n" "#define HAVE_EC_KEY_METHOD_NEW 1" >>confdefs.h fi openssl_ecc=yes else openssl_ecc=no fi if test x$enable_nistp256 = x1; then printf "%s\n" "#define OPENSSL_HAS_NISTP256 1" >>confdefs.h else unsupported_algorithms="$unsupported_algorithms \ ecdsa-sha2-nistp256 \ ecdh-sha2-nistp256 \ ecdsa-sha2-nistp256-cert-v01@openssh.com" fi if test x$enable_nistp384 = x1; then printf "%s\n" "#define OPENSSL_HAS_NISTP384 1" >>confdefs.h else unsupported_algorithms="$unsupported_algorithms \ ecdsa-sha2-nistp384 \ ecdh-sha2-nistp384 \ ecdsa-sha2-nistp384-cert-v01@openssh.com" fi if test x$enable_nistp521 = x1; then printf "%s\n" "#define OPENSSL_HAS_NISTP521 1" >>confdefs.h else unsupported_algorithms="$unsupported_algorithms \ ecdh-sha2-nistp521 \ ecdsa-sha2-nistp521 \ ecdsa-sha2-nistp521-cert-v01@openssh.com" fi fi # PKCS11/U2F depend on OpenSSL and dlopen(). enable_pkcs11=yes enable_sk=yes if test "x$openssl" != "xyes" ; then enable_pkcs11="disabled; missing libcrypto" fi if test "x$ac_cv_func_dlopen" != "xyes" ; then enable_pkcs11="disabled; missing dlopen(3)" enable_sk="disabled; missing dlopen(3)" fi if test "x$ac_cv_have_decl_RTLD_NOW" != "xyes" ; then enable_pkcs11="disabled; missing RTLD_NOW" enable_sk="disabled; missing RTLD_NOW" fi if test ! -z "$disable_pkcs11" ; then enable_pkcs11="disabled by user" fi if test ! -z "$disable_sk" ; then enable_sk="disabled by user" fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether to enable PKCS11" >&5 printf %s "checking whether to enable PKCS11... " >&6; } if test "x$enable_pkcs11" = "xyes" ; then printf "%s\n" "#define ENABLE_PKCS11 /**/" >>confdefs.h fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $enable_pkcs11" >&5 printf "%s\n" "$enable_pkcs11" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether to enable U2F" >&5 printf %s "checking whether to enable U2F... " >&6; } if test "x$enable_sk" = "xyes" ; then printf "%s\n" "#define ENABLE_SK /**/" >>confdefs.h SK_DUMMY_LIBRARY=regress/misc/sk-dummy/sk-dummy.so else # Do not try to build sk-dummy library. SK_DUMMY_LIBRARY="" fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $enable_sk" >&5 printf "%s\n" "$enable_sk" >&6; } # Now check for built-in security key support. if test "x$enable_sk" = "xyes" -a "x$enable_sk_internal" != "xno" ; then use_pkgconfig_for_libfido2= if test "x$PKGCONFIG" != "xno"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $PKGCONFIG knows about libfido2" >&5 printf %s "checking if $PKGCONFIG knows about libfido2... " >&6; } if "$PKGCONFIG" libfido2; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } use_pkgconfig_for_libfido2=yes else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test "x$use_pkgconfig_for_libfido2" = "xyes"; then LIBFIDO2=`$PKGCONFIG --libs libfido2` CPPFLAGS="$CPPFLAGS `$PKGCONFIG --cflags libfido2`" else LIBFIDO2="-lfido2 -lcbor" fi OTHERLIBS=`echo $LIBFIDO2 | sed 's/-lfido2//'` fido2_error= { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for fido_init in -lfido2" >&5 printf %s "checking for fido_init in -lfido2... " >&6; } if test ${ac_cv_lib_fido2_fido_init+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lfido2 $OTHERLIBS $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char fido_init (); int main (void) { return fido_init (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_fido2_fido_init=yes else $as_nop ac_cv_lib_fido2_fido_init=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_fido2_fido_init" >&5 printf "%s\n" "$ac_cv_lib_fido2_fido_init" >&6; } if test "x$ac_cv_lib_fido2_fido_init" = xyes then : else $as_nop fido2_error="missing/unusable libfido2" fi ac_fn_c_check_header_compile "$LINENO" "fido.h" "ac_cv_header_fido_h" "$ac_includes_default" if test "x$ac_cv_header_fido_h" = xyes then : else $as_nop fido2_error="missing fido.h from libfido2" fi ac_fn_c_check_header_compile "$LINENO" "fido/credman.h" "ac_cv_header_fido_credman_h" " #include " if test "x$ac_cv_header_fido_credman_h" = xyes then : else $as_nop fido2_error="missing fido/credman.h from libfido2" fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for usable libfido2 installation" >&5 printf %s "checking for usable libfido2 installation... " >&6; } if test ! -z "$fido2_error" ; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $fido2_error" >&5 printf "%s\n" "$fido2_error" >&6; } if test "x$enable_sk_internal" = "xyes" ; then as_fn_error $? "No usable libfido2 library/headers found" "$LINENO" 5 fi LIBFIDO2="" else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } printf "%s\n" "#define ENABLE_SK_INTERNAL /**/" >>confdefs.h enable_sk="built-in" saved_LIBS="$LIBS" LIBS="$LIBFIDO2 $LIBS" ac_fn_c_check_func "$LINENO" "fido_assert_set_clientdata" "ac_cv_func_fido_assert_set_clientdata" if test "x$ac_cv_func_fido_assert_set_clientdata" = xyes then : printf "%s\n" "#define HAVE_FIDO_ASSERT_SET_CLIENTDATA 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "fido_cred_prot" "ac_cv_func_fido_cred_prot" if test "x$ac_cv_func_fido_cred_prot" = xyes then : printf "%s\n" "#define HAVE_FIDO_CRED_PROT 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "fido_cred_set_prot" "ac_cv_func_fido_cred_set_prot" if test "x$ac_cv_func_fido_cred_set_prot" = xyes then : printf "%s\n" "#define HAVE_FIDO_CRED_SET_PROT 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "fido_cred_set_clientdata" "ac_cv_func_fido_cred_set_clientdata" if test "x$ac_cv_func_fido_cred_set_clientdata" = xyes then : printf "%s\n" "#define HAVE_FIDO_CRED_SET_CLIENTDATA 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "fido_dev_get_touch_begin" "ac_cv_func_fido_dev_get_touch_begin" if test "x$ac_cv_func_fido_dev_get_touch_begin" = xyes then : printf "%s\n" "#define HAVE_FIDO_DEV_GET_TOUCH_BEGIN 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "fido_dev_get_touch_status" "ac_cv_func_fido_dev_get_touch_status" if test "x$ac_cv_func_fido_dev_get_touch_status" = xyes then : printf "%s\n" "#define HAVE_FIDO_DEV_GET_TOUCH_STATUS 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "fido_dev_supports_cred_prot" "ac_cv_func_fido_dev_supports_cred_prot" if test "x$ac_cv_func_fido_dev_supports_cred_prot" = xyes then : printf "%s\n" "#define HAVE_FIDO_DEV_SUPPORTS_CRED_PROT 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "fido_dev_is_winhello" "ac_cv_func_fido_dev_is_winhello" if test "x$ac_cv_func_fido_dev_is_winhello" = xyes then : printf "%s\n" "#define HAVE_FIDO_DEV_IS_WINHELLO 1" >>confdefs.h fi LIBS="$saved_LIBS" fi fi ac_fn_c_check_func "$LINENO" "arc4random" "ac_cv_func_arc4random" if test "x$ac_cv_func_arc4random" = xyes then : printf "%s\n" "#define HAVE_ARC4RANDOM 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "arc4random_buf" "ac_cv_func_arc4random_buf" if test "x$ac_cv_func_arc4random_buf" = xyes then : printf "%s\n" "#define HAVE_ARC4RANDOM_BUF 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "arc4random_stir" "ac_cv_func_arc4random_stir" if test "x$ac_cv_func_arc4random_stir" = xyes then : printf "%s\n" "#define HAVE_ARC4RANDOM_STIR 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "arc4random_uniform" "ac_cv_func_arc4random_uniform" if test "x$ac_cv_func_arc4random_uniform" = xyes then : printf "%s\n" "#define HAVE_ARC4RANDOM_UNIFORM 1" >>confdefs.h fi ### Configure cryptographic random number support # Check whether OpenSSL seeds itself if test "x$openssl" = "xyes" ; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether OpenSSL's PRNG is internally seeded" >&5 printf %s "checking whether OpenSSL's PRNG is internally seeded... " >&6; } if test "$cross_compiling" = yes then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cross compiling: assuming yes" >&5 printf "%s\n" "$as_me: WARNING: cross compiling: assuming yes" >&2;} # This is safe, since we will fatal() at runtime if # OpenSSL is not seeded correctly. OPENSSL_SEEDS_ITSELF=yes else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include int main (void) { exit(RAND_status() == 1 ? 0 : 1); ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO" then : OPENSSL_SEEDS_ITSELF=yes { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi # PRNGD TCP socket # Check whether --with-prngd-port was given. if test ${with_prngd_port+y} then : withval=$with_prngd_port; case "$withval" in no) withval="" ;; [0-9]*) ;; *) as_fn_error $? "You must specify a numeric port number for --with-prngd-port" "$LINENO" 5 ;; esac if test ! -z "$withval" ; then PRNGD_PORT="$withval" printf "%s\n" "#define PRNGD_PORT $PRNGD_PORT" >>confdefs.h fi fi # PRNGD Unix domain socket # Check whether --with-prngd-socket was given. if test ${with_prngd_socket+y} then : withval=$with_prngd_socket; case "$withval" in yes) withval="/var/run/egd-pool" ;; no) withval="" ;; /*) ;; *) as_fn_error $? "You must specify an absolute path to the entropy socket" "$LINENO" 5 ;; esac if test ! -z "$withval" ; then if test ! -z "$PRNGD_PORT" ; then as_fn_error $? "You may not specify both a PRNGD/EGD port and socket" "$LINENO" 5 fi if test ! -r "$withval" ; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: Entropy socket is not readable" >&5 printf "%s\n" "$as_me: WARNING: Entropy socket is not readable" >&2;} fi PRNGD_SOCKET="$withval" printf "%s\n" "#define PRNGD_SOCKET \"$PRNGD_SOCKET\"" >>confdefs.h fi else $as_nop # Check for existing socket only if we don't have a random device already if test "x$OPENSSL_SEEDS_ITSELF" != "xyes" ; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for PRNGD/EGD socket" >&5 printf %s "checking for PRNGD/EGD socket... " >&6; } # Insert other locations here for sock in /var/run/egd-pool /dev/egd-pool /etc/entropy; do if test -r $sock && $TEST_MINUS_S_SH -c "test -S $sock -o -p $sock" ; then PRNGD_SOCKET="$sock" printf "%s\n" "#define PRNGD_SOCKET \"$PRNGD_SOCKET\"" >>confdefs.h break; fi done if test ! -z "$PRNGD_SOCKET" ; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $PRNGD_SOCKET" >&5 printf "%s\n" "$PRNGD_SOCKET" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: not found" >&5 printf "%s\n" "not found" >&6; } fi fi fi # Which randomness source do we use? if test ! -z "$PRNGD_PORT" ; then RAND_MSG="PRNGd port $PRNGD_PORT" elif test ! -z "$PRNGD_SOCKET" ; then RAND_MSG="PRNGd socket $PRNGD_SOCKET" elif test ! -z "$OPENSSL_SEEDS_ITSELF" ; then printf "%s\n" "#define OPENSSL_PRNG_ONLY 1" >>confdefs.h RAND_MSG="OpenSSL internal ONLY" elif test "x$openssl" = "xno" ; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: OpenSSH will use /dev/urandom as a source of random numbers. It will fail if this device is not supported or accessible" >&5 printf "%s\n" "$as_me: WARNING: OpenSSH will use /dev/urandom as a source of random numbers. It will fail if this device is not supported or accessible" >&2;} else as_fn_error $? "OpenSSH has no source of random numbers. Please configure OpenSSL with an entropy source or re-run configure using one of the --with-prngd-port or --with-prngd-socket options" "$LINENO" 5 fi LIBS="$nocrypto_saved_LIBS" saved_LIBS="$LIBS" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for ia_openinfo in -liaf" >&5 printf %s "checking for ia_openinfo in -liaf... " >&6; } if test ${ac_cv_lib_iaf_ia_openinfo+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-liaf $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char ia_openinfo (); int main (void) { return ia_openinfo (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_iaf_ia_openinfo=yes else $as_nop ac_cv_lib_iaf_ia_openinfo=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_iaf_ia_openinfo" >&5 printf "%s\n" "$ac_cv_lib_iaf_ia_openinfo" >&6; } if test "x$ac_cv_lib_iaf_ia_openinfo" = xyes then : LIBS="$LIBS -liaf" for ac_func in set_id do : ac_fn_c_check_func "$LINENO" "set_id" "ac_cv_func_set_id" if test "x$ac_cv_func_set_id" = xyes then : printf "%s\n" "#define HAVE_SET_ID 1" >>confdefs.h SSHDLIBS="$SSHDLIBS -liaf" printf "%s\n" "#define HAVE_LIBIAF 1" >>confdefs.h fi done fi LIBS="$saved_LIBS" # Check for crypt() in libcrypt. If we have it, we only need it for sshd. saved_LIBS="$LIBS" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for crypt in -lcrypt" >&5 printf %s "checking for crypt in -lcrypt... " >&6; } if test ${ac_cv_lib_crypt_crypt+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lcrypt $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char crypt (); int main (void) { return crypt (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_crypt_crypt=yes else $as_nop ac_cv_lib_crypt_crypt=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_crypt_crypt" >&5 printf "%s\n" "$ac_cv_lib_crypt_crypt" >&6; } if test "x$ac_cv_lib_crypt_crypt" = xyes then : LIBS="-lcrypt $LIBS" SSHDLIBS="-lcrypt $SSHDLIBS" fi ac_fn_c_check_func "$LINENO" "crypt" "ac_cv_func_crypt" if test "x$ac_cv_func_crypt" = xyes then : printf "%s\n" "#define HAVE_CRYPT 1" >>confdefs.h fi LIBS="$saved_LIBS" # Check for PAM libs PAM_MSG="no" # Check whether --with-pam was given. if test ${with_pam+y} then : withval=$with_pam; if test "x$withval" != "xno" ; then if test "x$ac_cv_header_security_pam_appl_h" != "xyes" && \ test "x$ac_cv_header_pam_pam_appl_h" != "xyes" ; then as_fn_error $? "PAM headers not found" "$LINENO" 5 fi saved_LIBS="$LIBS" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 printf %s "checking for dlopen in -ldl... " >&6; } if test ${ac_cv_lib_dl_dlopen+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-ldl $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char dlopen (); int main (void) { return dlopen (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_dl_dlopen=yes else $as_nop ac_cv_lib_dl_dlopen=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 printf "%s\n" "$ac_cv_lib_dl_dlopen" >&6; } if test "x$ac_cv_lib_dl_dlopen" = xyes then : printf "%s\n" "#define HAVE_LIBDL 1" >>confdefs.h LIBS="-ldl $LIBS" fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for pam_set_item in -lpam" >&5 printf %s "checking for pam_set_item in -lpam... " >&6; } if test ${ac_cv_lib_pam_pam_set_item+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lpam $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char pam_set_item (); int main (void) { return pam_set_item (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_pam_pam_set_item=yes else $as_nop ac_cv_lib_pam_pam_set_item=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pam_pam_set_item" >&5 printf "%s\n" "$ac_cv_lib_pam_pam_set_item" >&6; } if test "x$ac_cv_lib_pam_pam_set_item" = xyes then : printf "%s\n" "#define HAVE_LIBPAM 1" >>confdefs.h LIBS="-lpam $LIBS" else $as_nop as_fn_error $? "*** libpam missing" "$LINENO" 5 fi ac_fn_c_check_func "$LINENO" "pam_getenvlist" "ac_cv_func_pam_getenvlist" if test "x$ac_cv_func_pam_getenvlist" = xyes then : printf "%s\n" "#define HAVE_PAM_GETENVLIST 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "pam_putenv" "ac_cv_func_pam_putenv" if test "x$ac_cv_func_pam_putenv" = xyes then : printf "%s\n" "#define HAVE_PAM_PUTENV 1" >>confdefs.h fi LIBS="$saved_LIBS" PAM_MSG="yes" SSHDLIBS="$SSHDLIBS -lpam" printf "%s\n" "#define USE_PAM 1" >>confdefs.h if test $ac_cv_lib_dl_dlopen = yes; then case "$LIBS" in *-ldl*) # libdl already in LIBS ;; *) SSHDLIBS="$SSHDLIBS -ldl" ;; esac fi fi fi # Check whether --with-pam-service was given. if test ${with_pam_service+y} then : withval=$with_pam_service; if test "x$withval" != "xno" && \ test "x$withval" != "xyes" ; then printf "%s\n" "#define SSHD_PAM_SERVICE \"$withval\"" >>confdefs.h fi fi # Check for older PAM if test "x$PAM_MSG" = "xyes" ; then # Check PAM strerror arguments (old PAM) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether pam_strerror takes only one argument" >&5 printf %s "checking whether pam_strerror takes only one argument... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #if defined(HAVE_SECURITY_PAM_APPL_H) #include #elif defined (HAVE_PAM_PAM_APPL_H) #include #endif int main (void) { (void)pam_strerror((pam_handle_t *)NULL, -1); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } else $as_nop printf "%s\n" "#define HAVE_OLD_PAM 1" >>confdefs.h { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } PAM_MSG="yes (old library)" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi case "$host" in *-*-cygwin*) SSH_PRIVSEP_USER=CYGWIN_SSH_PRIVSEP_USER ;; *) SSH_PRIVSEP_USER=sshd ;; esac # Check whether --with-privsep-user was given. if test ${with_privsep_user+y} then : withval=$with_privsep_user; if test -n "$withval" && test "x$withval" != "xno" && \ test "x${withval}" != "xyes"; then SSH_PRIVSEP_USER=$withval fi fi if test "x$SSH_PRIVSEP_USER" = "xCYGWIN_SSH_PRIVSEP_USER" ; then printf "%s\n" "#define SSH_PRIVSEP_USER CYGWIN_SSH_PRIVSEP_USER" >>confdefs.h else printf "%s\n" "#define SSH_PRIVSEP_USER \"$SSH_PRIVSEP_USER\"" >>confdefs.h fi if test "x$have_linux_no_new_privs" = "x1" ; then ac_fn_check_decl "$LINENO" "SECCOMP_MODE_FILTER" "ac_cv_have_decl_SECCOMP_MODE_FILTER" " #include #include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_SECCOMP_MODE_FILTER" = xyes then : have_seccomp_filter=1 fi fi if test "x$have_seccomp_filter" = "x1" ; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking kernel for seccomp_filter support" >&5 printf %s "checking kernel for seccomp_filter support... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include #include #include int main (void) { int i = $seccomp_audit_arch; errno = 0; prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, NULL, 0, 0); exit(errno == EFAULT ? 0 : 1); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } # Disable seccomp filter as a target have_seccomp_filter=0 fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext fi ac_fn_c_check_member "$LINENO" "struct pollfd" "fd" "ac_cv_member_struct_pollfd_fd" " #include #ifdef HAVE_POLL_H #include #endif #ifdef HAVE_SYS_POLL_H #include #endif " if test "x$ac_cv_member_struct_pollfd_fd" = xyes then : printf "%s\n" "#define HAVE_STRUCT_POLLFD_FD 1" >>confdefs.h fi ac_fn_c_check_type "$LINENO" "nfds_t" "ac_cv_type_nfds_t" " #include #ifdef HAVE_POLL_H #include #endif #ifdef HAVE_SYS_POLL_H #include #endif " if test "x$ac_cv_type_nfds_t" = xyes then : printf "%s\n" "#define HAVE_NFDS_T 1" >>confdefs.h fi # Decide which sandbox style to use sandbox_arg="" # Check whether --with-sandbox was given. if test ${with_sandbox+y} then : withval=$with_sandbox; if test "x$withval" = "xyes" ; then sandbox_arg="" else sandbox_arg="$withval" fi fi if test "x$sandbox_arg" != "xno"; then # POSIX specifies that poll() "shall fail with EINVAL if the nfds argument # is greater than OPEN_MAX". On some platforms that includes implementions # of select in userspace on top of poll() so check both work with rlimit # NOFILES so check that both work before enabling the rlimit sandbox. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if select and/or poll works with descriptor rlimit" >&5 printf %s "checking if select and/or poll works with descriptor rlimit... " >&6; } if test "$cross_compiling" = yes then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cross compiling: assuming no" >&5 printf "%s\n" "$as_me: WARNING: cross compiling: assuming no" >&2;} select_works_with_rlimit=no else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #ifdef HAVE_SYS_TIME_H # include #endif #include #ifdef HAVE_SYS_SELECT_H # include #endif #ifdef HAVE_POLL_H # include #elif HAVE_SYS_POLL_H # include #endif #include #include #include int main (void) { struct rlimit rl_zero; int fd, r; fd_set fds; struct timeval tv; #ifdef HAVE_POLL struct pollfd pfd; #endif fd = open("/dev/null", O_RDONLY); FD_ZERO(&fds); FD_SET(fd, &fds); rl_zero.rlim_cur = rl_zero.rlim_max = 0; setrlimit(RLIMIT_FSIZE, &rl_zero); setrlimit(RLIMIT_NOFILE, &rl_zero); tv.tv_sec = 1; tv.tv_usec = 0; r = select(fd+1, &fds, NULL, NULL, &tv); if (r == -1) exit(1); #ifdef HAVE_POLL pfd.fd = fd; pfd.events = POLLIN; r = poll(&pfd, 1, 1); if (r == -1) exit(2); #endif exit(0); ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } select_works_with_rlimit=yes else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } select_works_with_rlimit=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if setrlimit(RLIMIT_NOFILE,{0,0}) works" >&5 printf %s "checking if setrlimit(RLIMIT_NOFILE,{0,0}) works... " >&6; } if test "$cross_compiling" = yes then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cross compiling: assuming yes" >&5 printf "%s\n" "$as_me: WARNING: cross compiling: assuming yes" >&2;} rlimit_nofile_zero_works=yes else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #ifdef HAVE_SYS_TIME_H # include #endif #include #include #include int main (void) { struct rlimit rl_zero; int r; rl_zero.rlim_cur = rl_zero.rlim_max = 0; r = setrlimit(RLIMIT_NOFILE, &rl_zero); exit (r == -1 ? 1 : 0); ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } rlimit_nofile_zero_works=yes else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } rlimit_nofile_zero_works=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if setrlimit RLIMIT_FSIZE works" >&5 printf %s "checking if setrlimit RLIMIT_FSIZE works... " >&6; } if test "$cross_compiling" = yes then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cross compiling: assuming yes" >&5 printf "%s\n" "$as_me: WARNING: cross compiling: assuming yes" >&2;} else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include int main (void) { struct rlimit rl_zero; rl_zero.rlim_cur = rl_zero.rlim_max = 0; exit(setrlimit(RLIMIT_FSIZE, &rl_zero) != 0); ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } printf "%s\n" "#define SANDBOX_SKIP_RLIMIT_FSIZE 1" >>confdefs.h fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi if test "x$sandbox_arg" = "xpledge" || \ ( test -z "$sandbox_arg" && test "x$ac_cv_func_pledge" = "xyes" ) ; then test "x$ac_cv_func_pledge" != "xyes" && \ as_fn_error $? "pledge sandbox requires pledge(2) support" "$LINENO" 5 SANDBOX_STYLE="pledge" printf "%s\n" "#define SANDBOX_PLEDGE 1" >>confdefs.h elif test "x$sandbox_arg" = "xsystrace" || \ ( test -z "$sandbox_arg" && test "x$have_systr_policy_kill" = "x1" ) ; then test "x$have_systr_policy_kill" != "x1" && \ as_fn_error $? "systrace sandbox requires systrace headers and SYSTR_POLICY_KILL support" "$LINENO" 5 SANDBOX_STYLE="systrace" printf "%s\n" "#define SANDBOX_SYSTRACE 1" >>confdefs.h elif test "x$sandbox_arg" = "xdarwin" || \ ( test -z "$sandbox_arg" && test "x$ac_cv_func_sandbox_init" = "xyes" && \ test "x$ac_cv_header_sandbox_h" = "xyes") ; then test "x$ac_cv_func_sandbox_init" != "xyes" -o \ "x$ac_cv_header_sandbox_h" != "xyes" && \ as_fn_error $? "Darwin seatbelt sandbox requires sandbox.h and sandbox_init function" "$LINENO" 5 SANDBOX_STYLE="darwin" printf "%s\n" "#define SANDBOX_DARWIN 1" >>confdefs.h elif test "x$sandbox_arg" = "xseccomp_filter" || \ ( test -z "$sandbox_arg" && \ test "x$have_seccomp_filter" = "x1" && \ test "x$ac_cv_header_elf_h" = "xyes" && \ test "x$ac_cv_header_linux_audit_h" = "xyes" && \ test "x$ac_cv_header_linux_filter_h" = "xyes" && \ test "x$seccomp_audit_arch" != "x" && \ test "x$have_linux_no_new_privs" = "x1" && \ test "x$ac_cv_func_prctl" = "xyes" ) ; then test "x$seccomp_audit_arch" = "x" && \ as_fn_error $? "seccomp_filter sandbox not supported on $host" "$LINENO" 5 test "x$have_linux_no_new_privs" != "x1" && \ as_fn_error $? "seccomp_filter sandbox requires PR_SET_NO_NEW_PRIVS" "$LINENO" 5 test "x$have_seccomp_filter" != "x1" && \ as_fn_error $? "seccomp_filter sandbox requires seccomp headers" "$LINENO" 5 test "x$ac_cv_func_prctl" != "xyes" && \ as_fn_error $? "seccomp_filter sandbox requires prctl function" "$LINENO" 5 SANDBOX_STYLE="seccomp_filter" printf "%s\n" "#define SANDBOX_SECCOMP_FILTER 1" >>confdefs.h elif test "x$sandbox_arg" = "xcapsicum" || \ ( test -z "$sandbox_arg" && \ test "x$disable_capsicum" != "xyes" && \ test "x$ac_cv_header_sys_capsicum_h" = "xyes" && \ test "x$ac_cv_func_cap_rights_limit" = "xyes") ; then test "x$ac_cv_header_sys_capsicum_h" != "xyes" && \ as_fn_error $? "capsicum sandbox requires sys/capsicum.h header" "$LINENO" 5 test "x$ac_cv_func_cap_rights_limit" != "xyes" && \ as_fn_error $? "capsicum sandbox requires cap_rights_limit function" "$LINENO" 5 SANDBOX_STYLE="capsicum" printf "%s\n" "#define SANDBOX_CAPSICUM 1" >>confdefs.h elif test "x$sandbox_arg" = "xrlimit" || \ ( test -z "$sandbox_arg" && test "x$ac_cv_func_setrlimit" = "xyes" && \ test "x$select_works_with_rlimit" = "xyes" && \ test "x$rlimit_nofile_zero_works" = "xyes" ) ; then test "x$ac_cv_func_setrlimit" != "xyes" && \ as_fn_error $? "rlimit sandbox requires setrlimit function" "$LINENO" 5 test "x$select_works_with_rlimit" != "xyes" && \ as_fn_error $? "rlimit sandbox requires select to work with rlimit" "$LINENO" 5 SANDBOX_STYLE="rlimit" printf "%s\n" "#define SANDBOX_RLIMIT 1" >>confdefs.h elif test "x$sandbox_arg" = "xsolaris" || \ ( test -z "$sandbox_arg" && test "x$SOLARIS_PRIVS" = "xyes" ) ; then SANDBOX_STYLE="solaris" printf "%s\n" "#define SANDBOX_SOLARIS 1" >>confdefs.h elif test -z "$sandbox_arg" || test "x$sandbox_arg" = "xno" || \ test "x$sandbox_arg" = "xnone" || test "x$sandbox_arg" = "xnull" ; then SANDBOX_STYLE="none" printf "%s\n" "#define SANDBOX_NULL 1" >>confdefs.h else as_fn_error $? "unsupported --with-sandbox" "$LINENO" 5 fi # Cheap hack to ensure NEWS-OS libraries are arranged right. if test ! -z "$SONY" ; then LIBS="$LIBS -liberty"; fi # Check for long long datatypes ac_fn_c_check_type "$LINENO" "long long" "ac_cv_type_long_long" "$ac_includes_default" if test "x$ac_cv_type_long_long" = xyes then : printf "%s\n" "#define HAVE_LONG_LONG 1" >>confdefs.h fi ac_fn_c_check_type "$LINENO" "unsigned long long" "ac_cv_type_unsigned_long_long" "$ac_includes_default" if test "x$ac_cv_type_unsigned_long_long" = xyes then : printf "%s\n" "#define HAVE_UNSIGNED_LONG_LONG 1" >>confdefs.h fi ac_fn_c_check_type "$LINENO" "long double" "ac_cv_type_long_double" "$ac_includes_default" if test "x$ac_cv_type_long_double" = xyes then : printf "%s\n" "#define HAVE_LONG_DOUBLE 1" >>confdefs.h fi # Check datatype sizes # The cast to long int works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. # This bug is HP SR number 8606223364. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking size of short int" >&5 printf %s "checking size of short int... " >&6; } if test ${ac_cv_sizeof_short_int+y} then : printf %s "(cached) " >&6 else $as_nop if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (short int))" "ac_cv_sizeof_short_int" "$ac_includes_default" then : else $as_nop if test "$ac_cv_type_short_int" = yes; then { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "cannot compute sizeof (short int) See \`config.log' for more details" "$LINENO" 5; } else ac_cv_sizeof_short_int=0 fi fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_short_int" >&5 printf "%s\n" "$ac_cv_sizeof_short_int" >&6; } printf "%s\n" "#define SIZEOF_SHORT_INT $ac_cv_sizeof_short_int" >>confdefs.h # The cast to long int works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. # This bug is HP SR number 8606223364. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking size of int" >&5 printf %s "checking size of int... " >&6; } if test ${ac_cv_sizeof_int+y} then : printf %s "(cached) " >&6 else $as_nop if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (int))" "ac_cv_sizeof_int" "$ac_includes_default" then : else $as_nop if test "$ac_cv_type_int" = yes; then { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "cannot compute sizeof (int) See \`config.log' for more details" "$LINENO" 5; } else ac_cv_sizeof_int=0 fi fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_int" >&5 printf "%s\n" "$ac_cv_sizeof_int" >&6; } printf "%s\n" "#define SIZEOF_INT $ac_cv_sizeof_int" >>confdefs.h # The cast to long int works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. # This bug is HP SR number 8606223364. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking size of long int" >&5 printf %s "checking size of long int... " >&6; } if test ${ac_cv_sizeof_long_int+y} then : printf %s "(cached) " >&6 else $as_nop if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (long int))" "ac_cv_sizeof_long_int" "$ac_includes_default" then : else $as_nop if test "$ac_cv_type_long_int" = yes; then { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "cannot compute sizeof (long int) See \`config.log' for more details" "$LINENO" 5; } else ac_cv_sizeof_long_int=0 fi fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_long_int" >&5 printf "%s\n" "$ac_cv_sizeof_long_int" >&6; } printf "%s\n" "#define SIZEOF_LONG_INT $ac_cv_sizeof_long_int" >>confdefs.h # The cast to long int works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. # This bug is HP SR number 8606223364. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking size of long long int" >&5 printf %s "checking size of long long int... " >&6; } if test ${ac_cv_sizeof_long_long_int+y} then : printf %s "(cached) " >&6 else $as_nop if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (long long int))" "ac_cv_sizeof_long_long_int" "$ac_includes_default" then : else $as_nop if test "$ac_cv_type_long_long_int" = yes; then { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "cannot compute sizeof (long long int) See \`config.log' for more details" "$LINENO" 5; } else ac_cv_sizeof_long_long_int=0 fi fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_long_long_int" >&5 printf "%s\n" "$ac_cv_sizeof_long_long_int" >&6; } printf "%s\n" "#define SIZEOF_LONG_LONG_INT $ac_cv_sizeof_long_long_int" >>confdefs.h # The cast to long int works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. # This bug is HP SR number 8606223364. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking size of time_t" >&5 printf %s "checking size of time_t... " >&6; } if test ${ac_cv_sizeof_time_t+y} then : printf %s "(cached) " >&6 else $as_nop if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (time_t))" "ac_cv_sizeof_time_t" " #include #ifdef HAVE_SYS_TIME_H # include #endif #ifdef HAVE_TIME_H # include #endif " then : else $as_nop if test "$ac_cv_type_time_t" = yes; then { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "cannot compute sizeof (time_t) See \`config.log' for more details" "$LINENO" 5; } else ac_cv_sizeof_time_t=0 fi fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_time_t" >&5 printf "%s\n" "$ac_cv_sizeof_time_t" >&6; } printf "%s\n" "#define SIZEOF_TIME_T $ac_cv_sizeof_time_t" >>confdefs.h # Sanity check long long for some platforms (AIX) if test "x$ac_cv_sizeof_long_long_int" = "x4" ; then ac_cv_sizeof_long_long_int=0 fi # compute LLONG_MIN and LLONG_MAX if we don't know them. if test -z "$have_llong_max" && test -z "$have_long_long_max"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for max value of long long" >&5 printf %s "checking for max value of long long... " >&6; } if test "$cross_compiling" = yes then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cross compiling: not checking" >&5 printf "%s\n" "$as_me: WARNING: cross compiling: not checking" >&2;} else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include /* Why is this so damn hard? */ #ifdef __GNUC__ # undef __GNUC__ #endif #define __USE_ISOC99 #include #define DATA "conftest.llminmax" #define my_abs(a) ((a) < 0 ? ((a) * -1) : (a)) /* * printf in libc on some platforms (eg old Tru64) does not understand %lld so * we do this the hard way. */ static int fprint_ll(FILE *f, long long n) { unsigned int i; int l[sizeof(long long) * 8]; if (n < 0) if (fprintf(f, "-") < 0) return -1; for (i = 0; n != 0; i++) { l[i] = my_abs(n % 10); n /= 10; } do { if (fprintf(f, "%d", l[--i]) < 0) return -1; } while (i != 0); if (fprintf(f, " ") < 0) return -1; return 0; } int main (void) { FILE *f; long long i, llmin, llmax = 0; if((f = fopen(DATA,"w")) == NULL) exit(1); #if defined(LLONG_MIN) && defined(LLONG_MAX) fprintf(stderr, "Using system header for LLONG_MIN and LLONG_MAX\n"); llmin = LLONG_MIN; llmax = LLONG_MAX; #else fprintf(stderr, "Calculating LLONG_MIN and LLONG_MAX\n"); /* This will work on one's complement and two's complement */ for (i = 1; i > llmax; i <<= 1, i++) llmax = i; llmin = llmax + 1LL; /* wrap */ #endif /* Sanity check */ if (llmin + 1 < llmin || llmin - 1 < llmin || llmax + 1 > llmax || llmax - 1 > llmax || llmin == llmax || llmin == 0 || llmax == 0 || llmax < LONG_MAX || llmin > LONG_MIN) { fprintf(f, "unknown unknown\n"); exit(2); } if (fprint_ll(f, llmin) < 0) exit(3); if (fprint_ll(f, llmax) < 0) exit(4); if (fclose(f) < 0) exit(5); exit(0); ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO" then : llong_min=`$AWK '{print $1}' conftest.llminmax` llong_max=`$AWK '{print $2}' conftest.llminmax` { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $llong_max" >&5 printf "%s\n" "$llong_max" >&6; } printf "%s\n" "#define LLONG_MAX ${llong_max}LL" >>confdefs.h { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for min value of long long" >&5 printf %s "checking for min value of long long... " >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $llong_min" >&5 printf "%s\n" "$llong_min" >&6; } printf "%s\n" "#define LLONG_MIN ${llong_min}LL" >>confdefs.h else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: not found" >&5 printf "%s\n" "not found" >&6; } fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi ac_fn_check_decl "$LINENO" "UINT32_MAX" "ac_cv_have_decl_UINT32_MAX" " #ifdef HAVE_SYS_LIMITS_H # include #endif #ifdef HAVE_LIMITS_H # include #endif #ifdef HAVE_STDINT_H # include #endif " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_UINT32_MAX" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_UINT32_MAX $ac_have_decl" >>confdefs.h # More checks for data types { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for u_int type" >&5 printf %s "checking for u_int type... " >&6; } if test ${ac_cv_have_u_int+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { u_int a; a = 1; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_have_u_int="yes" else $as_nop ac_cv_have_u_int="no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_u_int" >&5 printf "%s\n" "$ac_cv_have_u_int" >&6; } if test "x$ac_cv_have_u_int" = "xyes" ; then printf "%s\n" "#define HAVE_U_INT 1" >>confdefs.h have_u_int=1 fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for intXX_t types" >&5 printf %s "checking for intXX_t types... " >&6; } if test ${ac_cv_have_intxx_t+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { int8_t a; int16_t b; int32_t c; a = b = c = 1; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_have_intxx_t="yes" else $as_nop ac_cv_have_intxx_t="no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_intxx_t" >&5 printf "%s\n" "$ac_cv_have_intxx_t" >&6; } if test "x$ac_cv_have_intxx_t" = "xyes" ; then printf "%s\n" "#define HAVE_INTXX_T 1" >>confdefs.h have_intxx_t=1 fi if (test -z "$have_intxx_t" && \ test "x$ac_cv_header_stdint_h" = "xyes") then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for intXX_t types in stdint.h" >&5 printf %s "checking for intXX_t types in stdint.h... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { int8_t a; int16_t b; int32_t c; a = b = c = 1; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : printf "%s\n" "#define HAVE_INTXX_T 1" >>confdefs.h { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for int64_t type" >&5 printf %s "checking for int64_t type... " >&6; } if test ${ac_cv_have_int64_t+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #ifdef HAVE_STDINT_H # include #endif #include #ifdef HAVE_SYS_BITYPES_H # include #endif int main (void) { int64_t a; a = 1; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_have_int64_t="yes" else $as_nop ac_cv_have_int64_t="no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_int64_t" >&5 printf "%s\n" "$ac_cv_have_int64_t" >&6; } if test "x$ac_cv_have_int64_t" = "xyes" ; then printf "%s\n" "#define HAVE_INT64_T 1" >>confdefs.h fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for u_intXX_t types" >&5 printf %s "checking for u_intXX_t types... " >&6; } if test ${ac_cv_have_u_intxx_t+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { u_int8_t a; u_int16_t b; u_int32_t c; a = b = c = 1; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_have_u_intxx_t="yes" else $as_nop ac_cv_have_u_intxx_t="no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_u_intxx_t" >&5 printf "%s\n" "$ac_cv_have_u_intxx_t" >&6; } if test "x$ac_cv_have_u_intxx_t" = "xyes" ; then printf "%s\n" "#define HAVE_U_INTXX_T 1" >>confdefs.h have_u_intxx_t=1 fi if test -z "$have_u_intxx_t" ; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for u_intXX_t types in sys/socket.h" >&5 printf %s "checking for u_intXX_t types in sys/socket.h... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { u_int8_t a; u_int16_t b; u_int32_t c; a = b = c = 1; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : printf "%s\n" "#define HAVE_U_INTXX_T 1" >>confdefs.h { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for u_int64_t types" >&5 printf %s "checking for u_int64_t types... " >&6; } if test ${ac_cv_have_u_int64_t+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { u_int64_t a; a = 1; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_have_u_int64_t="yes" else $as_nop ac_cv_have_u_int64_t="no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_u_int64_t" >&5 printf "%s\n" "$ac_cv_have_u_int64_t" >&6; } if test "x$ac_cv_have_u_int64_t" = "xyes" ; then printf "%s\n" "#define HAVE_U_INT64_T 1" >>confdefs.h have_u_int64_t=1 fi if (test -z "$have_u_int64_t" && \ test "x$ac_cv_header_sys_bitypes_h" = "xyes") then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for u_int64_t type in sys/bitypes.h" >&5 printf %s "checking for u_int64_t type in sys/bitypes.h... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { u_int64_t a; a = 1 ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : printf "%s\n" "#define HAVE_U_INT64_T 1" >>confdefs.h { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi if test -z "$have_u_intxx_t" ; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for uintXX_t types" >&5 printf %s "checking for uintXX_t types... " >&6; } if test ${ac_cv_have_uintxx_t+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { uint8_t a; uint16_t b; uint32_t c; a = b = c = 1; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_have_uintxx_t="yes" else $as_nop ac_cv_have_uintxx_t="no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_uintxx_t" >&5 printf "%s\n" "$ac_cv_have_uintxx_t" >&6; } if test "x$ac_cv_have_uintxx_t" = "xyes" ; then printf "%s\n" "#define HAVE_UINTXX_T 1" >>confdefs.h fi fi if (test -z "$have_uintxx_t" && \ test "x$ac_cv_header_stdint_h" = "xyes") then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for uintXX_t types in stdint.h" >&5 printf %s "checking for uintXX_t types in stdint.h... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { uint8_t a; uint16_t b; uint32_t c; a = b = c = 1; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : printf "%s\n" "#define HAVE_UINTXX_T 1" >>confdefs.h { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi if (test -z "$have_uintxx_t" && \ test "x$ac_cv_header_inttypes_h" = "xyes") then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for uintXX_t types in inttypes.h" >&5 printf %s "checking for uintXX_t types in inttypes.h... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { uint8_t a; uint16_t b; uint32_t c; a = b = c = 1; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : printf "%s\n" "#define HAVE_UINTXX_T 1" >>confdefs.h { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi if (test -z "$have_u_intxx_t" || test -z "$have_intxx_t" && \ test "x$ac_cv_header_sys_bitypes_h" = "xyes") then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for intXX_t and u_intXX_t types in sys/bitypes.h" >&5 printf %s "checking for intXX_t and u_intXX_t types in sys/bitypes.h... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { int8_t a; int16_t b; int32_t c; u_int8_t e; u_int16_t f; u_int32_t g; a = b = c = e = f = g = 1; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : printf "%s\n" "#define HAVE_U_INTXX_T 1" >>confdefs.h printf "%s\n" "#define HAVE_INTXX_T 1" >>confdefs.h { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for u_char" >&5 printf %s "checking for u_char... " >&6; } if test ${ac_cv_have_u_char+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { u_char foo; foo = 125; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_have_u_char="yes" else $as_nop ac_cv_have_u_char="no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_u_char" >&5 printf "%s\n" "$ac_cv_have_u_char" >&6; } if test "x$ac_cv_have_u_char" = "xyes" ; then printf "%s\n" "#define HAVE_U_CHAR 1" >>confdefs.h fi ac_fn_c_check_type "$LINENO" "intmax_t" "ac_cv_type_intmax_t" " #include #ifdef HAVE_STDINT_H # include #endif " if test "x$ac_cv_type_intmax_t" = xyes then : printf "%s\n" "#define HAVE_INTMAX_T 1" >>confdefs.h fi ac_fn_c_check_type "$LINENO" "uintmax_t" "ac_cv_type_uintmax_t" " #include #ifdef HAVE_STDINT_H # include #endif " if test "x$ac_cv_type_uintmax_t" = xyes then : printf "%s\n" "#define HAVE_UINTMAX_T 1" >>confdefs.h fi ac_fn_c_check_type "$LINENO" "socklen_t" "ac_cv_type_socklen_t" "#include #include " if test "x$ac_cv_type_socklen_t" = xyes then : else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for socklen_t equivalent" >&5 printf %s "checking for socklen_t equivalent... " >&6; } if test ${curl_cv_socklen_t_equiv+y} then : printf %s "(cached) " >&6 else $as_nop # Systems have either "struct sockaddr *" or # "void *" as the second argument to getpeername curl_cv_socklen_t_equiv= for arg2 in "struct sockaddr" void; do for t in int size_t unsigned long "unsigned long"; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int getpeername (int, $arg2 *, $t *); int main (void) { $t len; getpeername(0,0,&len); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : curl_cv_socklen_t_equiv="$t" break fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext done done if test "x$curl_cv_socklen_t_equiv" = x; then as_fn_error $? "Cannot find a type to use in place of socklen_t" "$LINENO" 5 fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $curl_cv_socklen_t_equiv" >&5 printf "%s\n" "$curl_cv_socklen_t_equiv" >&6; } printf "%s\n" "#define socklen_t $curl_cv_socklen_t_equiv" >>confdefs.h fi ac_fn_c_check_type "$LINENO" "sig_atomic_t" "ac_cv_type_sig_atomic_t" "#include " if test "x$ac_cv_type_sig_atomic_t" = xyes then : printf "%s\n" "#define HAVE_SIG_ATOMIC_T 1" >>confdefs.h fi ac_fn_c_check_type "$LINENO" "sighandler_t" "ac_cv_type_sighandler_t" "#include " if test "x$ac_cv_type_sighandler_t" = xyes then : printf "%s\n" "#define HAVE_SIGHANDLER_T 1" >>confdefs.h fi ac_fn_c_check_type "$LINENO" "fsblkcnt_t" "ac_cv_type_fsblkcnt_t" " #include #ifdef HAVE_SYS_BITYPES_H #include #endif #ifdef HAVE_SYS_STATFS_H #include #endif #ifdef HAVE_SYS_STATVFS_H #include #endif " if test "x$ac_cv_type_fsblkcnt_t" = xyes then : printf "%s\n" "#define HAVE_FSBLKCNT_T 1" >>confdefs.h fi ac_fn_c_check_type "$LINENO" "fsfilcnt_t" "ac_cv_type_fsfilcnt_t" " #include #ifdef HAVE_SYS_BITYPES_H #include #endif #ifdef HAVE_SYS_STATFS_H #include #endif #ifdef HAVE_SYS_STATVFS_H #include #endif " if test "x$ac_cv_type_fsfilcnt_t" = xyes then : printf "%s\n" "#define HAVE_FSFILCNT_T 1" >>confdefs.h fi ac_fn_c_check_member "$LINENO" "struct statfs" "f_files" "ac_cv_member_struct_statfs_f_files" " #include #include #ifdef HAVE_SYS_BITYPES_H #include #endif #ifdef HAVE_SYS_STATFS_H #include #endif #ifdef HAVE_SYS_STATVFS_H #include #endif #ifdef HAVE_SYS_VFS_H #include #endif #ifdef HAVE_SYS_MOUNT_H #include #endif " if test "x$ac_cv_member_struct_statfs_f_files" = xyes then : printf "%s\n" "#define HAVE_STRUCT_STATFS_F_FILES 1" >>confdefs.h fi ac_fn_c_check_member "$LINENO" "struct statfs" "f_flags" "ac_cv_member_struct_statfs_f_flags" " #include #include #ifdef HAVE_SYS_BITYPES_H #include #endif #ifdef HAVE_SYS_STATFS_H #include #endif #ifdef HAVE_SYS_STATVFS_H #include #endif #ifdef HAVE_SYS_VFS_H #include #endif #ifdef HAVE_SYS_MOUNT_H #include #endif " if test "x$ac_cv_member_struct_statfs_f_flags" = xyes then : printf "%s\n" "#define HAVE_STRUCT_STATFS_F_FLAGS 1" >>confdefs.h fi ac_fn_c_check_type "$LINENO" "in_addr_t" "ac_cv_type_in_addr_t" "#include #include " if test "x$ac_cv_type_in_addr_t" = xyes then : printf "%s\n" "#define HAVE_IN_ADDR_T 1" >>confdefs.h fi ac_fn_c_check_type "$LINENO" "in_port_t" "ac_cv_type_in_port_t" "#include #include " if test "x$ac_cv_type_in_port_t" = xyes then : printf "%s\n" "#define HAVE_IN_PORT_T 1" >>confdefs.h fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for size_t" >&5 printf %s "checking for size_t... " >&6; } if test ${ac_cv_have_size_t+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { size_t foo; foo = 1235; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_have_size_t="yes" else $as_nop ac_cv_have_size_t="no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_size_t" >&5 printf "%s\n" "$ac_cv_have_size_t" >&6; } if test "x$ac_cv_have_size_t" = "xyes" ; then printf "%s\n" "#define HAVE_SIZE_T 1" >>confdefs.h fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for ssize_t" >&5 printf %s "checking for ssize_t... " >&6; } if test ${ac_cv_have_ssize_t+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { ssize_t foo; foo = 1235; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_have_ssize_t="yes" else $as_nop ac_cv_have_ssize_t="no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_ssize_t" >&5 printf "%s\n" "$ac_cv_have_ssize_t" >&6; } if test "x$ac_cv_have_ssize_t" = "xyes" ; then printf "%s\n" "#define HAVE_SSIZE_T 1" >>confdefs.h fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for clock_t" >&5 printf %s "checking for clock_t... " >&6; } if test ${ac_cv_have_clock_t+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { clock_t foo; foo = 1235; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_have_clock_t="yes" else $as_nop ac_cv_have_clock_t="no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_clock_t" >&5 printf "%s\n" "$ac_cv_have_clock_t" >&6; } if test "x$ac_cv_have_clock_t" = "xyes" ; then printf "%s\n" "#define HAVE_CLOCK_T 1" >>confdefs.h fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for sa_family_t" >&5 printf %s "checking for sa_family_t... " >&6; } if test ${ac_cv_have_sa_family_t+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main (void) { sa_family_t foo; foo = 1235; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_have_sa_family_t="yes" else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include int main (void) { sa_family_t foo; foo = 1235; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_have_sa_family_t="yes" else $as_nop ac_cv_have_sa_family_t="no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_sa_family_t" >&5 printf "%s\n" "$ac_cv_have_sa_family_t" >&6; } if test "x$ac_cv_have_sa_family_t" = "xyes" ; then printf "%s\n" "#define HAVE_SA_FAMILY_T 1" >>confdefs.h fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for pid_t" >&5 printf %s "checking for pid_t... " >&6; } if test ${ac_cv_have_pid_t+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { pid_t foo; foo = 1235; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_have_pid_t="yes" else $as_nop ac_cv_have_pid_t="no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_pid_t" >&5 printf "%s\n" "$ac_cv_have_pid_t" >&6; } if test "x$ac_cv_have_pid_t" = "xyes" ; then printf "%s\n" "#define HAVE_PID_T 1" >>confdefs.h fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for mode_t" >&5 printf %s "checking for mode_t... " >&6; } if test ${ac_cv_have_mode_t+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { mode_t foo; foo = 1235; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_have_mode_t="yes" else $as_nop ac_cv_have_mode_t="no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_mode_t" >&5 printf "%s\n" "$ac_cv_have_mode_t" >&6; } if test "x$ac_cv_have_mode_t" = "xyes" ; then printf "%s\n" "#define HAVE_MODE_T 1" >>confdefs.h fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for struct sockaddr_storage" >&5 printf %s "checking for struct sockaddr_storage... " >&6; } if test ${ac_cv_have_struct_sockaddr_storage+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main (void) { struct sockaddr_storage s; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_have_struct_sockaddr_storage="yes" else $as_nop ac_cv_have_struct_sockaddr_storage="no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_struct_sockaddr_storage" >&5 printf "%s\n" "$ac_cv_have_struct_sockaddr_storage" >&6; } if test "x$ac_cv_have_struct_sockaddr_storage" = "xyes" ; then printf "%s\n" "#define HAVE_STRUCT_SOCKADDR_STORAGE 1" >>confdefs.h fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for struct sockaddr_in6" >&5 printf %s "checking for struct sockaddr_in6... " >&6; } if test ${ac_cv_have_struct_sockaddr_in6+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main (void) { struct sockaddr_in6 s; s.sin6_family = 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_have_struct_sockaddr_in6="yes" else $as_nop ac_cv_have_struct_sockaddr_in6="no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_struct_sockaddr_in6" >&5 printf "%s\n" "$ac_cv_have_struct_sockaddr_in6" >&6; } if test "x$ac_cv_have_struct_sockaddr_in6" = "xyes" ; then printf "%s\n" "#define HAVE_STRUCT_SOCKADDR_IN6 1" >>confdefs.h fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for struct in6_addr" >&5 printf %s "checking for struct in6_addr... " >&6; } if test ${ac_cv_have_struct_in6_addr+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main (void) { struct in6_addr s; s.s6_addr[0] = 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_have_struct_in6_addr="yes" else $as_nop ac_cv_have_struct_in6_addr="no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_struct_in6_addr" >&5 printf "%s\n" "$ac_cv_have_struct_in6_addr" >&6; } if test "x$ac_cv_have_struct_in6_addr" = "xyes" ; then printf "%s\n" "#define HAVE_STRUCT_IN6_ADDR 1" >>confdefs.h ac_fn_c_check_member "$LINENO" "struct sockaddr_in6" "sin6_scope_id" "ac_cv_member_struct_sockaddr_in6_sin6_scope_id" " #ifdef HAVE_SYS_TYPES_H #include #endif #include " if test "x$ac_cv_member_struct_sockaddr_in6_sin6_scope_id" = xyes then : printf "%s\n" "#define HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID 1" >>confdefs.h fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for struct addrinfo" >&5 printf %s "checking for struct addrinfo... " >&6; } if test ${ac_cv_have_struct_addrinfo+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include int main (void) { struct addrinfo s; s.ai_flags = AI_PASSIVE; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_have_struct_addrinfo="yes" else $as_nop ac_cv_have_struct_addrinfo="no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_struct_addrinfo" >&5 printf "%s\n" "$ac_cv_have_struct_addrinfo" >&6; } if test "x$ac_cv_have_struct_addrinfo" = "xyes" ; then printf "%s\n" "#define HAVE_STRUCT_ADDRINFO 1" >>confdefs.h fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for struct timeval" >&5 printf %s "checking for struct timeval... " >&6; } if test ${ac_cv_have_struct_timeval+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { struct timeval tv; tv.tv_sec = 1; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_have_struct_timeval="yes" else $as_nop ac_cv_have_struct_timeval="no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_struct_timeval" >&5 printf "%s\n" "$ac_cv_have_struct_timeval" >&6; } if test "x$ac_cv_have_struct_timeval" = "xyes" ; then printf "%s\n" "#define HAVE_STRUCT_TIMEVAL 1" >>confdefs.h have_struct_timeval=1 fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for struct timespec" >&5 printf %s "checking for struct timespec... " >&6; } if test ${ac_cv_have_struct_timespec+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef HAVE_SYS_TIME_H # include #endif #ifdef HAVE_TIME_H # include #endif int main (void) { struct timespec ts; ts.tv_sec = 1; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_have_struct_timespec="yes" else $as_nop ac_cv_have_struct_timespec="no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_struct_timespec" >&5 printf "%s\n" "$ac_cv_have_struct_timespec" >&6; } if test "x$ac_cv_have_struct_timespec" = "xyes" ; then printf "%s\n" "#define HAVE_STRUCT_TIMESPEC 1" >>confdefs.h have_struct_timespec=1 fi # We need int64_t or else certain parts of the compile will fail. if test "x$ac_cv_have_int64_t" = "xno" && \ test "x$ac_cv_sizeof_long_int" != "x8" && \ test "x$ac_cv_sizeof_long_long_int" = "x0" ; then echo "OpenSSH requires int64_t support. Contact your vendor or install" echo "an alternative compiler (I.E., GCC) before continuing." echo "" exit 1; else if test "$cross_compiling" = yes then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cross compiling: Assuming working snprintf()" >&5 printf "%s\n" "$as_me: WARNING: cross compiling: Assuming working snprintf()" >&2;} else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #ifdef HAVE_SNPRINTF int main(void) { char buf[50]; char expected_out[50]; int mazsize = 50 ; #if (SIZEOF_LONG_INT == 8) long int num = 0x7fffffffffffffff; #else long long num = 0x7fffffffffffffffll; #endif strcpy(expected_out, "9223372036854775807"); snprintf(buf, mazsize, "%lld", num); if(strcmp(buf, expected_out) != 0) exit(1); exit(0); } #else int main(void) { exit(0); } #endif _ACEOF if ac_fn_c_try_run "$LINENO" then : true else $as_nop printf "%s\n" "#define BROKEN_SNPRINTF 1" >>confdefs.h fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi # look for field 'ut_host' in header 'utmp.h' ossh_safe=`echo "utmp.h" | sed 'y%./+-%__p_%'` ossh_varname="ossh_cv_$ossh_safe""_has_"ut_host { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for ut_host field in utmp.h" >&5 printf %s "checking for ut_host field in utmp.h... " >&6; } if eval test \${$ossh_varname+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "ut_host" >/dev/null 2>&1 then : eval "$ossh_varname=yes" else $as_nop eval "$ossh_varname=no" fi rm -rf conftest* fi ossh_result=`eval 'echo $'"$ossh_varname"` if test -n "`echo $ossh_varname`"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ossh_result" >&5 printf "%s\n" "$ossh_result" >&6; } if test "x$ossh_result" = "xyes"; then printf "%s\n" "#define HAVE_HOST_IN_UTMP 1" >>confdefs.h fi else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi # look for field 'ut_host' in header 'utmpx.h' ossh_safe=`echo "utmpx.h" | sed 'y%./+-%__p_%'` ossh_varname="ossh_cv_$ossh_safe""_has_"ut_host { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for ut_host field in utmpx.h" >&5 printf %s "checking for ut_host field in utmpx.h... " >&6; } if eval test \${$ossh_varname+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "ut_host" >/dev/null 2>&1 then : eval "$ossh_varname=yes" else $as_nop eval "$ossh_varname=no" fi rm -rf conftest* fi ossh_result=`eval 'echo $'"$ossh_varname"` if test -n "`echo $ossh_varname`"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ossh_result" >&5 printf "%s\n" "$ossh_result" >&6; } if test "x$ossh_result" = "xyes"; then printf "%s\n" "#define HAVE_HOST_IN_UTMPX 1" >>confdefs.h fi else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi # look for field 'syslen' in header 'utmpx.h' ossh_safe=`echo "utmpx.h" | sed 'y%./+-%__p_%'` ossh_varname="ossh_cv_$ossh_safe""_has_"syslen { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for syslen field in utmpx.h" >&5 printf %s "checking for syslen field in utmpx.h... " >&6; } if eval test \${$ossh_varname+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "syslen" >/dev/null 2>&1 then : eval "$ossh_varname=yes" else $as_nop eval "$ossh_varname=no" fi rm -rf conftest* fi ossh_result=`eval 'echo $'"$ossh_varname"` if test -n "`echo $ossh_varname`"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ossh_result" >&5 printf "%s\n" "$ossh_result" >&6; } if test "x$ossh_result" = "xyes"; then printf "%s\n" "#define HAVE_SYSLEN_IN_UTMPX 1" >>confdefs.h fi else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi # look for field 'ut_pid' in header 'utmp.h' ossh_safe=`echo "utmp.h" | sed 'y%./+-%__p_%'` ossh_varname="ossh_cv_$ossh_safe""_has_"ut_pid { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for ut_pid field in utmp.h" >&5 printf %s "checking for ut_pid field in utmp.h... " >&6; } if eval test \${$ossh_varname+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "ut_pid" >/dev/null 2>&1 then : eval "$ossh_varname=yes" else $as_nop eval "$ossh_varname=no" fi rm -rf conftest* fi ossh_result=`eval 'echo $'"$ossh_varname"` if test -n "`echo $ossh_varname`"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ossh_result" >&5 printf "%s\n" "$ossh_result" >&6; } if test "x$ossh_result" = "xyes"; then printf "%s\n" "#define HAVE_PID_IN_UTMP 1" >>confdefs.h fi else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi # look for field 'ut_type' in header 'utmp.h' ossh_safe=`echo "utmp.h" | sed 'y%./+-%__p_%'` ossh_varname="ossh_cv_$ossh_safe""_has_"ut_type { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for ut_type field in utmp.h" >&5 printf %s "checking for ut_type field in utmp.h... " >&6; } if eval test \${$ossh_varname+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "ut_type" >/dev/null 2>&1 then : eval "$ossh_varname=yes" else $as_nop eval "$ossh_varname=no" fi rm -rf conftest* fi ossh_result=`eval 'echo $'"$ossh_varname"` if test -n "`echo $ossh_varname`"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ossh_result" >&5 printf "%s\n" "$ossh_result" >&6; } if test "x$ossh_result" = "xyes"; then printf "%s\n" "#define HAVE_TYPE_IN_UTMP 1" >>confdefs.h fi else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi # look for field 'ut_type' in header 'utmpx.h' ossh_safe=`echo "utmpx.h" | sed 'y%./+-%__p_%'` ossh_varname="ossh_cv_$ossh_safe""_has_"ut_type { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for ut_type field in utmpx.h" >&5 printf %s "checking for ut_type field in utmpx.h... " >&6; } if eval test \${$ossh_varname+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "ut_type" >/dev/null 2>&1 then : eval "$ossh_varname=yes" else $as_nop eval "$ossh_varname=no" fi rm -rf conftest* fi ossh_result=`eval 'echo $'"$ossh_varname"` if test -n "`echo $ossh_varname`"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ossh_result" >&5 printf "%s\n" "$ossh_result" >&6; } if test "x$ossh_result" = "xyes"; then printf "%s\n" "#define HAVE_TYPE_IN_UTMPX 1" >>confdefs.h fi else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi # look for field 'ut_tv' in header 'utmp.h' ossh_safe=`echo "utmp.h" | sed 'y%./+-%__p_%'` ossh_varname="ossh_cv_$ossh_safe""_has_"ut_tv { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for ut_tv field in utmp.h" >&5 printf %s "checking for ut_tv field in utmp.h... " >&6; } if eval test \${$ossh_varname+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "ut_tv" >/dev/null 2>&1 then : eval "$ossh_varname=yes" else $as_nop eval "$ossh_varname=no" fi rm -rf conftest* fi ossh_result=`eval 'echo $'"$ossh_varname"` if test -n "`echo $ossh_varname`"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ossh_result" >&5 printf "%s\n" "$ossh_result" >&6; } if test "x$ossh_result" = "xyes"; then printf "%s\n" "#define HAVE_TV_IN_UTMP 1" >>confdefs.h fi else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi # look for field 'ut_id' in header 'utmp.h' ossh_safe=`echo "utmp.h" | sed 'y%./+-%__p_%'` ossh_varname="ossh_cv_$ossh_safe""_has_"ut_id { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for ut_id field in utmp.h" >&5 printf %s "checking for ut_id field in utmp.h... " >&6; } if eval test \${$ossh_varname+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "ut_id" >/dev/null 2>&1 then : eval "$ossh_varname=yes" else $as_nop eval "$ossh_varname=no" fi rm -rf conftest* fi ossh_result=`eval 'echo $'"$ossh_varname"` if test -n "`echo $ossh_varname`"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ossh_result" >&5 printf "%s\n" "$ossh_result" >&6; } if test "x$ossh_result" = "xyes"; then printf "%s\n" "#define HAVE_ID_IN_UTMP 1" >>confdefs.h fi else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi # look for field 'ut_id' in header 'utmpx.h' ossh_safe=`echo "utmpx.h" | sed 'y%./+-%__p_%'` ossh_varname="ossh_cv_$ossh_safe""_has_"ut_id { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for ut_id field in utmpx.h" >&5 printf %s "checking for ut_id field in utmpx.h... " >&6; } if eval test \${$ossh_varname+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "ut_id" >/dev/null 2>&1 then : eval "$ossh_varname=yes" else $as_nop eval "$ossh_varname=no" fi rm -rf conftest* fi ossh_result=`eval 'echo $'"$ossh_varname"` if test -n "`echo $ossh_varname`"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ossh_result" >&5 printf "%s\n" "$ossh_result" >&6; } if test "x$ossh_result" = "xyes"; then printf "%s\n" "#define HAVE_ID_IN_UTMPX 1" >>confdefs.h fi else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi # look for field 'ut_addr' in header 'utmp.h' ossh_safe=`echo "utmp.h" | sed 'y%./+-%__p_%'` ossh_varname="ossh_cv_$ossh_safe""_has_"ut_addr { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for ut_addr field in utmp.h" >&5 printf %s "checking for ut_addr field in utmp.h... " >&6; } if eval test \${$ossh_varname+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "ut_addr" >/dev/null 2>&1 then : eval "$ossh_varname=yes" else $as_nop eval "$ossh_varname=no" fi rm -rf conftest* fi ossh_result=`eval 'echo $'"$ossh_varname"` if test -n "`echo $ossh_varname`"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ossh_result" >&5 printf "%s\n" "$ossh_result" >&6; } if test "x$ossh_result" = "xyes"; then printf "%s\n" "#define HAVE_ADDR_IN_UTMP 1" >>confdefs.h fi else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi # look for field 'ut_addr' in header 'utmpx.h' ossh_safe=`echo "utmpx.h" | sed 'y%./+-%__p_%'` ossh_varname="ossh_cv_$ossh_safe""_has_"ut_addr { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for ut_addr field in utmpx.h" >&5 printf %s "checking for ut_addr field in utmpx.h... " >&6; } if eval test \${$ossh_varname+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "ut_addr" >/dev/null 2>&1 then : eval "$ossh_varname=yes" else $as_nop eval "$ossh_varname=no" fi rm -rf conftest* fi ossh_result=`eval 'echo $'"$ossh_varname"` if test -n "`echo $ossh_varname`"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ossh_result" >&5 printf "%s\n" "$ossh_result" >&6; } if test "x$ossh_result" = "xyes"; then printf "%s\n" "#define HAVE_ADDR_IN_UTMPX 1" >>confdefs.h fi else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi # look for field 'ut_addr_v6' in header 'utmp.h' ossh_safe=`echo "utmp.h" | sed 'y%./+-%__p_%'` ossh_varname="ossh_cv_$ossh_safe""_has_"ut_addr_v6 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for ut_addr_v6 field in utmp.h" >&5 printf %s "checking for ut_addr_v6 field in utmp.h... " >&6; } if eval test \${$ossh_varname+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "ut_addr_v6" >/dev/null 2>&1 then : eval "$ossh_varname=yes" else $as_nop eval "$ossh_varname=no" fi rm -rf conftest* fi ossh_result=`eval 'echo $'"$ossh_varname"` if test -n "`echo $ossh_varname`"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ossh_result" >&5 printf "%s\n" "$ossh_result" >&6; } if test "x$ossh_result" = "xyes"; then printf "%s\n" "#define HAVE_ADDR_V6_IN_UTMP 1" >>confdefs.h fi else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi # look for field 'ut_addr_v6' in header 'utmpx.h' ossh_safe=`echo "utmpx.h" | sed 'y%./+-%__p_%'` ossh_varname="ossh_cv_$ossh_safe""_has_"ut_addr_v6 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for ut_addr_v6 field in utmpx.h" >&5 printf %s "checking for ut_addr_v6 field in utmpx.h... " >&6; } if eval test \${$ossh_varname+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "ut_addr_v6" >/dev/null 2>&1 then : eval "$ossh_varname=yes" else $as_nop eval "$ossh_varname=no" fi rm -rf conftest* fi ossh_result=`eval 'echo $'"$ossh_varname"` if test -n "`echo $ossh_varname`"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ossh_result" >&5 printf "%s\n" "$ossh_result" >&6; } if test "x$ossh_result" = "xyes"; then printf "%s\n" "#define HAVE_ADDR_V6_IN_UTMPX 1" >>confdefs.h fi else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi # look for field 'ut_exit' in header 'utmp.h' ossh_safe=`echo "utmp.h" | sed 'y%./+-%__p_%'` ossh_varname="ossh_cv_$ossh_safe""_has_"ut_exit { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for ut_exit field in utmp.h" >&5 printf %s "checking for ut_exit field in utmp.h... " >&6; } if eval test \${$ossh_varname+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "ut_exit" >/dev/null 2>&1 then : eval "$ossh_varname=yes" else $as_nop eval "$ossh_varname=no" fi rm -rf conftest* fi ossh_result=`eval 'echo $'"$ossh_varname"` if test -n "`echo $ossh_varname`"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ossh_result" >&5 printf "%s\n" "$ossh_result" >&6; } if test "x$ossh_result" = "xyes"; then printf "%s\n" "#define HAVE_EXIT_IN_UTMP 1" >>confdefs.h fi else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi # look for field 'ut_time' in header 'utmp.h' ossh_safe=`echo "utmp.h" | sed 'y%./+-%__p_%'` ossh_varname="ossh_cv_$ossh_safe""_has_"ut_time { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for ut_time field in utmp.h" >&5 printf %s "checking for ut_time field in utmp.h... " >&6; } if eval test \${$ossh_varname+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "ut_time" >/dev/null 2>&1 then : eval "$ossh_varname=yes" else $as_nop eval "$ossh_varname=no" fi rm -rf conftest* fi ossh_result=`eval 'echo $'"$ossh_varname"` if test -n "`echo $ossh_varname`"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ossh_result" >&5 printf "%s\n" "$ossh_result" >&6; } if test "x$ossh_result" = "xyes"; then printf "%s\n" "#define HAVE_TIME_IN_UTMP 1" >>confdefs.h fi else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi # look for field 'ut_time' in header 'utmpx.h' ossh_safe=`echo "utmpx.h" | sed 'y%./+-%__p_%'` ossh_varname="ossh_cv_$ossh_safe""_has_"ut_time { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for ut_time field in utmpx.h" >&5 printf %s "checking for ut_time field in utmpx.h... " >&6; } if eval test \${$ossh_varname+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "ut_time" >/dev/null 2>&1 then : eval "$ossh_varname=yes" else $as_nop eval "$ossh_varname=no" fi rm -rf conftest* fi ossh_result=`eval 'echo $'"$ossh_varname"` if test -n "`echo $ossh_varname`"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ossh_result" >&5 printf "%s\n" "$ossh_result" >&6; } if test "x$ossh_result" = "xyes"; then printf "%s\n" "#define HAVE_TIME_IN_UTMPX 1" >>confdefs.h fi else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi # look for field 'ut_tv' in header 'utmpx.h' ossh_safe=`echo "utmpx.h" | sed 'y%./+-%__p_%'` ossh_varname="ossh_cv_$ossh_safe""_has_"ut_tv { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for ut_tv field in utmpx.h" >&5 printf %s "checking for ut_tv field in utmpx.h... " >&6; } if eval test \${$ossh_varname+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "ut_tv" >/dev/null 2>&1 then : eval "$ossh_varname=yes" else $as_nop eval "$ossh_varname=no" fi rm -rf conftest* fi ossh_result=`eval 'echo $'"$ossh_varname"` if test -n "`echo $ossh_varname`"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ossh_result" >&5 printf "%s\n" "$ossh_result" >&6; } if test "x$ossh_result" = "xyes"; then printf "%s\n" "#define HAVE_TV_IN_UTMPX 1" >>confdefs.h fi else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi # look for field 'ut_ss' in header 'utmpx.h' ossh_safe=`echo "utmpx.h" | sed 'y%./+-%__p_%'` ossh_varname="ossh_cv_$ossh_safe""_has_"ut_ss { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for ut_ss field in utmpx.h" >&5 printf %s "checking for ut_ss field in utmpx.h... " >&6; } if eval test \${$ossh_varname+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "ut_ss" >/dev/null 2>&1 then : eval "$ossh_varname=yes" else $as_nop eval "$ossh_varname=no" fi rm -rf conftest* fi ossh_result=`eval 'echo $'"$ossh_varname"` if test -n "`echo $ossh_varname`"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ossh_result" >&5 printf "%s\n" "$ossh_result" >&6; } if test "x$ossh_result" = "xyes"; then printf "%s\n" "#define HAVE_SS_IN_UTMPX 1" >>confdefs.h fi else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi ac_fn_c_check_member "$LINENO" "struct stat" "st_blksize" "ac_cv_member_struct_stat_st_blksize" "$ac_includes_default" if test "x$ac_cv_member_struct_stat_st_blksize" = xyes then : printf "%s\n" "#define HAVE_STRUCT_STAT_ST_BLKSIZE 1" >>confdefs.h fi ac_fn_c_check_member "$LINENO" "struct stat" "st_mtim" "ac_cv_member_struct_stat_st_mtim" "$ac_includes_default" if test "x$ac_cv_member_struct_stat_st_mtim" = xyes then : printf "%s\n" "#define HAVE_STRUCT_STAT_ST_MTIM 1" >>confdefs.h fi ac_fn_c_check_member "$LINENO" "struct stat" "st_mtime" "ac_cv_member_struct_stat_st_mtime" "$ac_includes_default" if test "x$ac_cv_member_struct_stat_st_mtime" = xyes then : printf "%s\n" "#define HAVE_STRUCT_STAT_ST_MTIME 1" >>confdefs.h fi ac_fn_c_check_member "$LINENO" "struct passwd" "pw_gecos" "ac_cv_member_struct_passwd_pw_gecos" " #include #include " if test "x$ac_cv_member_struct_passwd_pw_gecos" = xyes then : printf "%s\n" "#define HAVE_STRUCT_PASSWD_PW_GECOS 1" >>confdefs.h fi ac_fn_c_check_member "$LINENO" "struct passwd" "pw_class" "ac_cv_member_struct_passwd_pw_class" " #include #include " if test "x$ac_cv_member_struct_passwd_pw_class" = xyes then : printf "%s\n" "#define HAVE_STRUCT_PASSWD_PW_CLASS 1" >>confdefs.h fi ac_fn_c_check_member "$LINENO" "struct passwd" "pw_change" "ac_cv_member_struct_passwd_pw_change" " #include #include " if test "x$ac_cv_member_struct_passwd_pw_change" = xyes then : printf "%s\n" "#define HAVE_STRUCT_PASSWD_PW_CHANGE 1" >>confdefs.h fi ac_fn_c_check_member "$LINENO" "struct passwd" "pw_expire" "ac_cv_member_struct_passwd_pw_expire" " #include #include " if test "x$ac_cv_member_struct_passwd_pw_expire" = xyes then : printf "%s\n" "#define HAVE_STRUCT_PASSWD_PW_EXPIRE 1" >>confdefs.h fi ac_fn_c_check_member "$LINENO" "struct __res_state" "retrans" "ac_cv_member_struct___res_state_retrans" " #include #if HAVE_SYS_TYPES_H # include #endif #include #include #include " if test "x$ac_cv_member_struct___res_state_retrans" = xyes then : else $as_nop printf "%s\n" "#define __res_state state" >>confdefs.h fi ac_fn_c_check_member "$LINENO" "struct sockaddr_in" "sin_len" "ac_cv_member_struct_sockaddr_in_sin_len" " #include #include #include " if test "x$ac_cv_member_struct_sockaddr_in_sin_len" = xyes then : printf "%s\n" "#define SOCK_HAS_LEN 1" >>confdefs.h fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for ss_family field in struct sockaddr_storage" >&5 printf %s "checking for ss_family field in struct sockaddr_storage... " >&6; } if test ${ac_cv_have_ss_family_in_struct_ss+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main (void) { struct sockaddr_storage s; s.ss_family = 1; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_have_ss_family_in_struct_ss="yes" else $as_nop ac_cv_have_ss_family_in_struct_ss="no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_ss_family_in_struct_ss" >&5 printf "%s\n" "$ac_cv_have_ss_family_in_struct_ss" >&6; } if test "x$ac_cv_have_ss_family_in_struct_ss" = "xyes" ; then printf "%s\n" "#define HAVE_SS_FAMILY_IN_SS 1" >>confdefs.h fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for __ss_family field in struct sockaddr_storage" >&5 printf %s "checking for __ss_family field in struct sockaddr_storage... " >&6; } if test ${ac_cv_have___ss_family_in_struct_ss+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main (void) { struct sockaddr_storage s; s.__ss_family = 1; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_have___ss_family_in_struct_ss="yes" else $as_nop ac_cv_have___ss_family_in_struct_ss="no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___ss_family_in_struct_ss" >&5 printf "%s\n" "$ac_cv_have___ss_family_in_struct_ss" >&6; } if test "x$ac_cv_have___ss_family_in_struct_ss" = "xyes" ; then printf "%s\n" "#define HAVE___SS_FAMILY_IN_SS 1" >>confdefs.h fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for msg_accrights field in struct msghdr" >&5 printf %s "checking for msg_accrights field in struct msghdr... " >&6; } if test ${ac_cv_have_accrights_in_msghdr+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include int main (void) { #ifdef msg_accrights #error "msg_accrights is a macro" exit(1); #endif struct msghdr m; m.msg_accrights = 0; exit(0); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_have_accrights_in_msghdr="yes" else $as_nop ac_cv_have_accrights_in_msghdr="no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_accrights_in_msghdr" >&5 printf "%s\n" "$ac_cv_have_accrights_in_msghdr" >&6; } if test "x$ac_cv_have_accrights_in_msghdr" = "xyes" ; then printf "%s\n" "#define HAVE_ACCRIGHTS_IN_MSGHDR 1" >>confdefs.h fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if struct statvfs.f_fsid is integral type" >&5 printf %s "checking if struct statvfs.f_fsid is integral type... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #ifdef HAVE_SYS_TIME_H # include #endif #ifdef HAVE_SYS_MOUNT_H #include #endif #ifdef HAVE_SYS_STATVFS_H #include #endif int main (void) { struct statvfs s; s.f_fsid = 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if fsid_t has member val" >&5 printf %s "checking if fsid_t has member val... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main (void) { fsid_t t; t.val[0] = 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } printf "%s\n" "#define FSID_HAS_VAL 1" >>confdefs.h else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if f_fsid has member __val" >&5 printf %s "checking if f_fsid has member __val... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main (void) { fsid_t t; t.__val[0] = 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } printf "%s\n" "#define FSID_HAS___VAL 1" >>confdefs.h else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for msg_control field in struct msghdr" >&5 printf %s "checking for msg_control field in struct msghdr... " >&6; } if test ${ac_cv_have_control_in_msghdr+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include int main (void) { #ifdef msg_control #error "msg_control is a macro" exit(1); #endif struct msghdr m; m.msg_control = 0; exit(0); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_have_control_in_msghdr="yes" else $as_nop ac_cv_have_control_in_msghdr="no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_control_in_msghdr" >&5 printf "%s\n" "$ac_cv_have_control_in_msghdr" >&6; } if test "x$ac_cv_have_control_in_msghdr" = "xyes" ; then printf "%s\n" "#define HAVE_CONTROL_IN_MSGHDR 1" >>confdefs.h fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if libc defines __progname" >&5 printf %s "checking if libc defines __progname... " >&6; } if test ${ac_cv_libc_defines___progname+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { extern char *__progname; printf("%s", __progname); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_libc_defines___progname="yes" else $as_nop ac_cv_libc_defines___progname="no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_libc_defines___progname" >&5 printf "%s\n" "$ac_cv_libc_defines___progname" >&6; } if test "x$ac_cv_libc_defines___progname" = "xyes" ; then printf "%s\n" "#define HAVE___PROGNAME 1" >>confdefs.h fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC implements __FUNCTION__" >&5 printf %s "checking whether $CC implements __FUNCTION__... " >&6; } if test ${ac_cv_cc_implements___FUNCTION__+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { printf("%s", __FUNCTION__); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_cc_implements___FUNCTION__="yes" else $as_nop ac_cv_cc_implements___FUNCTION__="no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cc_implements___FUNCTION__" >&5 printf "%s\n" "$ac_cv_cc_implements___FUNCTION__" >&6; } if test "x$ac_cv_cc_implements___FUNCTION__" = "xyes" ; then printf "%s\n" "#define HAVE___FUNCTION__ 1" >>confdefs.h fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC implements __func__" >&5 printf %s "checking whether $CC implements __func__... " >&6; } if test ${ac_cv_cc_implements___func__+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { printf("%s", __func__); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_cc_implements___func__="yes" else $as_nop ac_cv_cc_implements___func__="no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cc_implements___func__" >&5 printf "%s\n" "$ac_cv_cc_implements___func__" >&6; } if test "x$ac_cv_cc_implements___func__" = "xyes" ; then printf "%s\n" "#define HAVE___func__ 1" >>confdefs.h fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether va_copy exists" >&5 printf %s "checking whether va_copy exists... " >&6; } if test ${ac_cv_have_va_copy+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include va_list x,y; int main (void) { va_copy(x,y); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_have_va_copy="yes" else $as_nop ac_cv_have_va_copy="no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_va_copy" >&5 printf "%s\n" "$ac_cv_have_va_copy" >&6; } if test "x$ac_cv_have_va_copy" = "xyes" ; then printf "%s\n" "#define HAVE_VA_COPY 1" >>confdefs.h fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether __va_copy exists" >&5 printf %s "checking whether __va_copy exists... " >&6; } if test ${ac_cv_have___va_copy+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include va_list x,y; int main (void) { __va_copy(x,y); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_have___va_copy="yes" else $as_nop ac_cv_have___va_copy="no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have___va_copy" >&5 printf "%s\n" "$ac_cv_have___va_copy" >&6; } if test "x$ac_cv_have___va_copy" = "xyes" ; then printf "%s\n" "#define HAVE___VA_COPY 1" >>confdefs.h fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether getopt has optreset support" >&5 printf %s "checking whether getopt has optreset support... " >&6; } if test ${ac_cv_have_getopt_optreset+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { extern int optreset; optreset = 0; ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_have_getopt_optreset="yes" else $as_nop ac_cv_have_getopt_optreset="no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_getopt_optreset" >&5 printf "%s\n" "$ac_cv_have_getopt_optreset" >&6; } if test "x$ac_cv_have_getopt_optreset" = "xyes" ; then printf "%s\n" "#define HAVE_GETOPT_OPTRESET 1" >>confdefs.h fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if libc defines sys_errlist" >&5 printf %s "checking if libc defines sys_errlist... " >&6; } if test ${ac_cv_libc_defines_sys_errlist+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { extern const char *const sys_errlist[]; printf("%s", sys_errlist[0]); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_libc_defines_sys_errlist="yes" else $as_nop ac_cv_libc_defines_sys_errlist="no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_libc_defines_sys_errlist" >&5 printf "%s\n" "$ac_cv_libc_defines_sys_errlist" >&6; } if test "x$ac_cv_libc_defines_sys_errlist" = "xyes" ; then printf "%s\n" "#define HAVE_SYS_ERRLIST 1" >>confdefs.h fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if libc defines sys_nerr" >&5 printf %s "checking if libc defines sys_nerr... " >&6; } if test ${ac_cv_libc_defines_sys_nerr+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { extern int sys_nerr; printf("%i", sys_nerr); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_libc_defines_sys_nerr="yes" else $as_nop ac_cv_libc_defines_sys_nerr="no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_libc_defines_sys_nerr" >&5 printf "%s\n" "$ac_cv_libc_defines_sys_nerr" >&6; } if test "x$ac_cv_libc_defines_sys_nerr" = "xyes" ; then printf "%s\n" "#define HAVE_SYS_NERR 1" >>confdefs.h fi # Check libraries needed by DNS fingerprint support { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing getrrsetbyname" >&5 printf %s "checking for library containing getrrsetbyname... " >&6; } if test ${ac_cv_search_getrrsetbyname+y} then : printf %s "(cached) " >&6 else $as_nop ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char getrrsetbyname (); int main (void) { return getrrsetbyname (); ; return 0; } _ACEOF for ac_lib in '' resolv do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO" then : ac_cv_search_getrrsetbyname=$ac_res fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext if test ${ac_cv_search_getrrsetbyname+y} then : break fi done if test ${ac_cv_search_getrrsetbyname+y} then : else $as_nop ac_cv_search_getrrsetbyname=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_getrrsetbyname" >&5 printf "%s\n" "$ac_cv_search_getrrsetbyname" >&6; } ac_res=$ac_cv_search_getrrsetbyname if test "$ac_res" != no then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" printf "%s\n" "#define HAVE_GETRRSETBYNAME 1" >>confdefs.h else $as_nop # Needed by our getrrsetbyname() { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing res_query" >&5 printf %s "checking for library containing res_query... " >&6; } if test ${ac_cv_search_res_query+y} then : printf %s "(cached) " >&6 else $as_nop ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char res_query (); int main (void) { return res_query (); ; return 0; } _ACEOF for ac_lib in '' resolv do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO" then : ac_cv_search_res_query=$ac_res fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext if test ${ac_cv_search_res_query+y} then : break fi done if test ${ac_cv_search_res_query+y} then : else $as_nop ac_cv_search_res_query=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_res_query" >&5 printf "%s\n" "$ac_cv_search_res_query" >&6; } ac_res=$ac_cv_search_res_query if test "$ac_res" != no then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing dn_expand" >&5 printf %s "checking for library containing dn_expand... " >&6; } if test ${ac_cv_search_dn_expand+y} then : printf %s "(cached) " >&6 else $as_nop ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char dn_expand (); int main (void) { return dn_expand (); ; return 0; } _ACEOF for ac_lib in '' resolv do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO" then : ac_cv_search_dn_expand=$ac_res fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext if test ${ac_cv_search_dn_expand+y} then : break fi done if test ${ac_cv_search_dn_expand+y} then : else $as_nop ac_cv_search_dn_expand=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_dn_expand" >&5 printf "%s\n" "$ac_cv_search_dn_expand" >&6; } ac_res=$ac_cv_search_dn_expand if test "$ac_res" != no then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if res_query will link" >&5 printf %s "checking if res_query will link... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include #include int main (void) { res_query (0, 0, 0, 0, 0); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } saved_LIBS="$LIBS" LIBS="$LIBS -lresolv" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for res_query in -lresolv" >&5 printf %s "checking for res_query in -lresolv... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include #include int main (void) { res_query (0, 0, 0, 0, 0); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else $as_nop LIBS="$saved_LIBS" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext ac_fn_c_check_func "$LINENO" "_getshort" "ac_cv_func__getshort" if test "x$ac_cv_func__getshort" = xyes then : printf "%s\n" "#define HAVE__GETSHORT 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "_getlong" "ac_cv_func__getlong" if test "x$ac_cv_func__getlong" = xyes then : printf "%s\n" "#define HAVE__GETLONG 1" >>confdefs.h fi ac_fn_check_decl "$LINENO" "_getshort" "ac_cv_have_decl__getshort" "#include #include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl__getshort" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL__GETSHORT $ac_have_decl" >>confdefs.h ac_fn_check_decl "$LINENO" "_getlong" "ac_cv_have_decl__getlong" "#include #include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl__getlong" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL__GETLONG $ac_have_decl" >>confdefs.h ac_fn_c_check_member "$LINENO" "HEADER" "ad" "ac_cv_member_HEADER_ad" "#include " if test "x$ac_cv_member_HEADER_ad" = xyes then : printf "%s\n" "#define HAVE_HEADER_AD 1" >>confdefs.h fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if struct __res_state _res is an extern" >&5 printf %s "checking if struct __res_state _res is an extern... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #if HAVE_SYS_TYPES_H # include #endif #include #include #include extern struct __res_state _res; int main (void) { struct __res_state *volatile p = &_res; /* force resolution of _res */ return 0; ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } printf "%s\n" "#define HAVE__RES_EXTERN 1" >>confdefs.h else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext # Check whether user wants SELinux support SELINUX_MSG="no" LIBSELINUX="" # Check whether --with-selinux was given. if test ${with_selinux+y} then : withval=$with_selinux; if test "x$withval" != "xno" ; then save_LIBS="$LIBS" printf "%s\n" "#define WITH_SELINUX 1" >>confdefs.h SELINUX_MSG="yes" ac_fn_c_check_header_compile "$LINENO" "selinux/selinux.h" "ac_cv_header_selinux_selinux_h" "$ac_includes_default" if test "x$ac_cv_header_selinux_selinux_h" = xyes then : else $as_nop as_fn_error $? "SELinux support requires selinux.h header" "$LINENO" 5 fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for setexeccon in -lselinux" >&5 printf %s "checking for setexeccon in -lselinux... " >&6; } if test ${ac_cv_lib_selinux_setexeccon+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lselinux $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char setexeccon (); int main (void) { return setexeccon (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_selinux_setexeccon=yes else $as_nop ac_cv_lib_selinux_setexeccon=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_selinux_setexeccon" >&5 printf "%s\n" "$ac_cv_lib_selinux_setexeccon" >&6; } if test "x$ac_cv_lib_selinux_setexeccon" = xyes then : LIBSELINUX="-lselinux" LIBS="$LIBS -lselinux" else $as_nop as_fn_error $? "SELinux support requires libselinux library" "$LINENO" 5 fi ac_fn_c_check_func "$LINENO" "getseuserbyname" "ac_cv_func_getseuserbyname" if test "x$ac_cv_func_getseuserbyname" = xyes then : printf "%s\n" "#define HAVE_GETSEUSERBYNAME 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "get_default_context_with_level" "ac_cv_func_get_default_context_with_level" if test "x$ac_cv_func_get_default_context_with_level" = xyes then : printf "%s\n" "#define HAVE_GET_DEFAULT_CONTEXT_WITH_LEVEL 1" >>confdefs.h fi LIBS="$save_LIBS $LIBSELINUX" fi fi # Check whether user wants Kerberos 5 support KRB5_MSG="no" # Check whether --with-kerberos5 was given. if test ${with_kerberos5+y} then : withval=$with_kerberos5; if test "x$withval" != "xno" ; then if test "x$withval" = "xyes" ; then KRB5ROOT="/usr/local" else KRB5ROOT=${withval} fi printf "%s\n" "#define KRB5 1" >>confdefs.h KRB5_MSG="yes" use_pkgconfig_for_krb5= if test "x$PKGCONFIG" != "xno"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $PKGCONFIG knows about kerberos5" >&5 printf %s "checking if $PKGCONFIG knows about kerberos5... " >&6; } if "$PKGCONFIG" krb5; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } use_pkgconfig_for_krb5=yes else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test "x$use_pkgconfig_for_krb5" = "xyes"; then K5CFLAGS=`$PKGCONFIG --cflags krb5` K5LIBS=`$PKGCONFIG --libs krb5` CPPFLAGS="$CPPFLAGS $K5CFLAGS" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for gssapi support" >&5 printf %s "checking for gssapi support... " >&6; } if "$PKGCONFIG" krb5-gssapi; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } printf "%s\n" "#define GSSAPI 1" >>confdefs.h GSSCFLAGS="`$PKGCONFIG --cflags krb5-gssapi`" GSSLIBS="`$PKGCONFIG --libs krb5-gssapi`" CPPFLAGS="$CPPFLAGS $GSSCFLAGS" else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether we are using Heimdal" >&5 printf %s "checking whether we are using Heimdal... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { char *tmp = heimdal_version; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } printf "%s\n" "#define HEIMDAL 1" >>confdefs.h else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext else if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}krb5-config", so it can be a program name with args. set dummy ${ac_tool_prefix}krb5-config; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_KRB5CONF+y} then : printf %s "(cached) " >&6 else $as_nop case $KRB5CONF in [\\/]* | ?:[\\/]*) ac_cv_path_KRB5CONF="$KRB5CONF" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_dummy="$KRB5ROOT/bin:$PATH" for as_dir in $as_dummy do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_path_KRB5CONF="$as_dir$ac_word$ac_exec_ext" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi KRB5CONF=$ac_cv_path_KRB5CONF if test -n "$KRB5CONF"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $KRB5CONF" >&5 printf "%s\n" "$KRB5CONF" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_path_KRB5CONF"; then ac_pt_KRB5CONF=$KRB5CONF # Extract the first word of "krb5-config", so it can be a program name with args. set dummy krb5-config; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_ac_pt_KRB5CONF+y} then : printf %s "(cached) " >&6 else $as_nop case $ac_pt_KRB5CONF in [\\/]* | ?:[\\/]*) ac_cv_path_ac_pt_KRB5CONF="$ac_pt_KRB5CONF" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_dummy="$KRB5ROOT/bin:$PATH" for as_dir in $as_dummy do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_path_ac_pt_KRB5CONF="$as_dir$ac_word$ac_exec_ext" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi ac_pt_KRB5CONF=$ac_cv_path_ac_pt_KRB5CONF if test -n "$ac_pt_KRB5CONF"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_pt_KRB5CONF" >&5 printf "%s\n" "$ac_pt_KRB5CONF" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_pt_KRB5CONF" = x; then KRB5CONF="$KRB5ROOT/bin/krb5-config" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac KRB5CONF=$ac_pt_KRB5CONF fi else KRB5CONF="$ac_cv_path_KRB5CONF" fi if test -x $KRB5CONF ; then K5CFLAGS="`$KRB5CONF --cflags`" K5LIBS="`$KRB5CONF --libs`" CPPFLAGS="$CPPFLAGS $K5CFLAGS" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for gssapi support" >&5 printf %s "checking for gssapi support... " >&6; } if $KRB5CONF | grep gssapi >/dev/null ; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } printf "%s\n" "#define GSSAPI 1" >>confdefs.h GSSCFLAGS="`$KRB5CONF --cflags gssapi`" GSSLIBS="`$KRB5CONF --libs gssapi`" CPPFLAGS="$CPPFLAGS $GSSCFLAGS" else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether we are using Heimdal" >&5 printf %s "checking whether we are using Heimdal... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { char *tmp = heimdal_version; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } printf "%s\n" "#define HEIMDAL 1" >>confdefs.h else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext else CPPFLAGS="$CPPFLAGS -I${KRB5ROOT}/include" LDFLAGS="$LDFLAGS -L${KRB5ROOT}/lib" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether we are using Heimdal" >&5 printf %s "checking whether we are using Heimdal... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { char *tmp = heimdal_version; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } printf "%s\n" "#define HEIMDAL 1" >>confdefs.h K5LIBS="-lkrb5" K5LIBS="$K5LIBS -lcom_err -lasn1" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for net_write in -lroken" >&5 printf %s "checking for net_write in -lroken... " >&6; } if test ${ac_cv_lib_roken_net_write+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lroken $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char net_write (); int main (void) { return net_write (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_roken_net_write=yes else $as_nop ac_cv_lib_roken_net_write=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_roken_net_write" >&5 printf "%s\n" "$ac_cv_lib_roken_net_write" >&6; } if test "x$ac_cv_lib_roken_net_write" = xyes then : K5LIBS="$K5LIBS -lroken" fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for des_cbc_encrypt in -ldes" >&5 printf %s "checking for des_cbc_encrypt in -ldes... " >&6; } if test ${ac_cv_lib_des_des_cbc_encrypt+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-ldes $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char des_cbc_encrypt (); int main (void) { return des_cbc_encrypt (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_des_des_cbc_encrypt=yes else $as_nop ac_cv_lib_des_des_cbc_encrypt=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_des_des_cbc_encrypt" >&5 printf "%s\n" "$ac_cv_lib_des_des_cbc_encrypt" >&6; } if test "x$ac_cv_lib_des_des_cbc_encrypt" = xyes then : K5LIBS="$K5LIBS -ldes" fi else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } K5LIBS="-lkrb5 -lk5crypto -lcom_err" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing dn_expand" >&5 printf %s "checking for library containing dn_expand... " >&6; } if test ${ac_cv_search_dn_expand+y} then : printf %s "(cached) " >&6 else $as_nop ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char dn_expand (); int main (void) { return dn_expand (); ; return 0; } _ACEOF for ac_lib in '' resolv do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO" then : ac_cv_search_dn_expand=$ac_res fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext if test ${ac_cv_search_dn_expand+y} then : break fi done if test ${ac_cv_search_dn_expand+y} then : else $as_nop ac_cv_search_dn_expand=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_dn_expand" >&5 printf "%s\n" "$ac_cv_search_dn_expand" >&6; } ac_res=$ac_cv_search_dn_expand if test "$ac_res" != no then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for gss_init_sec_context in -lgssapi_krb5" >&5 printf %s "checking for gss_init_sec_context in -lgssapi_krb5... " >&6; } if test ${ac_cv_lib_gssapi_krb5_gss_init_sec_context+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lgssapi_krb5 $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char gss_init_sec_context (); int main (void) { return gss_init_sec_context (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_gssapi_krb5_gss_init_sec_context=yes else $as_nop ac_cv_lib_gssapi_krb5_gss_init_sec_context=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_gssapi_krb5_gss_init_sec_context" >&5 printf "%s\n" "$ac_cv_lib_gssapi_krb5_gss_init_sec_context" >&6; } if test "x$ac_cv_lib_gssapi_krb5_gss_init_sec_context" = xyes then : printf "%s\n" "#define GSSAPI 1" >>confdefs.h GSSLIBS="-lgssapi_krb5" else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for gss_init_sec_context in -lgssapi" >&5 printf %s "checking for gss_init_sec_context in -lgssapi... " >&6; } if test ${ac_cv_lib_gssapi_gss_init_sec_context+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lgssapi $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char gss_init_sec_context (); int main (void) { return gss_init_sec_context (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_gssapi_gss_init_sec_context=yes else $as_nop ac_cv_lib_gssapi_gss_init_sec_context=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_gssapi_gss_init_sec_context" >&5 printf "%s\n" "$ac_cv_lib_gssapi_gss_init_sec_context" >&6; } if test "x$ac_cv_lib_gssapi_gss_init_sec_context" = xyes then : printf "%s\n" "#define GSSAPI 1" >>confdefs.h GSSLIBS="-lgssapi" else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for gss_init_sec_context in -lgss" >&5 printf %s "checking for gss_init_sec_context in -lgss... " >&6; } if test ${ac_cv_lib_gss_gss_init_sec_context+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lgss $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char gss_init_sec_context (); int main (void) { return gss_init_sec_context (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_gss_gss_init_sec_context=yes else $as_nop ac_cv_lib_gss_gss_init_sec_context=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_gss_gss_init_sec_context" >&5 printf "%s\n" "$ac_cv_lib_gss_gss_init_sec_context" >&6; } if test "x$ac_cv_lib_gss_gss_init_sec_context" = xyes then : printf "%s\n" "#define GSSAPI 1" >>confdefs.h GSSLIBS="-lgss" else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: Cannot find any suitable gss-api library - build may fail" >&5 printf "%s\n" "$as_me: WARNING: Cannot find any suitable gss-api library - build may fail" >&2;} fi fi fi ac_fn_c_check_header_compile "$LINENO" "gssapi.h" "ac_cv_header_gssapi_h" "$ac_includes_default" if test "x$ac_cv_header_gssapi_h" = xyes then : else $as_nop unset ac_cv_header_gssapi_h CPPFLAGS="$CPPFLAGS -I${KRB5ROOT}/include/gssapi" for ac_header in gssapi.h do : ac_fn_c_check_header_compile "$LINENO" "gssapi.h" "ac_cv_header_gssapi_h" "$ac_includes_default" if test "x$ac_cv_header_gssapi_h" = xyes then : printf "%s\n" "#define HAVE_GSSAPI_H 1" >>confdefs.h else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: Cannot find any suitable gss-api header - build may fail" >&5 printf "%s\n" "$as_me: WARNING: Cannot find any suitable gss-api header - build may fail" >&2;} fi done fi oldCPP="$CPPFLAGS" CPPFLAGS="$CPPFLAGS -I${KRB5ROOT}/include/gssapi" ac_fn_c_check_header_compile "$LINENO" "gssapi_krb5.h" "ac_cv_header_gssapi_krb5_h" "$ac_includes_default" if test "x$ac_cv_header_gssapi_krb5_h" = xyes then : else $as_nop CPPFLAGS="$oldCPP" fi fi fi if test -n "${rpath_opt}" ; then LDFLAGS="$LDFLAGS ${rpath_opt}${KRB5ROOT}/lib" fi if test ! -z "$blibpath" ; then blibpath="$blibpath:${KRB5ROOT}/lib" fi ac_fn_c_check_header_compile "$LINENO" "gssapi.h" "ac_cv_header_gssapi_h" "$ac_includes_default" if test "x$ac_cv_header_gssapi_h" = xyes then : printf "%s\n" "#define HAVE_GSSAPI_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "gssapi/gssapi.h" "ac_cv_header_gssapi_gssapi_h" "$ac_includes_default" if test "x$ac_cv_header_gssapi_gssapi_h" = xyes then : printf "%s\n" "#define HAVE_GSSAPI_GSSAPI_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "gssapi_krb5.h" "ac_cv_header_gssapi_krb5_h" "$ac_includes_default" if test "x$ac_cv_header_gssapi_krb5_h" = xyes then : printf "%s\n" "#define HAVE_GSSAPI_KRB5_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "gssapi/gssapi_krb5.h" "ac_cv_header_gssapi_gssapi_krb5_h" "$ac_includes_default" if test "x$ac_cv_header_gssapi_gssapi_krb5_h" = xyes then : printf "%s\n" "#define HAVE_GSSAPI_GSSAPI_KRB5_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "gssapi_generic.h" "ac_cv_header_gssapi_generic_h" "$ac_includes_default" if test "x$ac_cv_header_gssapi_generic_h" = xyes then : printf "%s\n" "#define HAVE_GSSAPI_GENERIC_H 1" >>confdefs.h fi ac_fn_c_check_header_compile "$LINENO" "gssapi/gssapi_generic.h" "ac_cv_header_gssapi_gssapi_generic_h" "$ac_includes_default" if test "x$ac_cv_header_gssapi_gssapi_generic_h" = xyes then : printf "%s\n" "#define HAVE_GSSAPI_GSSAPI_GENERIC_H 1" >>confdefs.h fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing k_hasafs" >&5 printf %s "checking for library containing k_hasafs... " >&6; } if test ${ac_cv_search_k_hasafs+y} then : printf %s "(cached) " >&6 else $as_nop ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char k_hasafs (); int main (void) { return k_hasafs (); ; return 0; } _ACEOF for ac_lib in '' kafs do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO" then : ac_cv_search_k_hasafs=$ac_res fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext if test ${ac_cv_search_k_hasafs+y} then : break fi done if test ${ac_cv_search_k_hasafs+y} then : else $as_nop ac_cv_search_k_hasafs=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_k_hasafs" >&5 printf "%s\n" "$ac_cv_search_k_hasafs" >&6; } ac_res=$ac_cv_search_k_hasafs if test "$ac_res" != no then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" printf "%s\n" "#define USE_AFS 1" >>confdefs.h fi ac_fn_check_decl "$LINENO" "GSS_C_NT_HOSTBASED_SERVICE" "ac_cv_have_decl_GSS_C_NT_HOSTBASED_SERVICE" " #ifdef HAVE_GSSAPI_H # include #elif defined(HAVE_GSSAPI_GSSAPI_H) # include #endif #ifdef HAVE_GSSAPI_GENERIC_H # include #elif defined(HAVE_GSSAPI_GSSAPI_GENERIC_H) # include #endif " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_GSS_C_NT_HOSTBASED_SERVICE" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_GSS_C_NT_HOSTBASED_SERVICE $ac_have_decl" >>confdefs.h saved_LIBS="$LIBS" LIBS="$LIBS $K5LIBS" ac_fn_c_check_func "$LINENO" "krb5_cc_new_unique" "ac_cv_func_krb5_cc_new_unique" if test "x$ac_cv_func_krb5_cc_new_unique" = xyes then : printf "%s\n" "#define HAVE_KRB5_CC_NEW_UNIQUE 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "krb5_get_error_message" "ac_cv_func_krb5_get_error_message" if test "x$ac_cv_func_krb5_get_error_message" = xyes then : printf "%s\n" "#define HAVE_KRB5_GET_ERROR_MESSAGE 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "krb5_free_error_message" "ac_cv_func_krb5_free_error_message" if test "x$ac_cv_func_krb5_free_error_message" = xyes then : printf "%s\n" "#define HAVE_KRB5_FREE_ERROR_MESSAGE 1" >>confdefs.h fi LIBS="$saved_LIBS" fi fi # Looking for programs, paths and files PRIVSEP_PATH=/var/empty # Check whether --with-privsep-path was given. if test ${with_privsep_path+y} then : withval=$with_privsep_path; if test -n "$withval" && test "x$withval" != "xno" && \ test "x${withval}" != "xyes"; then PRIVSEP_PATH=$withval fi fi # Check whether --with-xauth was given. if test ${with_xauth+y} then : withval=$with_xauth; if test -n "$withval" && test "x$withval" != "xno" && \ test "x${withval}" != "xyes"; then xauth_path=$withval fi else $as_nop TestPath="$PATH" TestPath="${TestPath}${PATH_SEPARATOR}/usr/X/bin" TestPath="${TestPath}${PATH_SEPARATOR}/usr/bin/X11" TestPath="${TestPath}${PATH_SEPARATOR}/usr/X11R6/bin" TestPath="${TestPath}${PATH_SEPARATOR}/usr/openwin/bin" # Extract the first word of "xauth", so it can be a program name with args. set dummy xauth; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_xauth_path+y} then : printf %s "(cached) " >&6 else $as_nop case $xauth_path in [\\/]* | ?:[\\/]*) ac_cv_path_xauth_path="$xauth_path" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $TestPath do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_path_xauth_path="$as_dir$ac_word$ac_exec_ext" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi xauth_path=$ac_cv_path_xauth_path if test -n "$xauth_path"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $xauth_path" >&5 printf "%s\n" "$xauth_path" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if (test ! -z "$xauth_path" && test -x "/usr/openwin/bin/xauth") ; then xauth_path="/usr/openwin/bin/xauth" fi fi STRIP_OPT=-s # Check whether --enable-strip was given. if test ${enable_strip+y} then : enableval=$enable_strip; if test "x$enableval" = "xno" ; then STRIP_OPT= fi fi if test -z "$xauth_path" ; then XAUTH_PATH="undefined" else printf "%s\n" "#define XAUTH_PATH \"$xauth_path\"" >>confdefs.h XAUTH_PATH=$xauth_path fi # Check for mail directory # Check whether --with-maildir was given. if test ${with_maildir+y} then : withval=$with_maildir; if test "X$withval" != X && test "x$withval" != xno && \ test "x${withval}" != xyes; then printf "%s\n" "#define MAIL_DIRECTORY \"$withval\"" >>confdefs.h fi else $as_nop if test "X$maildir" != "X"; then printf "%s\n" "#define MAIL_DIRECTORY \"$maildir\"" >>confdefs.h else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking Discovering system mail directory" >&5 printf %s "checking Discovering system mail directory... " >&6; } if test "$cross_compiling" = yes then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cross compiling: use --with-maildir=/path/to/mail" >&5 printf "%s\n" "$as_me: WARNING: cross compiling: use --with-maildir=/path/to/mail" >&2;} else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #ifdef HAVE_PATHS_H #include #endif #ifdef HAVE_MAILLOCK_H #include #endif #define DATA "conftest.maildir" int main (void) { FILE *fd; int rc; fd = fopen(DATA,"w"); if(fd == NULL) exit(1); #if defined (_PATH_MAILDIR) if ((rc = fprintf(fd ,"_PATH_MAILDIR:%s\n", _PATH_MAILDIR)) <0) exit(1); #elif defined (MAILDIR) if ((rc = fprintf(fd ,"MAILDIR:%s\n", MAILDIR)) <0) exit(1); #elif defined (_PATH_MAIL) if ((rc = fprintf(fd ,"_PATH_MAIL:%s\n", _PATH_MAIL)) <0) exit(1); #else exit (2); #endif exit(0); ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO" then : maildir_what=`awk -F: '{print $1}' conftest.maildir` maildir=`awk -F: '{print $2}' conftest.maildir \ | sed 's|/$||'` { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Using: $maildir from $maildir_what" >&5 printf "%s\n" "Using: $maildir from $maildir_what" >&6; } if test "x$maildir_what" != "x_PATH_MAILDIR"; then printf "%s\n" "#define MAIL_DIRECTORY \"$maildir\"" >>confdefs.h fi else $as_nop if test "X$ac_status" = "X2";then # our test program didn't find it. Default to /var/spool/mail { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Using: default value of /var/spool/mail" >&5 printf "%s\n" "Using: default value of /var/spool/mail" >&6; } printf "%s\n" "#define MAIL_DIRECTORY \"/var/spool/mail\"" >>confdefs.h else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: *** not found ***" >&5 printf "%s\n" "*** not found ***" >&6; } fi fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi fi # maildir if test ! -z "$cross_compiling" && test "x$cross_compiling" = "xyes"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cross compiling: Disabling /dev/ptmx test" >&5 printf "%s\n" "$as_me: WARNING: cross compiling: Disabling /dev/ptmx test" >&2;} disable_ptmx_check=yes fi if test -z "$no_dev_ptmx" ; then if test "x$disable_ptmx_check" != "xyes" ; then as_ac_File=`printf "%s\n" "ac_cv_file_"/dev/ptmx"" | $as_tr_sh` { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for \"/dev/ptmx\"" >&5 printf %s "checking for \"/dev/ptmx\"... " >&6; } if eval test \${$as_ac_File+y} then : printf %s "(cached) " >&6 else $as_nop test "$cross_compiling" = yes && as_fn_error $? "cannot check for file existence when cross compiling" "$LINENO" 5 if test -r ""/dev/ptmx""; then eval "$as_ac_File=yes" else eval "$as_ac_File=no" fi fi eval ac_res=\$$as_ac_File { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 printf "%s\n" "$ac_res" >&6; } if eval test \"x\$"$as_ac_File"\" = x"yes" then : printf "%s\n" "#define HAVE_DEV_PTMX 1" >>confdefs.h have_dev_ptmx=1 fi fi fi if test ! -z "$cross_compiling" && test "x$cross_compiling" != "xyes"; then as_ac_File=`printf "%s\n" "ac_cv_file_"/dev/ptc"" | $as_tr_sh` { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for \"/dev/ptc\"" >&5 printf %s "checking for \"/dev/ptc\"... " >&6; } if eval test \${$as_ac_File+y} then : printf %s "(cached) " >&6 else $as_nop test "$cross_compiling" = yes && as_fn_error $? "cannot check for file existence when cross compiling" "$LINENO" 5 if test -r ""/dev/ptc""; then eval "$as_ac_File=yes" else eval "$as_ac_File=no" fi fi eval ac_res=\$$as_ac_File { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 printf "%s\n" "$ac_res" >&6; } if eval test \"x\$"$as_ac_File"\" = x"yes" then : printf "%s\n" "#define HAVE_DEV_PTS_AND_PTC 1" >>confdefs.h have_dev_ptc=1 fi else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cross compiling: Disabling /dev/ptc test" >&5 printf "%s\n" "$as_me: WARNING: cross compiling: Disabling /dev/ptc test" >&2;} fi # Options from here on. Some of these are preset by platform above # Check whether --with-mantype was given. if test ${with_mantype+y} then : withval=$with_mantype; case "$withval" in man|cat|doc) MANTYPE=$withval ;; *) as_fn_error $? "invalid man type: $withval" "$LINENO" 5 ;; esac fi if test -z "$MANTYPE"; then if ${MANDOC} ${srcdir}/ssh.1 >/dev/null 2>&1; then MANTYPE=doc elif ${NROFF} -mdoc ${srcdir}/ssh.1 >/dev/null 2>&1; then MANTYPE=doc elif ${NROFF} -man ${srcdir}/ssh.1 >/dev/null 2>&1; then MANTYPE=man else MANTYPE=cat fi fi if test "$MANTYPE" = "doc"; then mansubdir=man; else mansubdir=$MANTYPE; fi # Whether to disable shadow password support # Check whether --with-shadow was given. if test ${with_shadow+y} then : withval=$with_shadow; if test "x$withval" = "xno" ; then printf "%s\n" "#define DISABLE_SHADOW 1" >>confdefs.h disable_shadow=yes fi fi if test -z "$disable_shadow" ; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if the systems has expire shadow information" >&5 printf %s "checking if the systems has expire shadow information... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include struct spwd sp; int main (void) { sp.sp_expire = sp.sp_lstchg = sp.sp_inact = 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : sp_expire_available=yes fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext if test "x$sp_expire_available" = "xyes" ; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } printf "%s\n" "#define HAS_SHADOW_EXPIRE 1" >>confdefs.h else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi # Use ip address instead of hostname in $DISPLAY if test ! -z "$IPADDR_IN_DISPLAY" ; then DISPLAY_HACK_MSG="yes" printf "%s\n" "#define IPADDR_IN_DISPLAY 1" >>confdefs.h else DISPLAY_HACK_MSG="no" # Check whether --with-ipaddr-display was given. if test ${with_ipaddr_display+y} then : withval=$with_ipaddr_display; if test "x$withval" != "xno" ; then printf "%s\n" "#define IPADDR_IN_DISPLAY 1" >>confdefs.h DISPLAY_HACK_MSG="yes" fi fi fi # check for /etc/default/login and use it if present. # Check whether --enable-etc-default-login was given. if test ${enable_etc_default_login+y} then : enableval=$enable_etc_default_login; if test "x$enableval" = "xno"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: /etc/default/login handling disabled" >&5 printf "%s\n" "$as_me: /etc/default/login handling disabled" >&6;} etc_default_login=no else etc_default_login=yes fi else $as_nop if test ! -z "$cross_compiling" && test "x$cross_compiling" = "xyes"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cross compiling: not checking /etc/default/login" >&5 printf "%s\n" "$as_me: WARNING: cross compiling: not checking /etc/default/login" >&2;} etc_default_login=no else etc_default_login=yes fi fi if test "x$etc_default_login" != "xno"; then as_ac_File=`printf "%s\n" "ac_cv_file_"/etc/default/login"" | $as_tr_sh` { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for \"/etc/default/login\"" >&5 printf %s "checking for \"/etc/default/login\"... " >&6; } if eval test \${$as_ac_File+y} then : printf %s "(cached) " >&6 else $as_nop test "$cross_compiling" = yes && as_fn_error $? "cannot check for file existence when cross compiling" "$LINENO" 5 if test -r ""/etc/default/login""; then eval "$as_ac_File=yes" else eval "$as_ac_File=no" fi fi eval ac_res=\$$as_ac_File { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 printf "%s\n" "$ac_res" >&6; } if eval test \"x\$"$as_ac_File"\" = x"yes" then : external_path_file=/etc/default/login fi if test "x$external_path_file" = "x/etc/default/login"; then printf "%s\n" "#define HAVE_ETC_DEFAULT_LOGIN 1" >>confdefs.h fi fi if test $ac_cv_func_login_getcapbool = "yes" && \ test $ac_cv_header_login_cap_h = "yes" ; then external_path_file=/etc/login.conf fi # Whether to mess with the default path SERVER_PATH_MSG="(default)" # Check whether --with-default-path was given. if test ${with_default_path+y} then : withval=$with_default_path; if test "x$external_path_file" = "x/etc/login.conf" ; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: --with-default-path=PATH has no effect on this system. Edit /etc/login.conf instead." >&5 printf "%s\n" "$as_me: WARNING: --with-default-path=PATH has no effect on this system. Edit /etc/login.conf instead." >&2;} elif test "x$withval" != "xno" ; then if test ! -z "$external_path_file" ; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: --with-default-path=PATH will only be used if PATH is not defined in $external_path_file ." >&5 printf "%s\n" "$as_me: WARNING: --with-default-path=PATH will only be used if PATH is not defined in $external_path_file ." >&2;} fi user_path="$withval" SERVER_PATH_MSG="$withval" fi else $as_nop if test "x$external_path_file" = "x/etc/login.conf" ; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: Make sure the path to scp is in /etc/login.conf" >&5 printf "%s\n" "$as_me: WARNING: Make sure the path to scp is in /etc/login.conf" >&2;} else if test ! -z "$external_path_file" ; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: If PATH is defined in $external_path_file, ensure the path to scp is included, otherwise scp will not work." >&5 printf "%s\n" "$as_me: WARNING: If PATH is defined in $external_path_file, ensure the path to scp is included, otherwise scp will not work." >&2;} fi if test "$cross_compiling" = yes then : user_path="/usr/bin:/bin:/usr/sbin:/sbin" else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* find out what STDPATH is */ #include #include #ifdef HAVE_PATHS_H # include #endif #ifndef _PATH_STDPATH # ifdef _PATH_USERPATH /* Irix */ # define _PATH_STDPATH _PATH_USERPATH # else # define _PATH_STDPATH "/usr/bin:/bin:/usr/sbin:/sbin" # endif #endif #include #include #include #define DATA "conftest.stdpath" int main (void) { FILE *fd; int rc; fd = fopen(DATA,"w"); if(fd == NULL) exit(1); if ((rc = fprintf(fd,"%s", _PATH_STDPATH)) < 0) exit(1); exit(0); ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO" then : user_path=`cat conftest.stdpath` else $as_nop user_path="/usr/bin:/bin:/usr/sbin:/sbin" fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi # make sure $bindir is in USER_PATH so scp will work t_bindir="${bindir}" while echo "${t_bindir}" | egrep '\$\{|NONE/' >/dev/null 2>&1; do t_bindir=`eval echo ${t_bindir}` case $t_bindir in NONE/*) t_bindir=`echo $t_bindir | sed "s~NONE~$prefix~"` ;; esac case $t_bindir in NONE/*) t_bindir=`echo $t_bindir | sed "s~NONE~$ac_default_prefix~"` ;; esac done echo $user_path | grep ":$t_bindir" > /dev/null 2>&1 if test $? -ne 0 ; then echo $user_path | grep "^$t_bindir" > /dev/null 2>&1 if test $? -ne 0 ; then user_path=$user_path:$t_bindir { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Adding $t_bindir to USER_PATH so scp will work" >&5 printf "%s\n" "Adding $t_bindir to USER_PATH so scp will work" >&6; } fi fi fi fi if test "x$external_path_file" != "x/etc/login.conf" ; then printf "%s\n" "#define USER_PATH \"$user_path\"" >>confdefs.h fi # Set superuser path separately to user path # Check whether --with-superuser-path was given. if test ${with_superuser_path+y} then : withval=$with_superuser_path; if test -n "$withval" && test "x$withval" != "xno" && \ test "x${withval}" != "xyes"; then printf "%s\n" "#define SUPERUSER_PATH \"$withval\"" >>confdefs.h superuser_path=$withval fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if we need to convert IPv4 in IPv6-mapped addresses" >&5 printf %s "checking if we need to convert IPv4 in IPv6-mapped addresses... " >&6; } IPV4_IN6_HACK_MSG="no" # Check whether --with-4in6 was given. if test ${with_4in6+y} then : withval=$with_4in6; if test "x$withval" != "xno" ; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } printf "%s\n" "#define IPV4_IN_IPV6 1" >>confdefs.h IPV4_IN6_HACK_MSG="yes" else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi else $as_nop if test "x$inet6_default_4in6" = "xyes"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes (default)" >&5 printf "%s\n" "yes (default)" >&6; } printf "%s\n" "#define IPV4_IN_IPV6 1" >>confdefs.h IPV4_IN6_HACK_MSG="yes" else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no (default)" >&5 printf "%s\n" "no (default)" >&6; } fi fi # Whether to enable BSD auth support BSD_AUTH_MSG=no # Check whether --with-bsd-auth was given. if test ${with_bsd_auth+y} then : withval=$with_bsd_auth; if test "x$withval" != "xno" ; then printf "%s\n" "#define BSD_AUTH 1" >>confdefs.h BSD_AUTH_MSG=yes fi fi # Where to place sshd.pid piddir=/var/run # make sure the directory exists if test ! -d $piddir ; then piddir=`eval echo ${sysconfdir}` case $piddir in NONE/*) piddir=`echo $piddir | sed "s~NONE~$ac_default_prefix~"` ;; esac fi # Check whether --with-pid-dir was given. if test ${with_pid_dir+y} then : withval=$with_pid_dir; if test -n "$withval" && test "x$withval" != "xno" && \ test "x${withval}" != "xyes"; then piddir=$withval if test ! -d $piddir ; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: ** no $piddir directory on this system **" >&5 printf "%s\n" "$as_me: WARNING: ** no $piddir directory on this system **" >&2;} fi fi fi printf "%s\n" "#define _PATH_SSH_PIDDIR \"$piddir\"" >>confdefs.h # Check whether --enable-lastlog was given. if test ${enable_lastlog+y} then : enableval=$enable_lastlog; if test "x$enableval" = "xno" ; then printf "%s\n" "#define DISABLE_LASTLOG 1" >>confdefs.h fi fi # Check whether --enable-utmp was given. if test ${enable_utmp+y} then : enableval=$enable_utmp; if test "x$enableval" = "xno" ; then printf "%s\n" "#define DISABLE_UTMP 1" >>confdefs.h fi fi # Check whether --enable-utmpx was given. if test ${enable_utmpx+y} then : enableval=$enable_utmpx; if test "x$enableval" = "xno" ; then printf "%s\n" "#define DISABLE_UTMPX 1" >>confdefs.h fi fi # Check whether --enable-wtmp was given. if test ${enable_wtmp+y} then : enableval=$enable_wtmp; if test "x$enableval" = "xno" ; then printf "%s\n" "#define DISABLE_WTMP 1" >>confdefs.h fi fi # Check whether --enable-wtmpx was given. if test ${enable_wtmpx+y} then : enableval=$enable_wtmpx; if test "x$enableval" = "xno" ; then printf "%s\n" "#define DISABLE_WTMPX 1" >>confdefs.h fi fi # Check whether --enable-libutil was given. if test ${enable_libutil+y} then : enableval=$enable_libutil; if test "x$enableval" = "xno" ; then printf "%s\n" "#define DISABLE_LOGIN 1" >>confdefs.h fi fi # Check whether --enable-pututline was given. if test ${enable_pututline+y} then : enableval=$enable_pututline; if test "x$enableval" = "xno" ; then printf "%s\n" "#define DISABLE_PUTUTLINE 1" >>confdefs.h fi fi # Check whether --enable-pututxline was given. if test ${enable_pututxline+y} then : enableval=$enable_pututxline; if test "x$enableval" = "xno" ; then printf "%s\n" "#define DISABLE_PUTUTXLINE 1" >>confdefs.h fi fi # Check whether --with-lastlog was given. if test ${with_lastlog+y} then : withval=$with_lastlog; if test "x$withval" = "xno" ; then printf "%s\n" "#define DISABLE_LASTLOG 1" >>confdefs.h elif test -n "$withval" && test "x${withval}" != "xyes"; then conf_lastlog_location=$withval fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if your system defines LASTLOG_FILE" >&5 printf %s "checking if your system defines LASTLOG_FILE... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #ifdef HAVE_LASTLOG_H # include #endif #ifdef HAVE_PATHS_H # include #endif #ifdef HAVE_LOGIN_H # include #endif int main (void) { char *lastlog = LASTLOG_FILE; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if your system defines _PATH_LASTLOG" >&5 printf %s "checking if your system defines _PATH_LASTLOG... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #ifdef HAVE_LASTLOG_H # include #endif #ifdef HAVE_PATHS_H # include #endif int main (void) { char *lastlog = _PATH_LASTLOG; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } system_lastlog_path=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext if test -z "$conf_lastlog_location"; then if test x"$system_lastlog_path" = x"no" ; then for f in /var/log/lastlog /usr/adm/lastlog /var/adm/lastlog /etc/security/lastlog ; do if (test -d "$f" || test -f "$f") ; then conf_lastlog_location=$f fi done if test -z "$conf_lastlog_location"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: ** Cannot find lastlog **" >&5 printf "%s\n" "$as_me: WARNING: ** Cannot find lastlog **" >&2;} fi fi fi if test -n "$conf_lastlog_location"; then printf "%s\n" "#define CONF_LASTLOG_FILE \"$conf_lastlog_location\"" >>confdefs.h fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if your system defines UTMP_FILE" >&5 printf %s "checking if your system defines UTMP_FILE... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #ifdef HAVE_PATHS_H # include #endif int main (void) { char *utmp = UTMP_FILE; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } system_utmp_path=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext if test -z "$conf_utmp_location"; then if test x"$system_utmp_path" = x"no" ; then for f in /etc/utmp /usr/adm/utmp /var/run/utmp; do if test -f $f ; then conf_utmp_location=$f fi done if test -z "$conf_utmp_location"; then printf "%s\n" "#define DISABLE_UTMP 1" >>confdefs.h fi fi fi if test -n "$conf_utmp_location"; then printf "%s\n" "#define CONF_UTMP_FILE \"$conf_utmp_location\"" >>confdefs.h fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if your system defines WTMP_FILE" >&5 printf %s "checking if your system defines WTMP_FILE... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #ifdef HAVE_PATHS_H # include #endif int main (void) { char *wtmp = WTMP_FILE; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } system_wtmp_path=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext if test -z "$conf_wtmp_location"; then if test x"$system_wtmp_path" = x"no" ; then for f in /usr/adm/wtmp /var/log/wtmp; do if test -f $f ; then conf_wtmp_location=$f fi done if test -z "$conf_wtmp_location"; then printf "%s\n" "#define DISABLE_WTMP 1" >>confdefs.h fi fi fi if test -n "$conf_wtmp_location"; then printf "%s\n" "#define CONF_WTMP_FILE \"$conf_wtmp_location\"" >>confdefs.h fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if your system defines WTMPX_FILE" >&5 printf %s "checking if your system defines WTMPX_FILE... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #ifdef HAVE_UTMPX_H #include #endif #ifdef HAVE_PATHS_H # include #endif int main (void) { char *wtmpx = WTMPX_FILE; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } system_wtmpx_path=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext if test -z "$conf_wtmpx_location"; then if test x"$system_wtmpx_path" = x"no" ; then printf "%s\n" "#define DISABLE_WTMPX 1" >>confdefs.h fi else printf "%s\n" "#define CONF_WTMPX_FILE \"$conf_wtmpx_location\"" >>confdefs.h fi if test ! -z "$blibpath" ; then LDFLAGS="$LDFLAGS $blibflags$blibpath" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: Please check and edit blibpath in LDFLAGS in Makefile" >&5 printf "%s\n" "$as_me: WARNING: Please check and edit blibpath in LDFLAGS in Makefile" >&2;} fi ac_fn_c_check_member "$LINENO" "struct lastlog" "ll_line" "ac_cv_member_struct_lastlog_ll_line" " #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_UTMP_H #include #endif #ifdef HAVE_UTMPX_H #include #endif #ifdef HAVE_LASTLOG_H #include #endif " if test "x$ac_cv_member_struct_lastlog_ll_line" = xyes then : else $as_nop if test x$SKIP_DISABLE_LASTLOG_DEFINE != "xyes" ; then printf "%s\n" "#define DISABLE_LASTLOG 1" >>confdefs.h fi fi ac_fn_c_check_member "$LINENO" "struct utmp" "ut_line" "ac_cv_member_struct_utmp_ut_line" " #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_UTMP_H #include #endif #ifdef HAVE_UTMPX_H #include #endif #ifdef HAVE_LASTLOG_H #include #endif " if test "x$ac_cv_member_struct_utmp_ut_line" = xyes then : else $as_nop printf "%s\n" "#define DISABLE_UTMP 1" >>confdefs.h printf "%s\n" "#define DISABLE_WTMP 1" >>confdefs.h fi CFLAGS="$CFLAGS $werror_flags" if test "x$ac_cv_func_getaddrinfo" != "xyes" ; then TEST_SSH_IPV6=no else TEST_SSH_IPV6=yes fi ac_fn_check_decl "$LINENO" "BROKEN_GETADDRINFO" "ac_cv_have_decl_BROKEN_GETADDRINFO" "$ac_includes_default" "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_BROKEN_GETADDRINFO" = xyes then : TEST_SSH_IPV6=no fi TEST_SSH_IPV6=$TEST_SSH_IPV6 TEST_SSH_UTF8=$TEST_SSH_UTF8 TEST_MALLOC_OPTIONS=$TEST_MALLOC_OPTIONS UNSUPPORTED_ALGORITHMS=$unsupported_algorithms DEPEND=$(cat $srcdir/.depend) CFLAGS="${CFLAGS} ${CFLAGS_AFTER}" LDFLAGS="${LDFLAGS} ${LDFLAGS_AFTER}" # Make a copy of CFLAGS/LDFLAGS without PIE options. LDFLAGS_NOPIE=`echo "$LDFLAGS" | sed 's/ -pie//'` CFLAGS_NOPIE=`echo "$CFLAGS" | sed 's/ -fPIE//'` ac_config_files="$ac_config_files Makefile buildpkg.sh opensshd.init openssh.xml openbsd-compat/Makefile openbsd-compat/regress/Makefile survey.sh" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure # scripts and configure runs, see configure's option --config-cache. # It is not useful on other systems. If it contains results you don't # want to keep, you may remove or edit it. # # config.status only pays attention to the cache file if you give it # the --recheck option to rerun configure. # # `ac_cv_env_foo' variables (set or unset) will be overridden when # loading this file, other *unset* `ac_cv_foo' will be assigned the # following values. _ACEOF # The following way of writing the cache mishandles newlines in values, # but we know of no workaround that is simple, portable, and efficient. # So, we kill variables containing newlines. # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. ( for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 printf "%s\n" "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space=' '; set) 2>&1` in #( *${as_nl}ac_space=\ *) # `set' does not quote correctly, so add quotes: double-quote # substitution turns \\\\ into \\, and sed turns \\ into \. sed -n \ "s/'/'\\\\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" ;; #( *) # `set' quotes correctly as required by POSIX, so do not add quotes. sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) | sed ' /^ac_cv_env_/b end t clear :clear s/^\([^=]*\)=\(.*[{}].*\)$/test ${\1+y} || &/ t end s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ :end' >>confcache if diff "$cache_file" confcache >/dev/null 2>&1; then :; else if test -w "$cache_file"; then if test "x$cache_file" != "x/dev/null"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 printf "%s\n" "$as_me: updating cache $cache_file" >&6;} if test ! -f "$cache_file" || test -h "$cache_file"; then cat confcache >"$cache_file" else case $cache_file in #( */* | ?:*) mv -f confcache "$cache_file"$$ && mv -f "$cache_file"$$ "$cache_file" ;; #( *) mv -f confcache "$cache_file" ;; esac fi fi else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 printf "%s\n" "$as_me: not updating unwritable cache $cache_file" >&6;} fi fi rm -f confcache test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' DEFS=-DHAVE_CONFIG_H ac_libobjs= ac_ltlibobjs= U= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' ac_i=`printf "%s\n" "$ac_i" | sed "$ac_script"` # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR # will be set to the directory where LIBOBJS objects are built. as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' done LIBOBJS=$ac_libobjs LTLIBOBJS=$ac_ltlibobjs : "${CONFIG_STATUS=./config.status}" ac_write_fail=0 ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files $CONFIG_STATUS" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 printf "%s\n" "$as_me: creating $CONFIG_STATUS" >&6;} as_write_fail=0 cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 #! $SHELL # Generated by $as_me. # Run this file to recreate the current configuration. # Compiler output produced by configure, useful for debugging # configure, is in config.log if it exists. debug=false ac_cs_recheck=false ac_cs_silent=false SHELL=\${CONFIG_SHELL-$SHELL} export SHELL _ASEOF cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh as_nop=: if test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else $as_nop case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi # Reset variables that may have inherited troublesome values from # the environment. # IFS needs to be set, to space, tab, and newline, in precisely that order. # (If _AS_PATH_WALK were called with IFS unset, it would have the # side effect of setting IFS to empty, thus disabling word splitting.) # Quoting is to prevent editors from complaining about space-tab. as_nl=' ' export as_nl IFS=" "" $as_nl" PS1='$ ' PS2='> ' PS4='+ ' # Ensure predictable behavior from utilities with locale-dependent output. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # We cannot yet rely on "unset" to work, but we need these variables # to be unset--not just set to an empty or harmless value--now, to # avoid bugs in old shells (e.g. pre-3.0 UWIN ksh). This construct # also avoids known problems related to "unset" and subshell syntax # in other old shells (e.g. bash 2.01 and pdksh 5.2.14). for as_var in BASH_ENV ENV MAIL MAILPATH CDPATH do eval test \${$as_var+y} \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done # Ensure that fds 0, 1, and 2 are open. if (exec 3>&0) 2>/dev/null; then :; else exec 0&1) 2>/dev/null; then :; else exec 1>/dev/null; fi if (exec 3>&2) ; then :; else exec 2>/dev/null; fi # The user is always right. if ${PATH_SEPARATOR+false} :; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac test -r "$as_dir$0" && as_myself=$as_dir$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then printf "%s\n" "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi printf "%s\n" "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null then : eval 'as_fn_append () { eval $1+=\$2 }' else $as_nop as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null then : eval 'as_fn_arith () { as_val=$(( $* )) }' else $as_nop as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits # Determine whether it's possible to make 'echo' print without a newline. # These variables are no longer used directly by Autoconf, but are AC_SUBSTed # for compatibility with existing Makefiles. ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac # For backward compatibility with old third-party macros, we provide # the shell variables $as_echo and $as_echo_n. New code should use # AS_ECHO(["message"]) and AS_ECHO_N(["message"]), respectively. as_echo='printf %s\n' as_echo_n='printf %s' rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`printf "%s\n" "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" exec 6>&1 ## ----------------------------------- ## ## Main body of $CONFIG_STATUS script. ## ## ----------------------------------- ## _ASEOF test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Save the log message, to keep $0 and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" This file was extended by OpenSSH $as_me Portable, which was generated by GNU Autoconf 2.71. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ on `(hostname || uname -n) 2>/dev/null | sed 1q` " _ACEOF case $ac_config_files in *" "*) set x $ac_config_files; shift; ac_config_files=$*;; esac case $ac_config_headers in *" "*) set x $ac_config_headers; shift; ac_config_headers=$*;; esac cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # Files that config.status was made for. config_files="$ac_config_files" config_headers="$ac_config_headers" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 ac_cs_usage="\ \`$as_me' instantiates files and other configuration actions from templates according to the current configuration. Unless the files and actions are specified as TAGs, all are instantiated by default. Usage: $0 [OPTION]... [TAG]... -h, --help print this help, then exit -V, --version print version number and configuration settings, then exit --config print configuration, then exit -q, --quiet, --silent do not print progress messages -d, --debug don't remove temporary files --recheck update $as_me by reconfiguring in the same conditions --file=FILE[:TEMPLATE] instantiate the configuration file FILE --header=FILE[:TEMPLATE] instantiate the configuration header FILE Configuration files: $config_files Configuration headers: $config_headers Report bugs to ." _ACEOF ac_cs_config=`printf "%s\n" "$ac_configure_args" | sed "$ac_safe_unquote"` ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\''/g"` cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config='$ac_cs_config_escaped' ac_cs_version="\\ OpenSSH config.status Portable configured by $0, generated by GNU Autoconf 2.71, with options \\"\$ac_cs_config\\" Copyright (C) 2021 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." ac_pwd='$ac_pwd' srcdir='$srcdir' INSTALL='$INSTALL' MKDIR_P='$MKDIR_P' AWK='$AWK' test -n "\$AWK" || AWK=awk _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # The default lists apply if the user does not specify any file. ac_need_defaults=: while test $# != 0 do case $1 in --*=?*) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` ac_shift=: ;; --*=) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg= ac_shift=: ;; *) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; esac case $ac_option in # Handling of the options. -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) ac_cs_recheck=: ;; --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) printf "%s\n" "$ac_cs_version"; exit ;; --config | --confi | --conf | --con | --co | --c ) printf "%s\n" "$ac_cs_config"; exit ;; --debug | --debu | --deb | --de | --d | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`printf "%s\n" "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; '') as_fn_error $? "missing file argument" ;; esac as_fn_append CONFIG_FILES " '$ac_optarg'" ac_need_defaults=false;; --header | --heade | --head | --hea ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`printf "%s\n" "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; esac as_fn_append CONFIG_HEADERS " '$ac_optarg'" ac_need_defaults=false;; --he | --h) # Conflict between --help and --header as_fn_error $? "ambiguous option: \`$1' Try \`$0 --help' for more information.";; --help | --hel | -h ) printf "%s\n" "$ac_cs_usage"; exit ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil | --si | --s) ac_cs_silent=: ;; # This is an error. -*) as_fn_error $? "unrecognized option: \`$1' Try \`$0 --help' for more information." ;; *) as_fn_append ac_config_targets " $1" ac_need_defaults=false ;; esac shift done ac_configure_extra_args= if $ac_cs_silent; then exec 6>/dev/null ac_configure_extra_args="$ac_configure_extra_args --silent" fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 if \$ac_cs_recheck; then set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion shift \printf "%s\n" "running CONFIG_SHELL=$SHELL \$*" >&6 CONFIG_SHELL='$SHELL' export CONFIG_SHELL exec "\$@" fi _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 exec 5>>config.log { echo sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX ## Running $as_me. ## _ASBOX printf "%s\n" "$ac_log" } >&5 _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Handling of arguments. for ac_config_target in $ac_config_targets do case $ac_config_target in "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; "buildpkg.sh") CONFIG_FILES="$CONFIG_FILES buildpkg.sh" ;; "opensshd.init") CONFIG_FILES="$CONFIG_FILES opensshd.init" ;; "openssh.xml") CONFIG_FILES="$CONFIG_FILES openssh.xml" ;; "openbsd-compat/Makefile") CONFIG_FILES="$CONFIG_FILES openbsd-compat/Makefile" ;; "openbsd-compat/regress/Makefile") CONFIG_FILES="$CONFIG_FILES openbsd-compat/regress/Makefile" ;; "survey.sh") CONFIG_FILES="$CONFIG_FILES survey.sh" ;; *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; esac done # If the user did not use the arguments to specify the items to instantiate, # then the envvar interface is used. Set only those that are not. # We use the long form for the default assignment because of an extremely # bizarre bug on SunOS 4.1.3. if $ac_need_defaults; then test ${CONFIG_FILES+y} || CONFIG_FILES=$config_files test ${CONFIG_HEADERS+y} || CONFIG_HEADERS=$config_headers fi # Have a temporary directory for convenience. Make it in the build tree # simply because there is no reason against having it here, and in addition, # creating and moving files from /tmp can sometimes cause problems. # Hook for its removal unless debugging. # Note that there is a small window in which the directory will not be cleaned: # after its creation but before its name has been assigned to `$tmp'. $debug || { tmp= ac_tmp= trap 'exit_status=$? : "${ac_tmp:=$tmp}" { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status ' 0 trap 'as_fn_exit 1' 1 2 13 15 } # Create a (secure) tmp directory for tmp files. { tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && test -d "$tmp" } || { tmp=./conf$$-$RANDOM (umask 077 && mkdir "$tmp") } || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 ac_tmp=$tmp # Set up the scripts for CONFIG_FILES section. # No need to generate them if there are no CONFIG_FILES. # This happens for instance with `./config.status config.h'. if test -n "$CONFIG_FILES"; then ac_cr=`echo X | tr X '\015'` # On cygwin, bash can eat \r inside `` if the user requested igncr. # But we know of no other shell where ac_cr would be empty at this # point, so we can use a bashism as a fallback. if test "x$ac_cr" = x; then eval ac_cr=\$\'\\r\' fi ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then ac_cs_awk_cr='\\r' else ac_cs_awk_cr=$ac_cr fi echo 'BEGIN {' >"$ac_tmp/subs1.awk" && _ACEOF { echo "cat >conf$$subs.awk <<_ACEOF" && echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && echo "_ACEOF" } >conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` ac_delim='%!_!# ' for ac_last_try in false false false false false :; do . ./conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` if test $ac_delim_n = $ac_delim_num; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done rm -f conf$$subs.sh cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && _ACEOF sed -n ' h s/^/S["/; s/!.*/"]=/ p g s/^[^!]*!// :repl t repl s/'"$ac_delim"'$// t delim :nl h s/\(.\{148\}\)..*/\1/ t more1 s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ p n b repl :more1 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t nl :delim h s/\(.\{148\}\)..*/\1/ t more2 s/["\\]/\\&/g; s/^/"/; s/$/"/ p b :more2 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t delim ' >$CONFIG_STATUS || ac_write_fail=1 rm -f conf$$subs.awk cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACAWK cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && for (key in S) S_is_set[key] = 1 FS = "" } { line = $ 0 nfields = split(line, field, "@") substed = 0 len = length(field[1]) for (i = 2; i < nfields; i++) { key = field[i] keylen = length(key) if (S_is_set[key]) { value = S[key] line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) len += length(value) + length(field[++i]) substed = 1 } else len += 1 + keylen } print line } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" else cat fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 _ACEOF # VPATH may cause trouble with some makes, so we remove sole $(srcdir), # ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and # trailing colons and then remove the whole line if VPATH becomes empty # (actually we leave an empty line to preserve line numbers). if test "x$srcdir" = x.; then ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ h s/// s/^/:/ s/[ ]*$/:/ s/:\$(srcdir):/:/g s/:\${srcdir}:/:/g s/:@srcdir@:/:/g s/^:*// s/:*$// x s/\(=[ ]*\).*/\1/ G s/\n// s/^[^=]*=[ ]*$// }' fi cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 fi # test -n "$CONFIG_FILES" # Set up the scripts for CONFIG_HEADERS section. # No need to generate them if there are no CONFIG_HEADERS. # This happens for instance with `./config.status Makefile'. if test -n "$CONFIG_HEADERS"; then cat >"$ac_tmp/defines.awk" <<\_ACAWK || BEGIN { _ACEOF # Transform confdefs.h into an awk script `defines.awk', embedded as # here-document in config.status, that substitutes the proper values into # config.h.in to produce config.h. # Create a delimiter string that does not exist in confdefs.h, to ease # handling of long lines. ac_delim='%!_!# ' for ac_last_try in false false :; do ac_tt=`sed -n "/$ac_delim/p" confdefs.h` if test -z "$ac_tt"; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done # For the awk script, D is an array of macro values keyed by name, # likewise P contains macro parameters if any. Preserve backslash # newline sequences. ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* sed -n ' s/.\{148\}/&'"$ac_delim"'/g t rset :rset s/^[ ]*#[ ]*define[ ][ ]*/ / t def d :def s/\\$// t bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3"/p s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p d :bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3\\\\\\n"\\/p t cont s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p t cont d :cont n s/.\{148\}/&'"$ac_delim"'/g t clear :clear s/\\$// t bsnlc s/["\\]/\\&/g; s/^/"/; s/$/"/p d :bsnlc s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p b cont ' >$CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 for (key in D) D_is_set[key] = 1 FS = "" } /^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { line = \$ 0 split(line, arg, " ") if (arg[1] == "#") { defundef = arg[2] mac1 = arg[3] } else { defundef = substr(arg[1], 2) mac1 = arg[2] } split(mac1, mac2, "(") #) macro = mac2[1] prefix = substr(line, 1, index(line, defundef) - 1) if (D_is_set[macro]) { # Preserve the white space surrounding the "#". print prefix "define", macro P[macro] D[macro] next } else { # Replace #undef with comments. This is necessary, for example, # in the case of _POSIX_SOURCE, which is predefined and required # on some systems where configure will not decide to define it. if (defundef == "undef") { print "/*", prefix defundef, macro, "*/" next } } } { print } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 fi # test -n "$CONFIG_HEADERS" eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS " shift for ac_tag do case $ac_tag in :[FHLC]) ac_mode=$ac_tag; continue;; esac case $ac_mode$ac_tag in :[FHL]*:*);; :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; esac ac_save_IFS=$IFS IFS=: set x $ac_tag IFS=$ac_save_IFS shift ac_file=$1 shift case $ac_mode in :L) ac_source=$1;; :[FH]) ac_file_inputs= for ac_f do case $ac_f in -) ac_f="$ac_tmp/stdin";; *) # Look for the file first in the build tree, then in the source tree # (if the path is not absolute). The absolute path cannot be DOS-style, # because $ac_f cannot contain `:'. test -f "$ac_f" || case $ac_f in [\\/$]*) false;; *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; esac || as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; esac case $ac_f in *\'*) ac_f=`printf "%s\n" "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac as_fn_append ac_file_inputs " '$ac_f'" done # Let's still pretend it is `configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ configure_input='Generated from '` printf "%s\n" "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' `' by configure.' if test x"$ac_file" != x-; then configure_input="$ac_file. $configure_input" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 printf "%s\n" "$as_me: creating $ac_file" >&6;} fi # Neutralize special characters interpreted by sed in replacement strings. case $configure_input in #( *\&* | *\|* | *\\* ) ac_sed_conf_input=`printf "%s\n" "$configure_input" | sed 's/[\\\\&|]/\\\\&/g'`;; #( *) ac_sed_conf_input=$configure_input;; esac case $ac_tag in *:-:* | *:-) cat >"$ac_tmp/stdin" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; esac ;; esac ac_dir=`$as_dirname -- "$ac_file" || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` as_dir="$ac_dir"; as_fn_mkdir_p ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`printf "%s\n" "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix case $ac_mode in :F) # # CONFIG_FILE # case $INSTALL in [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; esac ac_MKDIR_P=$MKDIR_P case $MKDIR_P in [\\/$]* | ?:[\\/]* ) ;; */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;; esac _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # If the template does not know about datarootdir, expand it. # FIXME: This hack should be removed a few years after 2.60. ac_datarootdir_hack=; ac_datarootdir_seen= ac_sed_dataroot=' /datarootdir/ { p q } /@datadir@/p /@docdir@/p /@infodir@/p /@localedir@/p /@mandir@/p' case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in *datarootdir*) ac_datarootdir_seen=yes;; *@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 printf "%s\n" "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_datarootdir_hack=' s&@datadir@&$datadir&g s&@docdir@&$docdir&g s&@infodir@&$infodir&g s&@localedir@&$localedir&g s&@mandir@&$mandir&g s&\\\${datarootdir}&$datarootdir&g' ;; esac _ACEOF # Neutralize VPATH when `$srcdir' = `.'. # Shell code in configure.ac might set extrasub. # FIXME: do we really want to maintain this feature? cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_sed_extra="$ac_vpsub $extrasub _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 :t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b s|@configure_input@|$ac_sed_conf_input|;t t s&@top_builddir@&$ac_top_builddir_sub&;t t s&@top_build_prefix@&$ac_top_build_prefix&;t t s&@srcdir@&$ac_srcdir&;t t s&@abs_srcdir@&$ac_abs_srcdir&;t t s&@top_srcdir@&$ac_top_srcdir&;t t s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t s&@builddir@&$ac_builddir&;t t s&@abs_builddir@&$ac_abs_builddir&;t t s&@abs_top_builddir@&$ac_abs_top_builddir&;t t s&@INSTALL@&$ac_INSTALL&;t t s&@MKDIR_P@&$ac_MKDIR_P&;t t $ac_datarootdir_hack " eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ "$ac_tmp/out"`; test -z "$ac_out"; } && { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&5 printf "%s\n" "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&2;} rm -f "$ac_tmp/stdin" case $ac_file in -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; esac \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; :H) # # CONFIG_HEADER # if test x"$ac_file" != x-; then { printf "%s\n" "/* $configure_input */" >&1 \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" } >"$ac_tmp/config.h" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 printf "%s\n" "$as_me: $ac_file is unchanged" >&6;} else rm -f "$ac_file" mv "$ac_tmp/config.h" "$ac_file" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 fi else printf "%s\n" "/* $configure_input */" >&1 \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ || as_fn_error $? "could not create -" "$LINENO" 5 fi ;; esac done # for ac_tag as_fn_exit 0 _ACEOF ac_clean_files=$ac_clean_files_save test $ac_write_fail = 0 || as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 # configure is writing to config.log, and then calls config.status. # config.status does its own redirection, appending to config.log. # Unfortunately, on DOS this fails, as config.log is still kept open # by configure, so config.status won't be able to write to it; its # output is simply discarded. So we exec the FD to /dev/null, # effectively closing config.log, so it can be properly (re)opened and # appended to by config.status. When coming back to configure, we # need to make the FD available again. if test "$no_create" != yes; then ac_cs_success=: ac_config_status_args= test "$silent" = yes && ac_config_status_args="$ac_config_status_args --quiet" exec 5>/dev/null $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false exec 5>>config.log # Use ||, not &&, to avoid exiting from the if with $? = 1, which # would make configure fail if this is the last instruction. $ac_cs_success || as_fn_exit 1 fi if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} fi # Print summary of options # Someone please show me a better way :) A=`eval echo ${prefix}` ; A=`eval echo ${A}` B=`eval echo ${bindir}` ; B=`eval echo ${B}` C=`eval echo ${sbindir}` ; C=`eval echo ${C}` D=`eval echo ${sysconfdir}` ; D=`eval echo ${D}` E=`eval echo ${libexecdir}/ssh-askpass` ; E=`eval echo ${E}` F=`eval echo ${mandir}/${mansubdir}X` ; F=`eval echo ${F}` G=`eval echo ${piddir}` ; G=`eval echo ${G}` H=`eval echo ${PRIVSEP_PATH}` ; H=`eval echo ${H}` I=`eval echo ${user_path}` ; I=`eval echo ${I}` J=`eval echo ${superuser_path}` ; J=`eval echo ${J}` echo "" echo "OpenSSH has been configured with the following options:" echo " User binaries: $B" echo " System binaries: $C" echo " Configuration files: $D" echo " Askpass program: $E" echo " Manual pages: $F" echo " PID file: $G" echo " Privilege separation chroot path: $H" if test "x$external_path_file" = "x/etc/login.conf" ; then echo " At runtime, sshd will use the path defined in $external_path_file" echo " Make sure the path to scp is present, otherwise scp will not work" else echo " sshd default user PATH: $I" if test ! -z "$external_path_file"; then echo " (If PATH is set in $external_path_file it will be used instead. If" echo " used, ensure the path to scp is present, otherwise scp will not work.)" fi fi if test ! -z "$superuser_path" ; then echo " sshd superuser user PATH: $J" fi echo " Manpage format: $MANTYPE" echo " PAM support: $PAM_MSG" echo " OSF SIA support: $SIA_MSG" echo " KerberosV support: $KRB5_MSG" echo " SELinux support: $SELINUX_MSG" echo " libedit support: $LIBEDIT_MSG" echo " libldns support: $LDNS_MSG" echo " Solaris process contract support: $SPC_MSG" echo " Solaris project support: $SP_MSG" echo " Solaris privilege support: $SPP_MSG" echo " IP address in \$DISPLAY hack: $DISPLAY_HACK_MSG" echo " Translate v4 in v6 hack: $IPV4_IN6_HACK_MSG" echo " BSD Auth support: $BSD_AUTH_MSG" echo " Random number source: $RAND_MSG" echo " Privsep sandbox style: $SANDBOX_STYLE" echo " PKCS#11 support: $enable_pkcs11" echo " U2F/FIDO support: $enable_sk" echo "" echo " Host: ${host}" echo " Compiler: ${CC}" echo " Compiler flags: ${CFLAGS}" echo "Preprocessor flags: ${CPPFLAGS}" echo " Linker flags: ${LDFLAGS}" echo " Libraries: ${LIBS}" if test ! -z "${CHANNELLIBS}"; then echo " +for channels: ${CHANNELLIBS}" fi if test ! -z "${LIBFIDO2}"; then echo " +for FIDO2: ${LIBFIDO2}" fi if test ! -z "${SSHDLIBS}"; then echo " +for sshd: ${SSHDLIBS}" fi echo "" if test "x$MAKE_PACKAGE_SUPPORTED" = "xyes" ; then echo "SVR4 style packages are supported with \"make package\"" echo "" fi if test "x$PAM_MSG" = "xyes" ; then echo "PAM is enabled. You may need to install a PAM control file " echo "for sshd, otherwise password authentication may fail. " echo "Example PAM control files can be found in the contrib/ " echo "subdirectory" echo "" fi if test ! -z "$NO_PEERCHECK" ; then echo "WARNING: the operating system that you are using does not" echo "appear to support getpeereid(), getpeerucred() or the" echo "SO_PEERCRED getsockopt() option. These facilities are used to" echo "enforce security checks to prevent unauthorised connections to" echo "ssh-agent. Their absence increases the risk that a malicious" echo "user can connect to your agent." echo "" fi if test "$AUDIT_MODULE" = "bsm" ; then echo "WARNING: BSM audit support is currently considered EXPERIMENTAL." echo "See the Solaris section in README.platform for details." fi diff --git a/configure.ac b/configure.ac index 07893e870659..d8816e3f65de 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5664 +1,5670 @@ # # Copyright (c) 1999-2004 Damien Miller # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. AC_INIT([OpenSSH], [Portable], [openssh-unix-dev@mindrot.org]) AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_SRCDIR([ssh.c]) # Check for stale configure as early as possible. for i in $srcdir/configure.ac $srcdir/m4/*.m4; do if test "$i" -nt "$srcdir/configure"; then AC_MSG_ERROR([$i newer than configure, run autoreconf]) fi done AC_LANG([C]) AC_CONFIG_HEADERS([config.h]) AC_PROG_CC([cc gcc clang]) # XXX relax this after reimplementing logit() etc. AC_MSG_CHECKING([if $CC supports C99-style variadic macros]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ int f(int a, int b, int c) { return a + b + c; } #define F(a, ...) f(a, __VA_ARGS__) ]], [[return F(1, 2, -3);]])], [ AC_MSG_RESULT([yes]) ], [ AC_MSG_ERROR([*** OpenSSH requires support for C99-style variadic macros]) ] ) AC_CANONICAL_HOST AC_C_BIGENDIAN # Checks for programs. AC_PROG_AWK AC_PROG_CPP AC_PROG_RANLIB AC_PROG_INSTALL AC_PROG_EGREP AC_PROG_MKDIR_P AC_CHECK_TOOLS([AR], [ar]) AC_PATH_PROG([CAT], [cat]) AC_PATH_PROG([KILL], [kill]) AC_PATH_PROG([SED], [sed]) AC_PATH_PROG([TEST_MINUS_S_SH], [bash]) AC_PATH_PROG([TEST_MINUS_S_SH], [ksh]) AC_PATH_PROG([TEST_MINUS_S_SH], [sh]) AC_PATH_PROG([SH], [bash]) AC_PATH_PROG([SH], [ksh]) AC_PATH_PROG([SH], [sh]) AC_PATH_PROG([GROFF], [groff]) AC_PATH_PROG([NROFF], [nroff awf]) AC_PATH_PROG([MANDOC], [mandoc]) AC_PATH_TOOL([PKGCONFIG], [pkg-config], [no]) AC_SUBST([TEST_SHELL], [sh]) dnl select manpage formatter to be used to build "cat" format pages. if test "x$MANDOC" != "x" ; then MANFMT="$MANDOC" elif test "x$NROFF" != "x" ; then MANFMT="$NROFF -mandoc" elif test "x$GROFF" != "x" ; then MANFMT="$GROFF -mandoc -Tascii" else AC_MSG_WARN([no manpage formatter found]) MANFMT="false" fi AC_SUBST([MANFMT]) dnl for buildpkg.sh AC_PATH_PROG([PATH_GROUPADD_PROG], [groupadd], [groupadd], [/usr/sbin${PATH_SEPARATOR}/etc]) AC_PATH_PROG([PATH_USERADD_PROG], [useradd], [useradd], [/usr/sbin${PATH_SEPARATOR}/etc]) AC_CHECK_PROG([MAKE_PACKAGE_SUPPORTED], [pkgmk], [yes], [no]) if test -x /sbin/sh; then AC_SUBST([STARTUP_SCRIPT_SHELL], [/sbin/sh]) else AC_SUBST([STARTUP_SCRIPT_SHELL], [/bin/sh]) fi # System features AC_SYS_LARGEFILE if test -z "$AR" ; then AC_MSG_ERROR([*** 'ar' missing, please install or fix your \$PATH ***]) fi AC_PATH_PROG([PATH_PASSWD_PROG], [passwd]) if test ! -z "$PATH_PASSWD_PROG" ; then AC_DEFINE_UNQUOTED([_PATH_PASSWD_PROG], ["$PATH_PASSWD_PROG"], [Full path of your "passwd" program]) fi dnl Since autoconf doesn't support it very well, we no longer allow users to dnl override LD, however keeping the hook here for now in case there's a use dnl use case we overlooked and someone needs to re-enable it. Unless a good dnl reason is found we'll be removing this in future. LD="$CC" AC_SUBST([LD]) AC_C_INLINE AC_CHECK_DECL([LLONG_MAX], [have_llong_max=1], , [#include ]) AC_CHECK_DECL([LONG_LONG_MAX], [have_long_long_max=1], , [#include ]) AC_CHECK_DECL([SYSTR_POLICY_KILL], [have_systr_policy_kill=1], , [ #include #include #include ]) AC_CHECK_DECL([RLIMIT_NPROC], [AC_DEFINE([HAVE_RLIMIT_NPROC], [], [sys/resource.h has RLIMIT_NPROC])], , [ #include #include ]) AC_CHECK_DECL([PR_SET_NO_NEW_PRIVS], [have_linux_no_new_privs=1], , [ #include #include ]) openssl=yes openssl_bin=openssl AC_ARG_WITH([openssl], [ --without-openssl Disable use of OpenSSL; use only limited internal crypto **EXPERIMENTAL** ], [ if test "x$withval" = "xno" ; then openssl=no openssl_bin="" fi ] ) AC_MSG_CHECKING([whether OpenSSL will be used for cryptography]) if test "x$openssl" = "xyes" ; then AC_MSG_RESULT([yes]) AC_DEFINE_UNQUOTED([WITH_OPENSSL], [1], [use libcrypto for cryptography]) else AC_MSG_RESULT([no]) fi use_stack_protector=1 use_toolchain_hardening=1 AC_ARG_WITH([stackprotect], [ --without-stackprotect Don't use compiler's stack protection], [ if test "x$withval" = "xno"; then use_stack_protector=0 fi ]) AC_ARG_WITH([hardening], [ --without-hardening Don't use toolchain hardening flags], [ if test "x$withval" = "xno"; then use_toolchain_hardening=0 fi ]) # We use -Werror for the tests only so that we catch warnings like "this is # on by default" for things like -fPIE. AC_MSG_CHECKING([if $CC supports -Werror]) saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -Werror" AC_COMPILE_IFELSE([AC_LANG_SOURCE([[int main(void) { return 0; }]])], [ AC_MSG_RESULT([yes]) WERROR="-Werror"], [ AC_MSG_RESULT([no]) WERROR="" ] ) CFLAGS="$saved_CFLAGS" if test "$GCC" = "yes" || test "$GCC" = "egcs"; then AC_MSG_CHECKING([gcc version]) GCC_VER=`$CC -v 2>&1 | $AWK '/gcc version /{print $3}'` case "$GCC_VER" in 1.*) no_attrib_nonnull=1 ;; 2.8* | 2.9*) no_attrib_nonnull=1 ;; 2.*) no_attrib_nonnull=1 ;; *) ;; esac AC_MSG_RESULT([$GCC_VER]) AC_MSG_CHECKING([clang version]) - CLANG_VER=`$CC -v 2>&1 | $AWK '/clang version /{print $3}'` + ver="`$CC -v 2>&1`" + if echo "$ver" | grep "Apple" >/dev/null; then + CLANG_VER="apple-`echo "$ver" | \ + awk '/Apple LLVM/ {print $4"-"$5}'`" + else + CLANG_VER=`echo "$ver" | $AWK '/clang version /{print $3}'` + fi AC_MSG_RESULT([$CLANG_VER]) OSSH_CHECK_CFLAG_COMPILE([-pipe]) OSSH_CHECK_CFLAG_COMPILE([-Wunknown-warning-option]) OSSH_CHECK_CFLAG_COMPILE([-Wno-error=format-truncation]) OSSH_CHECK_CFLAG_COMPILE([-Qunused-arguments]) OSSH_CHECK_CFLAG_COMPILE([-Wall]) OSSH_CHECK_CFLAG_COMPILE([-Wextra]) OSSH_CHECK_CFLAG_COMPILE([-Wpointer-arith]) OSSH_CHECK_CFLAG_COMPILE([-Wuninitialized]) OSSH_CHECK_CFLAG_COMPILE([-Wsign-compare]) OSSH_CHECK_CFLAG_COMPILE([-Wformat-security]) OSSH_CHECK_CFLAG_COMPILE([-Wsizeof-pointer-memaccess]) OSSH_CHECK_CFLAG_COMPILE([-Wpointer-sign], [-Wno-pointer-sign]) OSSH_CHECK_CFLAG_COMPILE([-Wunused-parameter], [-Wno-unused-parameter]) OSSH_CHECK_CFLAG_COMPILE([-Wunused-result], [-Wno-unused-result]) OSSH_CHECK_CFLAG_COMPILE([-Wimplicit-fallthrough]) OSSH_CHECK_CFLAG_COMPILE([-Wmisleading-indentation]) OSSH_CHECK_CFLAG_COMPILE([-Wbitwise-instead-of-logical]) OSSH_CHECK_CFLAG_COMPILE([-fno-strict-aliasing]) if test "x$use_toolchain_hardening" = "x1"; then OSSH_CHECK_CFLAG_COMPILE([-mretpoline]) # clang OSSH_CHECK_LDFLAG_LINK([-Wl,-z,retpolineplt]) OSSH_CHECK_CFLAG_COMPILE([-D_FORTIFY_SOURCE=2]) OSSH_CHECK_LDFLAG_LINK([-Wl,-z,relro]) OSSH_CHECK_LDFLAG_LINK([-Wl,-z,now]) OSSH_CHECK_LDFLAG_LINK([-Wl,-z,noexecstack]) # NB. -ftrapv expects certain support functions to be present in # the compiler library (libgcc or similar) to detect integer operations # that can overflow. We must check that the result of enabling it # actually links. The test program compiled/linked includes a number # of integer operations that should exercise this. OSSH_CHECK_CFLAG_LINK([-ftrapv]) # clang 15 seems to have a bug in -fzero-call-used-regs=all. See # https://bugzilla.mindrot.org/show_bug.cgi?id=3475 and # https://github.com/llvm/llvm-project/issues/59242 case "$CLANG_VER" in - 15.*) OSSH_CHECK_CFLAG_COMPILE([-fzero-call-used-regs=used]) ;; + 15.*|apple*) OSSH_CHECK_CFLAG_COMPILE([-fzero-call-used-regs=used]) ;; *) OSSH_CHECK_CFLAG_COMPILE([-fzero-call-used-regs=all]) ;; esac OSSH_CHECK_CFLAG_COMPILE([-ftrivial-auto-var-init=zero]) fi AC_MSG_CHECKING([if $CC accepts -fno-builtin-memset]) saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -fno-builtin-memset" AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ char b[10]; memset(b, 0, sizeof(b)); ]])], [ AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) CFLAGS="$saved_CFLAGS" ] ) # -fstack-protector-all doesn't always work for some GCC versions # and/or platforms, so we test if we can. If it's not supported # on a given platform gcc will emit a warning so we use -Werror. if test "x$use_stack_protector" = "x1"; then for t in -fstack-protector-strong -fstack-protector-all \ -fstack-protector; do AC_MSG_CHECKING([if $CC supports $t]) saved_CFLAGS="$CFLAGS" saved_LDFLAGS="$LDFLAGS" CFLAGS="$CFLAGS $t -Werror" LDFLAGS="$LDFLAGS $t -Werror" AC_LINK_IFELSE( [AC_LANG_PROGRAM([[ #include int func (int t) {char b[100]; snprintf(b,sizeof b,"%d",t); return t;} ]], [[ char x[256]; snprintf(x, sizeof(x), "XXX%d", func(1)); ]])], [ AC_MSG_RESULT([yes]) CFLAGS="$saved_CFLAGS $t" LDFLAGS="$saved_LDFLAGS $t" AC_MSG_CHECKING([if $t works]) AC_RUN_IFELSE( [AC_LANG_PROGRAM([[ #include int func (int t) {char b[100]; snprintf(b,sizeof b,"%d",t); return t;} ]], [[ char x[256]; snprintf(x, sizeof(x), "XXX%d", func(1)); ]])], [ AC_MSG_RESULT([yes]) break ], [ AC_MSG_RESULT([no]) ], [ AC_MSG_WARN([cross compiling: cannot test]) break ] ) ], [ AC_MSG_RESULT([no]) ] ) CFLAGS="$saved_CFLAGS" LDFLAGS="$saved_LDFLAGS" done fi if test -z "$have_llong_max"; then # retry LLONG_MAX with -std=gnu99, needed on some Linuxes unset ac_cv_have_decl_LLONG_MAX saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -std=gnu99" AC_CHECK_DECL([LLONG_MAX], [have_llong_max=1], [CFLAGS="$saved_CFLAGS"], [#include ] ) fi fi AC_MSG_CHECKING([if compiler allows __attribute__ on return types]) AC_COMPILE_IFELSE( [AC_LANG_PROGRAM([[ #include __attribute__((__unused__)) static void foo(void){return;}]], [[ exit(0); ]])], [ AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) AC_DEFINE(NO_ATTRIBUTE_ON_RETURN_TYPE, 1, [compiler does not accept __attribute__ on return types]) ] ) AC_MSG_CHECKING([if compiler allows __attribute__ prototype args]) AC_COMPILE_IFELSE( [AC_LANG_PROGRAM([[ #include typedef void foo(const char *, ...) __attribute__((format(printf, 1, 2)));]], [[ exit(0); ]])], [ AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) AC_DEFINE(NO_ATTRIBUTE_ON_PROTOTYPE_ARGS, 1, [compiler does not accept __attribute__ on prototype args]) ] ) AC_MSG_CHECKING([if compiler supports variable length arrays]) AC_COMPILE_IFELSE( [AC_LANG_PROGRAM([[#include ]], [[ int i; for (i=0; i<3; i++){int a[i]; a[i-1]=0;} exit(0); ]])], [ AC_MSG_RESULT([yes]) AC_DEFINE(VARIABLE_LENGTH_ARRAYS, [1], [compiler supports variable length arrays]) ], [ AC_MSG_RESULT([no]) ] ) AC_MSG_CHECKING([if compiler accepts variable declarations after code]) AC_COMPILE_IFELSE( [AC_LANG_PROGRAM([[#include ]], [[ int a; a = 1; int b = 1; exit(a-b); ]])], [ AC_MSG_RESULT([yes]) AC_DEFINE(VARIABLE_DECLARATION_AFTER_CODE, [1], [compiler variable declarations after code]) ], [ AC_MSG_RESULT([no]) ] ) if test "x$no_attrib_nonnull" != "x1" ; then AC_DEFINE([HAVE_ATTRIBUTE__NONNULL__], [1], [Have attribute nonnull]) fi AC_ARG_WITH([rpath], [ --without-rpath Disable auto-added -R linker paths], [ if test "x$withval" = "xno" ; then rpath_opt="" elif test "x$withval" = "xyes" ; then rpath_opt="-R" else rpath_opt="$withval" fi ] ) # Allow user to specify flags AC_ARG_WITH([cflags], [ --with-cflags Specify additional flags to pass to compiler], [ if test -n "$withval" && test "x$withval" != "xno" && \ test "x${withval}" != "xyes"; then CFLAGS="$CFLAGS $withval" fi ] ) AC_ARG_WITH([cflags-after], [ --with-cflags-after Specify additional flags to pass to compiler after configure], [ if test -n "$withval" && test "x$withval" != "xno" && \ test "x${withval}" != "xyes"; then CFLAGS_AFTER="$withval" fi ] ) AC_ARG_WITH([cppflags], [ --with-cppflags Specify additional flags to pass to preprocessor] , [ if test -n "$withval" && test "x$withval" != "xno" && \ test "x${withval}" != "xyes"; then CPPFLAGS="$CPPFLAGS $withval" fi ] ) AC_ARG_WITH([ldflags], [ --with-ldflags Specify additional flags to pass to linker], [ if test -n "$withval" && test "x$withval" != "xno" && \ test "x${withval}" != "xyes"; then LDFLAGS="$LDFLAGS $withval" fi ] ) AC_ARG_WITH([ldflags-after], [ --with-ldflags-after Specify additional flags to pass to linker after configure], [ if test -n "$withval" && test "x$withval" != "xno" && \ test "x${withval}" != "xyes"; then LDFLAGS_AFTER="$withval" fi ] ) AC_ARG_WITH([libs], [ --with-libs Specify additional libraries to link with], [ if test -n "$withval" && test "x$withval" != "xno" && \ test "x${withval}" != "xyes"; then LIBS="$LIBS $withval" fi ] ) AC_ARG_WITH([Werror], [ --with-Werror Build main code with -Werror], [ if test -n "$withval" && test "x$withval" != "xno"; then werror_flags="-Werror" if test "x${withval}" != "xyes"; then werror_flags="$withval" fi fi ] ) dnl On some old platforms, sys/stat.h requires sys/types.h, but autoconf-2.71's dnl AC_CHECK_INCLUDES_DEFAULT checks for them in the opposite order. If we dnl haven't detected it, recheck. if test "x$ac_cv_header_sys_stat_h" != "xyes"; then unset ac_cv_header_sys_stat_h AC_CHECK_HEADERS([sys/stat.h]) fi AC_CHECK_HEADERS([ \ blf.h \ bstring.h \ crypt.h \ crypto/sha2.h \ dirent.h \ endian.h \ elf.h \ err.h \ features.h \ fcntl.h \ floatingpoint.h \ fnmatch.h \ getopt.h \ glob.h \ ia.h \ iaf.h \ ifaddrs.h \ inttypes.h \ langinfo.h \ limits.h \ locale.h \ login.h \ maillock.h \ ndir.h \ net/if_tun.h \ netdb.h \ netgroup.h \ pam/pam_appl.h \ paths.h \ poll.h \ pty.h \ readpassphrase.h \ rpc/types.h \ security/pam_appl.h \ sha2.h \ shadow.h \ stddef.h \ stdint.h \ string.h \ strings.h \ sys/bitypes.h \ sys/byteorder.h \ sys/bsdtty.h \ sys/cdefs.h \ sys/dir.h \ sys/file.h \ sys/mman.h \ sys/label.h \ sys/ndir.h \ sys/param.h \ sys/poll.h \ sys/prctl.h \ sys/procctl.h \ sys/pstat.h \ sys/ptrace.h \ sys/random.h \ sys/select.h \ sys/stream.h \ sys/stropts.h \ sys/strtio.h \ sys/statvfs.h \ sys/sysmacros.h \ sys/time.h \ sys/timers.h \ sys/vfs.h \ time.h \ tmpdir.h \ ttyent.h \ ucred.h \ unistd.h \ usersec.h \ util.h \ utime.h \ utmp.h \ utmpx.h \ vis.h \ wchar.h \ ]) # On some platforms (eg SunOS4) sys/audit.h requires sys/[time|types|label.h] # to be included first. AC_CHECK_HEADERS([sys/audit.h], [], [], [ #ifdef HAVE_SYS_TIME_H # include #endif #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_LABEL_H # include #endif ]) # sys/capsicum.h requires sys/types.h AC_CHECK_HEADERS([sys/capsicum.h capsicum_helpers.h], [], [], [ #ifdef HAVE_SYS_TYPES_H # include #endif ]) AC_MSG_CHECKING([for caph_cache_tzdata]) AC_LINK_IFELSE( [AC_LANG_PROGRAM([[ #include ]], [[caph_cache_tzdata();]])], [ AC_MSG_RESULT([yes]) AC_DEFINE([HAVE_CAPH_CACHE_TZDATA], [1], [Define if you have caph_cache_tzdata]) ], [ AC_MSG_RESULT([no]) ] ) # net/route.h requires sys/socket.h and sys/types.h. # sys/sysctl.h also requires sys/param.h AC_CHECK_HEADERS([net/route.h sys/sysctl.h], [], [], [ #ifdef HAVE_SYS_TYPES_H # include #endif #include #include ]) # lastlog.h requires sys/time.h to be included first on Solaris AC_CHECK_HEADERS([lastlog.h], [], [], [ #ifdef HAVE_SYS_TIME_H # include #endif ]) # sys/ptms.h requires sys/stream.h to be included first on Solaris AC_CHECK_HEADERS([sys/ptms.h], [], [], [ #ifdef HAVE_SYS_STREAM_H # include #endif ]) # login_cap.h requires sys/types.h on NetBSD AC_CHECK_HEADERS([login_cap.h], [], [], [ #include ]) # older BSDs need sys/param.h before sys/mount.h AC_CHECK_HEADERS([sys/mount.h], [], [], [ #include ]) # Android requires sys/socket.h to be included before sys/un.h AC_CHECK_HEADERS([sys/un.h], [], [], [ #include #include ]) # Messages for features tested for in target-specific section SIA_MSG="no" SPC_MSG="no" SP_MSG="no" SPP_MSG="no" # Support for Solaris/Illumos privileges (this test is used by both # the --with-solaris-privs option and --with-sandbox=solaris). SOLARIS_PRIVS="no" # Check for some target-specific stuff case "$host" in *-*-aix*) # Some versions of VAC won't allow macro redefinitions at # -qlanglevel=ansi, and autoconf 2.60 sometimes insists on using that # particularly with older versions of vac or xlc. # It also throws errors about null macro arguments, but these are # not fatal. AC_MSG_CHECKING([if compiler allows macro redefinitions]) AC_COMPILE_IFELSE( [AC_LANG_PROGRAM([[ #define testmacro foo #define testmacro bar]], [[ exit(0); ]])], [ AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) CC="`echo $CC | sed 's/-qlanglvl\=ansi//g'`" CFLAGS="`echo $CFLAGS | sed 's/-qlanglvl\=ansi//g'`" CPPFLAGS="`echo $CPPFLAGS | sed 's/-qlanglvl\=ansi//g'`" ] ) AC_MSG_CHECKING([how to specify blibpath for linker ($LD)]) if (test -z "$blibpath"); then blibpath="/usr/lib:/lib" fi saved_LDFLAGS="$LDFLAGS" if test "$GCC" = "yes"; then flags="-Wl,-blibpath: -Wl,-rpath, -blibpath:" else flags="-blibpath: -Wl,-blibpath: -Wl,-rpath," fi for tryflags in $flags ;do if (test -z "$blibflags"); then LDFLAGS="$saved_LDFLAGS $tryflags$blibpath" AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [[]])], [blibflags=$tryflags], []) fi done if (test -z "$blibflags"); then AC_MSG_RESULT([not found]) AC_MSG_ERROR([*** must be able to specify blibpath on AIX - check config.log]) else AC_MSG_RESULT([$blibflags]) fi LDFLAGS="$saved_LDFLAGS" dnl Check for authenticate. Might be in libs.a on older AIXes AC_CHECK_FUNC([authenticate], [AC_DEFINE([WITH_AIXAUTHENTICATE], [1], [Define if you want to enable AIX4's authenticate function])], [AC_CHECK_LIB([s], [authenticate], [ AC_DEFINE([WITH_AIXAUTHENTICATE]) LIBS="$LIBS -ls" ]) ]) dnl Check for various auth function declarations in headers. AC_CHECK_DECLS([authenticate, loginrestrictions, loginsuccess, passwdexpired, setauthdb], , , [#include ]) dnl Check if loginfailed is declared and takes 4 arguments (AIX >= 5.2) AC_CHECK_DECLS([loginfailed], [AC_MSG_CHECKING([if loginfailed takes 4 arguments]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ (void)loginfailed("user","host","tty",0); ]])], [AC_MSG_RESULT([yes]) AC_DEFINE([AIX_LOGINFAILED_4ARG], [1], [Define if your AIX loginfailed() function takes 4 arguments (AIX >= 5.2)])], [AC_MSG_RESULT([no]) ])], [], [#include ] ) AC_CHECK_FUNCS([getgrset setauthdb]) AC_CHECK_DECL([F_CLOSEM], AC_DEFINE([HAVE_FCNTL_CLOSEM], [1], [Use F_CLOSEM fcntl for closefrom]), [], [ #include #include ] ) check_for_aix_broken_getaddrinfo=1 AC_DEFINE([SETEUID_BREAKS_SETUID], [1], [Define if your platform breaks doing a seteuid before a setuid]) AC_DEFINE([BROKEN_SETREUID], [1], [Define if your setreuid() is broken]) AC_DEFINE([BROKEN_SETREGID], [1], [Define if your setregid() is broken]) dnl AIX handles lastlog as part of its login message AC_DEFINE([DISABLE_LASTLOG], [1], [Define if you don't want to use lastlog]) AC_DEFINE([LOGIN_NEEDS_UTMPX], [1], [Some systems need a utmpx entry for /bin/login to work]) AC_DEFINE([SPT_TYPE], [SPT_REUSEARGV], [Define to a Set Process Title type if your system is supported by bsd-setproctitle.c]) AC_DEFINE([SSHPAM_CHAUTHTOK_NEEDS_RUID], [1], [AIX 5.2 and 5.3 (and presumably newer) require this]) AC_DEFINE([PTY_ZEROREAD], [1], [read(1) can return 0 for a non-closed fd]) AC_DEFINE([PLATFORM_SYS_DIR_UID], 2, [System dirs owned by bin (uid 2)]) AC_DEFINE([BROKEN_STRNDUP], 1, [strndup broken, see APAR IY61211]) AC_DEFINE([BROKEN_STRNLEN], 1, [strnlen broken, see APAR IY62551]) ;; *-*-android*) AC_DEFINE([DISABLE_UTMP], [1], [Define if you don't want to use utmp]) AC_DEFINE([DISABLE_WTMP], [1], [Define if you don't want to use wtmp]) ;; *-*-cygwin*) LIBS="$LIBS /usr/lib/textreadmode.o" AC_DEFINE([HAVE_CYGWIN], [1], [Define if you are on Cygwin]) AC_DEFINE([USE_PIPES], [1], [Use PIPES instead of a socketpair()]) AC_DEFINE([NO_UID_RESTORATION_TEST], [1], [Define to disable UID restoration test]) AC_DEFINE([DISABLE_SHADOW], [1], [Define if you want to disable shadow passwords]) AC_DEFINE([NO_X11_UNIX_SOCKETS], [1], [Define if X11 doesn't support AF_UNIX sockets on that system]) AC_DEFINE([DISABLE_FD_PASSING], [1], [Define if your platform needs to skip post auth file descriptor passing]) AC_DEFINE([SSH_IOBUFSZ], [65535], [Windows is sensitive to read buffer size]) AC_DEFINE([FILESYSTEM_NO_BACKSLASH], [1], [File names may not contain backslash characters]) # Cygwin defines optargs, optargs as declspec(dllimport) for historical # reasons which cause compile warnings, so we disable those warnings. OSSH_CHECK_CFLAG_COMPILE([-Wno-attributes]) ;; *-*-dgux*) AC_DEFINE([IP_TOS_IS_BROKEN], [1], [Define if your system choked on IP TOS setting]) AC_DEFINE([SETEUID_BREAKS_SETUID]) AC_DEFINE([BROKEN_SETREUID]) AC_DEFINE([BROKEN_SETREGID]) ;; *-*-darwin*) use_pie=auto AC_MSG_CHECKING([if we have working getaddrinfo]) AC_RUN_IFELSE([AC_LANG_SOURCE([[ #include #include int main(void) { if (NSVersionOfRunTimeLibrary("System") >= (60 << 16)) exit(0); else exit(1); } ]])], [AC_MSG_RESULT([working])], [AC_MSG_RESULT([buggy]) AC_DEFINE([BROKEN_GETADDRINFO], [1], [getaddrinfo is broken (if present)]) ], [AC_MSG_RESULT([assume it is working])]) AC_DEFINE([SETEUID_BREAKS_SETUID]) AC_DEFINE([BROKEN_SETREUID]) AC_DEFINE([BROKEN_SETREGID]) AC_DEFINE([BROKEN_GLOB], [1], [OS X glob does not do what we expect]) AC_DEFINE_UNQUOTED([BIND_8_COMPAT], [1], [Define if your resolver libs need this for getrrsetbyname]) AC_DEFINE([SSH_TUN_FREEBSD], [1], [Open tunnel devices the FreeBSD way]) AC_DEFINE([SSH_TUN_COMPAT_AF], [1], [Use tunnel device compatibility to OpenBSD]) AC_DEFINE([SSH_TUN_PREPEND_AF], [1], [Prepend the address family to IP tunnel traffic]) m4_pattern_allow([AU_IPv]) AC_CHECK_DECL([AU_IPv4], [], AC_DEFINE([AU_IPv4], [0], [System only supports IPv4 audit records]) [#include ] AC_DEFINE([LASTLOG_WRITE_PUTUTXLINE], [1], [Define if pututxline updates lastlog too]) ) AC_DEFINE([SPT_TYPE], [SPT_REUSEARGV], [Define to a Set Process Title type if your system is supported by bsd-setproctitle.c]) AC_CHECK_FUNCS([sandbox_init]) AC_CHECK_HEADERS([sandbox.h]) AC_CHECK_LIB([sandbox], [sandbox_apply], [ SSHDLIBS="$SSHDLIBS -lsandbox" ]) # proc_pidinfo()-based closefrom() replacement. AC_CHECK_HEADERS([libproc.h]) AC_CHECK_FUNCS([proc_pidinfo]) # poll(2) is broken for character-special devices (at least). # cf. Apple bug 3710161 (not public, but searchable) AC_DEFINE([BROKEN_POLL], [1], [System poll(2) implementation is broken]) ;; *-*-dragonfly*) SSHDLIBS="$SSHDLIBS" TEST_MALLOC_OPTIONS="AFGJPRX" ;; *-*-haiku*) LIBS="$LIBS -lbsd " CFLAGS="$CFLAGS -D_BSD_SOURCE" AC_CHECK_LIB([network], [socket]) AC_DEFINE([HAVE_U_INT64_T]) AC_DEFINE([DISABLE_UTMPX], [1], [no utmpx]) MANTYPE=man ;; *-*-hpux*) # first we define all of the options common to all HP-UX releases CPPFLAGS="$CPPFLAGS -D_HPUX_SOURCE -D_XOPEN_SOURCE -D_XOPEN_SOURCE_EXTENDED=1" IPADDR_IN_DISPLAY=yes AC_DEFINE([USE_PIPES]) AC_DEFINE([LOGIN_NEEDS_UTMPX]) AC_DEFINE([LOCKED_PASSWD_STRING], ["*"], [String used in /etc/passwd to denote locked account]) AC_DEFINE([SPT_TYPE], [SPT_PSTAT]) AC_DEFINE([PLATFORM_SYS_DIR_UID], 2, [System dirs owned by bin (uid 2)]) maildir="/var/mail" LIBS="$LIBS -lsec" AC_CHECK_LIB([xnet], [t_error], , [AC_MSG_ERROR([*** -lxnet needed on HP-UX - check config.log ***])]) # next, we define all of the options specific to major releases case "$host" in *-*-hpux10*) if test -z "$GCC"; then CFLAGS="$CFLAGS -Ae" fi AC_DEFINE([BROKEN_GETLINE], [1], [getline is not what we expect]) ;; *-*-hpux11*) AC_DEFINE([PAM_SUN_CODEBASE], [1], [Define if you are using Solaris-derived PAM which passes pam_messages to the conversation function with an extra level of indirection]) AC_DEFINE([DISABLE_UTMP], [1], [Define if you don't want to use utmp]) AC_DEFINE([USE_BTMP], [1], [Use btmp to log bad logins]) check_for_hpux_broken_getaddrinfo=1 check_for_conflicting_getspnam=1 ;; esac # lastly, we define options specific to minor releases case "$host" in *-*-hpux10.26) AC_DEFINE([HAVE_SECUREWARE], [1], [Define if you have SecureWare-based protected password database]) disable_ptmx_check=yes LIBS="$LIBS -lsecpw" ;; esac ;; *-*-irix5*) PATH="$PATH:/usr/etc" AC_DEFINE([BROKEN_INET_NTOA], [1], [Define if you system's inet_ntoa is busted (e.g. Irix gcc issue)]) AC_DEFINE([SETEUID_BREAKS_SETUID]) AC_DEFINE([BROKEN_SETREUID]) AC_DEFINE([BROKEN_SETREGID]) AC_DEFINE([WITH_ABBREV_NO_TTY], [1], [Define if you shouldn't strip 'tty' from your ttyname in [uw]tmp]) AC_DEFINE([LOCKED_PASSWD_STRING], ["*LK*"]) ;; *-*-irix6*) PATH="$PATH:/usr/etc" AC_DEFINE([WITH_IRIX_ARRAY], [1], [Define if you have/want arrays (cluster-wide session management, not C arrays)]) AC_DEFINE([WITH_IRIX_PROJECT], [1], [Define if you want IRIX project management]) AC_DEFINE([WITH_IRIX_AUDIT], [1], [Define if you want IRIX audit trails]) AC_CHECK_FUNC([jlimit_startjob], [AC_DEFINE([WITH_IRIX_JOBS], [1], [Define if you want IRIX kernel jobs])]) AC_DEFINE([BROKEN_INET_NTOA]) AC_DEFINE([SETEUID_BREAKS_SETUID]) AC_DEFINE([BROKEN_SETREUID]) AC_DEFINE([BROKEN_SETREGID]) AC_DEFINE([BROKEN_UPDWTMPX], [1], [updwtmpx is broken (if present)]) AC_DEFINE([WITH_ABBREV_NO_TTY]) AC_DEFINE([LOCKED_PASSWD_STRING], ["*LK*"]) ;; *-*-k*bsd*-gnu | *-*-kopensolaris*-gnu) AC_DEFINE([PAM_TTY_KLUDGE]) AC_DEFINE([LOCKED_PASSWD_PREFIX], ["!"]) AC_DEFINE([SPT_TYPE], [SPT_REUSEARGV]) AC_DEFINE([_PATH_BTMP], ["/var/log/btmp"], [log for bad login attempts]) AC_DEFINE([USE_BTMP], [1], [Use btmp to log bad logins]) ;; *-*-linux*) no_dev_ptmx=1 use_pie=auto check_for_openpty_ctty_bug=1 dnl Target SUSv3/POSIX.1-2001 plus BSD specifics. dnl _DEFAULT_SOURCE is the new name for _BSD_SOURCE dnl _GNU_SOURCE is needed for setres*id prototypes. CPPFLAGS="$CPPFLAGS -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_DEFAULT_SOURCE -D_GNU_SOURCE" AC_DEFINE([BROKEN_CLOSEFROM], [1], [broken in chroots on older kernels]) AC_DEFINE([PAM_TTY_KLUDGE], [1], [Work around problematic Linux PAM modules handling of PAM_TTY]) AC_DEFINE([LOCKED_PASSWD_PREFIX], ["!"], [String used in /etc/passwd to denote locked account]) AC_DEFINE([SPT_TYPE], [SPT_REUSEARGV]) AC_DEFINE([LINK_OPNOTSUPP_ERRNO], [EPERM], [Define to whatever link() returns for "not supported" if it doesn't return EOPNOTSUPP.]) AC_DEFINE([_PATH_BTMP], ["/var/log/btmp"], [log for bad login attempts]) AC_DEFINE([USE_BTMP]) AC_DEFINE([LINUX_OOM_ADJUST], [1], [Adjust Linux out-of-memory killer]) inet6_default_4in6=yes case `uname -r` in 1.*|2.0.*) AC_DEFINE([BROKEN_CMSG_TYPE], [1], [Define if cmsg_type is not passed correctly]) ;; esac # tun(4) forwarding compat code AC_CHECK_HEADERS([linux/if_tun.h]) if test "x$ac_cv_header_linux_if_tun_h" = "xyes" ; then AC_DEFINE([SSH_TUN_LINUX], [1], [Open tunnel devices the Linux tun/tap way]) AC_DEFINE([SSH_TUN_COMPAT_AF], [1], [Use tunnel device compatibility to OpenBSD]) AC_DEFINE([SSH_TUN_PREPEND_AF], [1], [Prepend the address family to IP tunnel traffic]) fi AC_CHECK_HEADER([linux/if.h], AC_DEFINE([SYS_RDOMAIN_LINUX], [1], [Support routing domains using Linux VRF]), [], [ #ifdef HAVE_SYS_TYPES_H # include #endif ]) AC_CHECK_HEADERS([linux/seccomp.h linux/filter.h linux/audit.h], [], [], [#include ]) # Obtain MIPS ABI case "$host" in mips*) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #if _MIPS_SIM != _ABIO32 #error #endif ]])],[mips_abi="o32"],[AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #if _MIPS_SIM != _ABIN32 #error #endif ]])],[mips_abi="n32"],[AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #if _MIPS_SIM != _ABI64 #error #endif ]])],[mips_abi="n64"],[AC_MSG_ERROR([unknown MIPS ABI]) ]) ]) ]) ;; esac AC_MSG_CHECKING([for seccomp architecture]) seccomp_audit_arch= case "$host" in x86_64-*) seccomp_audit_arch=AUDIT_ARCH_X86_64 ;; i*86-*) seccomp_audit_arch=AUDIT_ARCH_I386 ;; arm*-*) seccomp_audit_arch=AUDIT_ARCH_ARM ;; aarch64*-*) seccomp_audit_arch=AUDIT_ARCH_AARCH64 ;; s390x-*) seccomp_audit_arch=AUDIT_ARCH_S390X ;; s390-*) seccomp_audit_arch=AUDIT_ARCH_S390 ;; powerpc-*) seccomp_audit_arch=AUDIT_ARCH_PPC ;; powerpc64-*) seccomp_audit_arch=AUDIT_ARCH_PPC64 ;; powerpc64le-*) seccomp_audit_arch=AUDIT_ARCH_PPC64LE ;; mips-*) seccomp_audit_arch=AUDIT_ARCH_MIPS ;; mipsel-*) seccomp_audit_arch=AUDIT_ARCH_MIPSEL ;; mips64-*) case "$mips_abi" in "n32") seccomp_audit_arch=AUDIT_ARCH_MIPS64N32 ;; "n64") seccomp_audit_arch=AUDIT_ARCH_MIPS64 ;; esac ;; mips64el-*) case "$mips_abi" in "n32") seccomp_audit_arch=AUDIT_ARCH_MIPSEL64N32 ;; "n64") seccomp_audit_arch=AUDIT_ARCH_MIPSEL64 ;; esac ;; riscv64-*) seccomp_audit_arch=AUDIT_ARCH_RISCV64 ;; esac if test "x$seccomp_audit_arch" != "x" ; then AC_MSG_RESULT(["$seccomp_audit_arch"]) AC_DEFINE_UNQUOTED([SECCOMP_AUDIT_ARCH], [$seccomp_audit_arch], [Specify the system call convention in use]) else AC_MSG_RESULT([architecture not supported]) fi ;; *-*-minix) AC_DEFINE([SETEUID_BREAKS_SETUID]) # poll(2) seems to choke on /dev/null; "Bad file descriptor" AC_DEFINE([BROKEN_POLL], [1], [System poll(2) implementation is broken]) ;; mips-sony-bsd|mips-sony-newsos4) AC_DEFINE([NEED_SETPGRP], [1], [Need setpgrp to acquire controlling tty]) SONY=1 ;; *-*-netbsd*) if test "x$withval" != "xno" ; then rpath_opt="-R" fi CPPFLAGS="$CPPFLAGS -D_OPENBSD_SOURCE" AC_DEFINE([SSH_TUN_FREEBSD], [1], [Open tunnel devices the FreeBSD way]) AC_CHECK_HEADER([net/if_tap.h], , AC_DEFINE([SSH_TUN_NO_L2], [1], [No layer 2 tunnel support])) AC_DEFINE([SSH_TUN_PREPEND_AF], [1], [Prepend the address family to IP tunnel traffic]) TEST_MALLOC_OPTIONS="AJRX" AC_DEFINE([BROKEN_READ_COMPARISON], [1], [NetBSD read function is sometimes redirected, breaking atomicio comparisons against it]) ;; *-*-freebsd*) AC_DEFINE([LOCKED_PASSWD_PREFIX], ["*LOCKED*"], [Account locked with pw(1)]) AC_DEFINE([SSH_TUN_FREEBSD], [1], [Open tunnel devices the FreeBSD way]) AC_CHECK_HEADER([net/if_tap.h], , AC_DEFINE([SSH_TUN_NO_L2], [1], [No layer 2 tunnel support])) AC_DEFINE([BROKEN_GLOB], [1], [FreeBSD glob does not do what we need]) TEST_MALLOC_OPTIONS="AJRX" # Preauth crypto occasionally uses file descriptors for crypto offload # and will crash if they cannot be opened. AC_DEFINE([SANDBOX_SKIP_RLIMIT_NOFILE], [1], [define if setrlimit RLIMIT_NOFILE breaks things]) case "$host" in *-*-freebsd9.*|*-*-freebsd10.*) # Capsicum on 9 and 10 do not allow ppoll() so don't auto-enable. disable_capsicum=yes esac ;; *-*-bsdi*) AC_DEFINE([SETEUID_BREAKS_SETUID]) AC_DEFINE([BROKEN_SETREUID]) AC_DEFINE([BROKEN_SETREGID]) ;; *-next-*) conf_lastlog_location="/usr/adm/lastlog" conf_utmp_location=/etc/utmp conf_wtmp_location=/usr/adm/wtmp maildir=/usr/spool/mail AC_DEFINE([HAVE_NEXT], [1], [Define if you are on NeXT]) AC_DEFINE([USE_PIPES]) AC_DEFINE([BROKEN_SAVED_UIDS], [1], [Needed for NeXT]) ;; *-*-openbsd*) use_pie=auto AC_DEFINE([HAVE_ATTRIBUTE__SENTINEL__], [1], [OpenBSD's gcc has sentinel]) AC_DEFINE([HAVE_ATTRIBUTE__BOUNDED__], [1], [OpenBSD's gcc has bounded]) AC_DEFINE([SSH_TUN_OPENBSD], [1], [Open tunnel devices the OpenBSD way]) AC_DEFINE([SYSLOG_R_SAFE_IN_SIGHAND], [1], [syslog_r function is safe to use in in a signal handler]) TEST_MALLOC_OPTIONS="AFGJPRX" ;; *-*-solaris*) if test "x$withval" != "xno" ; then rpath_opt="-R" fi AC_DEFINE([PAM_SUN_CODEBASE]) AC_DEFINE([LOGIN_NEEDS_UTMPX]) AC_DEFINE([PAM_TTY_KLUDGE]) AC_DEFINE([SSHPAM_CHAUTHTOK_NEEDS_RUID], [1], [Define if pam_chauthtok wants real uid set to the unpriv'ed user]) AC_DEFINE([LOCKED_PASSWD_STRING], ["*LK*"]) # Pushing STREAMS modules will cause sshd to acquire a controlling tty. AC_DEFINE([SSHD_ACQUIRES_CTTY], [1], [Define if sshd somehow reacquires a controlling TTY after setsid()]) AC_DEFINE([PASSWD_NEEDS_USERNAME], [1], [must supply username to passwd in case the name is longer than 8 chars]) AC_DEFINE([BROKEN_TCGETATTR_ICANON], [1], [tcgetattr with ICANON may hang]) external_path_file=/etc/default/login # hardwire lastlog location (can't detect it on some versions) conf_lastlog_location="/var/adm/lastlog" AC_MSG_CHECKING([for obsolete utmp and wtmp in solaris2.x]) sol2ver=`echo "$host"| sed -e 's/.*[[0-9]]\.//'` if test "$sol2ver" -ge 8; then AC_MSG_RESULT([yes]) AC_DEFINE([DISABLE_UTMP]) AC_DEFINE([DISABLE_WTMP], [1], [Define if you don't want to use wtmp]) else AC_MSG_RESULT([no]) fi AC_CHECK_FUNCS([setpflags]) AC_CHECK_FUNCS([setppriv]) AC_CHECK_FUNCS([priv_basicset]) AC_CHECK_HEADERS([priv.h]) AC_ARG_WITH([solaris-contracts], [ --with-solaris-contracts Enable Solaris process contracts (experimental)], [ AC_CHECK_LIB([contract], [ct_tmpl_activate], [ AC_DEFINE([USE_SOLARIS_PROCESS_CONTRACTS], [1], [Define if you have Solaris process contracts]) LIBS="$LIBS -lcontract" SPC_MSG="yes" ], ) ], ) AC_ARG_WITH([solaris-projects], [ --with-solaris-projects Enable Solaris projects (experimental)], [ AC_CHECK_LIB([project], [setproject], [ AC_DEFINE([USE_SOLARIS_PROJECTS], [1], [Define if you have Solaris projects]) LIBS="$LIBS -lproject" SP_MSG="yes" ], ) ], ) AC_ARG_WITH([solaris-privs], [ --with-solaris-privs Enable Solaris/Illumos privileges (experimental)], [ AC_MSG_CHECKING([for Solaris/Illumos privilege support]) if test "x$ac_cv_func_setppriv" = "xyes" -a \ "x$ac_cv_header_priv_h" = "xyes" ; then SOLARIS_PRIVS=yes AC_MSG_RESULT([found]) AC_DEFINE([NO_UID_RESTORATION_TEST], [1], [Define to disable UID restoration test]) AC_DEFINE([USE_SOLARIS_PRIVS], [1], [Define if you have Solaris privileges]) SPP_MSG="yes" else AC_MSG_RESULT([not found]) AC_MSG_ERROR([*** must have support for Solaris privileges to use --with-solaris-privs]) fi ], ) TEST_SHELL=$SHELL # let configure find us a capable shell ;; *-*-sunos4*) CPPFLAGS="$CPPFLAGS -DSUNOS4" AC_CHECK_FUNCS([getpwanam]) AC_DEFINE([PAM_SUN_CODEBASE]) conf_utmp_location=/etc/utmp conf_wtmp_location=/var/adm/wtmp conf_lastlog_location=/var/adm/lastlog AC_DEFINE([USE_PIPES]) AC_DEFINE([DISABLE_UTMPX], [1], [no utmpx]) ;; *-ncr-sysv*) LIBS="$LIBS -lc89" AC_DEFINE([USE_PIPES]) AC_DEFINE([SSHD_ACQUIRES_CTTY]) AC_DEFINE([SETEUID_BREAKS_SETUID]) AC_DEFINE([BROKEN_SETREUID]) AC_DEFINE([BROKEN_SETREGID]) ;; *-sni-sysv*) # /usr/ucblib MUST NOT be searched on ReliantUNIX AC_CHECK_LIB([dl], [dlsym], ,) # -lresolv needs to be at the end of LIBS or DNS lookups break AC_CHECK_LIB([resolv], [res_query], [ LIBS="$LIBS -lresolv" ]) IPADDR_IN_DISPLAY=yes AC_DEFINE([USE_PIPES]) AC_DEFINE([IP_TOS_IS_BROKEN]) AC_DEFINE([SETEUID_BREAKS_SETUID]) AC_DEFINE([BROKEN_SETREUID]) AC_DEFINE([BROKEN_SETREGID]) AC_DEFINE([SSHD_ACQUIRES_CTTY]) external_path_file=/etc/default/login # /usr/ucblib/libucb.a no longer needed on ReliantUNIX # Attention: always take care to bind libsocket and libnsl before libc, # otherwise you will find lots of "SIOCGPGRP errno 22" on syslog ;; # UnixWare 1.x, UnixWare 2.x, and others based on code from Univel. *-*-sysv4.2*) AC_DEFINE([USE_PIPES]) AC_DEFINE([SETEUID_BREAKS_SETUID]) AC_DEFINE([BROKEN_SETREUID]) AC_DEFINE([BROKEN_SETREGID]) AC_DEFINE([PASSWD_NEEDS_USERNAME], [1], [must supply username to passwd]) AC_DEFINE([LOCKED_PASSWD_STRING], ["*LK*"]) TEST_SHELL=$SHELL # let configure find us a capable shell ;; # UnixWare 7.x, OpenUNIX 8 *-*-sysv5*) CPPFLAGS="$CPPFLAGS -Dvsnprintf=_xvsnprintf -Dsnprintf=_xsnprintf" AC_DEFINE([UNIXWARE_LONG_PASSWORDS], [1], [Support passwords > 8 chars]) AC_DEFINE([USE_PIPES]) AC_DEFINE([SETEUID_BREAKS_SETUID]) AC_DEFINE([BROKEN_GETADDRINFO]) AC_DEFINE([BROKEN_SETREUID]) AC_DEFINE([BROKEN_SETREGID]) AC_DEFINE([PASSWD_NEEDS_USERNAME]) AC_DEFINE([BROKEN_TCGETATTR_ICANON]) TEST_SHELL=$SHELL # let configure find us a capable shell case "$host" in *-*-sysv5SCO_SV*) # SCO OpenServer 6.x maildir=/var/spool/mail AC_DEFINE([BROKEN_UPDWTMPX]) AC_CHECK_LIB([prot], [getluid], [ LIBS="$LIBS -lprot" AC_CHECK_FUNCS([getluid setluid], , , [-lprot]) ], , ) ;; *) AC_DEFINE([LOCKED_PASSWD_STRING], ["*LK*"]) ;; esac ;; *-*-sysv*) ;; # SCO UNIX and OEM versions of SCO UNIX *-*-sco3.2v4*) AC_MSG_ERROR("This Platform is no longer supported.") ;; # SCO OpenServer 5.x *-*-sco3.2v5*) if test -z "$GCC"; then CFLAGS="$CFLAGS -belf" fi LIBS="$LIBS -lprot -lx -ltinfo -lm" no_dev_ptmx=1 AC_DEFINE([USE_PIPES]) AC_DEFINE([HAVE_SECUREWARE]) AC_DEFINE([DISABLE_SHADOW]) AC_DEFINE([DISABLE_FD_PASSING]) AC_DEFINE([SETEUID_BREAKS_SETUID]) AC_DEFINE([BROKEN_GETADDRINFO]) AC_DEFINE([BROKEN_SETREUID]) AC_DEFINE([BROKEN_SETREGID]) AC_DEFINE([WITH_ABBREV_NO_TTY]) AC_DEFINE([BROKEN_UPDWTMPX]) AC_DEFINE([PASSWD_NEEDS_USERNAME]) AC_CHECK_FUNCS([getluid setluid]) MANTYPE=man TEST_SHELL=$SHELL # let configure find us a capable shell SKIP_DISABLE_LASTLOG_DEFINE=yes ;; *-dec-osf*) AC_MSG_CHECKING([for Digital Unix SIA]) no_osfsia="" AC_ARG_WITH([osfsia], [ --with-osfsia Enable Digital Unix SIA], [ if test "x$withval" = "xno" ; then AC_MSG_RESULT([disabled]) no_osfsia=1 fi ], ) if test -z "$no_osfsia" ; then if test -f /etc/sia/matrix.conf; then AC_MSG_RESULT([yes]) AC_DEFINE([HAVE_OSF_SIA], [1], [Define if you have Digital Unix Security Integration Architecture]) AC_DEFINE([DISABLE_LOGIN], [1], [Define if you don't want to use your system's login() call]) AC_DEFINE([DISABLE_FD_PASSING]) LIBS="$LIBS -lsecurity -ldb -lm -laud" SIA_MSG="yes" else AC_MSG_RESULT([no]) AC_DEFINE([LOCKED_PASSWD_SUBSTR], ["Nologin"], [String used in /etc/passwd to denote locked account]) fi fi AC_DEFINE([BROKEN_GETADDRINFO]) AC_DEFINE([SETEUID_BREAKS_SETUID]) AC_DEFINE([BROKEN_SETREUID]) AC_DEFINE([BROKEN_SETREGID]) AC_DEFINE([BROKEN_READV_COMPARISON], [1], [Can't do comparisons on readv]) ;; *-*-nto-qnx*) AC_DEFINE([USE_PIPES]) AC_DEFINE([NO_X11_UNIX_SOCKETS]) AC_DEFINE([DISABLE_LASTLOG]) AC_DEFINE([SSHD_ACQUIRES_CTTY]) AC_DEFINE([BROKEN_SHADOW_EXPIRE], [1], [QNX shadow support is broken]) enable_etc_default_login=no # has incompatible /etc/default/login case "$host" in *-*-nto-qnx6*) AC_DEFINE([DISABLE_FD_PASSING]) ;; esac ;; *-*-ultrix*) AC_DEFINE([BROKEN_GETGROUPS], [1], [getgroups(0,NULL) will return -1]) AC_DEFINE([NEED_SETPGRP], [1], [Need setpgrp to for controlling tty]) AC_DEFINE([HAVE_SYS_SYSLOG_H], [1], [Force use of sys/syslog.h on Ultrix]) AC_DEFINE([DISABLE_UTMPX], [1], [Disable utmpx]) # DISABLE_FD_PASSING so that we call setpgrp as root, otherwise we # don't get a controlling tty. AC_DEFINE([DISABLE_FD_PASSING], [1], [Need to call setpgrp as root]) # On Ultrix some headers are not protected against multiple includes, # so we create wrappers and put it where the compiler will find it. AC_MSG_WARN([creating compat wrappers for headers]) mkdir -p netinet for header in netinet/ip.h netdb.h resolv.h; do name=`echo $header | tr 'a-z/.' 'A-Z__'` cat >$header < ]], [[ exit(0); ]])], [ AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) AC_MSG_ERROR([*** compiler cannot create working executables, check config.log ***]) ], [ AC_MSG_WARN([cross compiling: not checking compiler sanity]) ] ) dnl Checks for header files. # Checks for libraries. AC_CHECK_FUNC([setsockopt], , [AC_CHECK_LIB([socket], [setsockopt])]) dnl IRIX and Solaris 2.5.1 have dirname() in libgen AC_CHECK_FUNCS([dirname], [AC_CHECK_HEADERS([libgen.h])] , [ AC_CHECK_LIB([gen], [dirname], [ AC_CACHE_CHECK([for broken dirname], ac_cv_have_broken_dirname, [ save_LIBS="$LIBS" LIBS="$LIBS -lgen" AC_RUN_IFELSE( [AC_LANG_SOURCE([[ #include #include #include int main(int argc, char **argv) { char *s, buf[32]; strncpy(buf,"/etc", 32); s = dirname(buf); if (!s || strncmp(s, "/", 32) != 0) { exit(1); } else { exit(0); } } ]])], [ ac_cv_have_broken_dirname="no" ], [ ac_cv_have_broken_dirname="yes" ], [ ac_cv_have_broken_dirname="no" ], ) LIBS="$save_LIBS" ]) if test "x$ac_cv_have_broken_dirname" = "xno" ; then LIBS="$LIBS -lgen" AC_DEFINE([HAVE_DIRNAME]) AC_CHECK_HEADERS([libgen.h]) fi ]) ]) AC_CHECK_FUNC([getspnam], , [AC_CHECK_LIB([gen], [getspnam], [LIBS="$LIBS -lgen"])]) AC_SEARCH_LIBS([basename], [gen], [AC_DEFINE([HAVE_BASENAME], [1], [Define if you have the basename function.])]) dnl zlib defaults to enabled zlib=yes AC_ARG_WITH([zlib], [ --with-zlib=PATH Use zlib in PATH], [ if test "x$withval" = "xno" ; then zlib=no elif test "x$withval" != "xyes"; then if test -d "$withval/lib"; then if test -n "${rpath_opt}"; then LDFLAGS="-L${withval}/lib ${rpath_opt}${withval}/lib ${LDFLAGS}" else LDFLAGS="-L${withval}/lib ${LDFLAGS}" fi else if test -n "${rpath_opt}"; then LDFLAGS="-L${withval} ${rpath_opt}${withval} ${LDFLAGS}" else LDFLAGS="-L${withval} ${LDFLAGS}" fi fi if test -d "$withval/include"; then CPPFLAGS="-I${withval}/include ${CPPFLAGS}" else CPPFLAGS="-I${withval} ${CPPFLAGS}" fi fi ] ) # These libraries are needed for anything that links in the channel code. CHANNELLIBS="" AC_MSG_CHECKING([for zlib]) if test "x${zlib}" = "xno"; then AC_MSG_RESULT([no]) else saved_LIBS="$LIBS" CHANNELLIBS="$CHANNELLIBS -lz" AC_MSG_RESULT([yes]) AC_DEFINE([WITH_ZLIB], [1], [Enable zlib]) AC_CHECK_HEADER([zlib.h], ,[AC_MSG_ERROR([*** zlib.h missing - please install first or check config.log ***])]) AC_CHECK_LIB([z], [deflate], [], [ saved_CPPFLAGS="$CPPFLAGS" saved_LDFLAGS="$LDFLAGS" dnl Check default zlib install dir if test -n "${rpath_opt}"; then LDFLAGS="-L/usr/local/lib ${rpath_opt}/usr/local/lib ${saved_LDFLAGS}" else LDFLAGS="-L/usr/local/lib ${saved_LDFLAGS}" fi CPPFLAGS="-I/usr/local/include ${saved_CPPFLAGS}" AC_TRY_LINK_FUNC([deflate], [AC_DEFINE([HAVE_LIBZ])], [ AC_MSG_ERROR([*** zlib missing - please install first or check config.log ***]) ] ) ] ) AC_ARG_WITH([zlib-version-check], [ --without-zlib-version-check Disable zlib version check], [ if test "x$withval" = "xno" ; then zlib_check_nonfatal=1 fi ] ) AC_MSG_CHECKING([for possibly buggy zlib]) AC_RUN_IFELSE([AC_LANG_PROGRAM([[ #include #include #include ]], [[ int a=0, b=0, c=0, d=0, n, v; n = sscanf(ZLIB_VERSION, "%d.%d.%d.%d", &a, &b, &c, &d); - if (n != 3 && n != 4) + if (n < 1) exit(1); v = a*1000000 + b*10000 + c*100 + d; fprintf(stderr, "found zlib version %s (%d)\n", ZLIB_VERSION, v); /* 1.1.4 is OK */ if (a == 1 && b == 1 && c >= 4) exit(0); /* 1.2.3 and up are OK */ if (v >= 1020300) exit(0); exit(2); ]])], AC_MSG_RESULT([no]), [ AC_MSG_RESULT([yes]) if test -z "$zlib_check_nonfatal" ; then AC_MSG_ERROR([*** zlib too old - check config.log *** Your reported zlib version has known security problems. It's possible your vendor has fixed these problems without changing the version number. If you are sure this is the case, you can disable the check by running "./configure --without-zlib-version-check". If you are in doubt, upgrade zlib to version 1.2.3 or greater. See http://www.gzip.org/zlib/ for details.]) else AC_MSG_WARN([zlib version may have security problems]) fi ], [ AC_MSG_WARN([cross compiling: not checking zlib version]) ] ) LIBS="$saved_LIBS" fi dnl UnixWare 2.x AC_CHECK_FUNC([strcasecmp], [], [ AC_CHECK_LIB([resolv], [strcasecmp], [LIBS="$LIBS -lresolv"]) ] ) AC_CHECK_FUNCS([utimes], [], [ AC_CHECK_LIB([c89], [utimes], [AC_DEFINE([HAVE_UTIMES]) LIBS="$LIBS -lc89"]) ] ) dnl Checks for libutil functions AC_CHECK_HEADERS([bsd/libutil.h libutil.h]) AC_SEARCH_LIBS([fmt_scaled], [util bsd]) AC_SEARCH_LIBS([scan_scaled], [util bsd]) AC_SEARCH_LIBS([login], [util bsd]) AC_SEARCH_LIBS([logout], [util bsd]) AC_SEARCH_LIBS([logwtmp], [util bsd]) AC_SEARCH_LIBS([openpty], [util bsd]) AC_SEARCH_LIBS([updwtmp], [util bsd]) AC_CHECK_FUNCS([fmt_scaled scan_scaled login logout openpty updwtmp logwtmp]) # On some platforms, inet_ntop and gethostbyname may be found in libresolv # or libnsl. AC_SEARCH_LIBS([inet_ntop], [resolv nsl]) AC_SEARCH_LIBS([gethostbyname], [resolv nsl]) # Some Linux distribtions ship the BSD libc hashing functions in # separate libraries. AC_SEARCH_LIBS([SHA256Update], [md bsd]) # "Particular Function Checks" # see https://www.gnu.org/software/autoconf/manual/autoconf-2.69/html_node/Particular-Functions.html AC_FUNC_STRFTIME AC_FUNC_MALLOC AC_FUNC_REALLOC # autoconf doesn't have AC_FUNC_CALLOC so fake it if malloc returns NULL; AC_MSG_CHECKING([if calloc(0, N) returns non-null]) AC_RUN_IFELSE( [AC_LANG_PROGRAM( [[ #include ]], [[ void *p = calloc(0, 1); exit(p == NULL); ]] )], [ func_calloc_0_nonnull=yes ], [ func_calloc_0_nonnull=no ], [ AC_MSG_WARN([cross compiling: assuming same as malloc]) func_calloc_0_nonnull="$ac_cv_func_malloc_0_nonnull"] ) AC_MSG_RESULT([$func_calloc_0_nonnull]) if test "x$func_calloc_0_nonnull" = "xyes"; then AC_DEFINE(HAVE_CALLOC, 1, [calloc(0, x) returns non-null]) else AC_DEFINE(HAVE_CALLOC, 0, [calloc(0, x) returns NULL]) AC_DEFINE(calloc, rpl_calloc, [Define to rpl_calloc if the replacement function should be used.]) fi # Check for ALTDIRFUNC glob() extension AC_MSG_CHECKING([for GLOB_ALTDIRFUNC support]) AC_EGREP_CPP([FOUNDIT], [ #include #ifdef GLOB_ALTDIRFUNC FOUNDIT #endif ], [ AC_DEFINE([GLOB_HAS_ALTDIRFUNC], [1], [Define if your system glob() function has the GLOB_ALTDIRFUNC extension]) AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) ] ) # Check for g.gl_matchc glob() extension AC_MSG_CHECKING([for gl_matchc field in glob_t]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ glob_t g; g.gl_matchc = 1; ]])], [ AC_DEFINE([GLOB_HAS_GL_MATCHC], [1], [Define if your system glob() function has gl_matchc options in glob_t]) AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) ]) # Check for g.gl_statv glob() extension AC_MSG_CHECKING([for gl_statv and GLOB_KEEPSTAT extensions for glob]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ #ifndef GLOB_KEEPSTAT #error "glob does not support GLOB_KEEPSTAT extension" #endif glob_t g; g.gl_statv = NULL; ]])], [ AC_DEFINE([GLOB_HAS_GL_STATV], [1], [Define if your system glob() function has gl_statv options in glob_t]) AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) ]) AC_CHECK_DECLS([GLOB_NOMATCH], , , [#include ]) AC_CHECK_DECL([VIS_ALL], , AC_DEFINE(BROKEN_STRNVIS, 1, [missing VIS_ALL]), [#include ]) AC_MSG_CHECKING([whether struct dirent allocates space for d_name]) AC_RUN_IFELSE( [AC_LANG_PROGRAM([[ #include #include #include ]], [[ struct dirent d; exit(sizeof(d.d_name)<=sizeof(char)); ]])], [AC_MSG_RESULT([yes])], [ AC_MSG_RESULT([no]) AC_DEFINE([BROKEN_ONE_BYTE_DIRENT_D_NAME], [1], [Define if your struct dirent expects you to allocate extra space for d_name]) ], [ AC_MSG_WARN([cross compiling: assuming BROKEN_ONE_BYTE_DIRENT_D_NAME]) AC_DEFINE([BROKEN_ONE_BYTE_DIRENT_D_NAME]) ] ) AC_MSG_CHECKING([for /proc/pid/fd directory]) if test -d "/proc/$$/fd" ; then AC_DEFINE([HAVE_PROC_PID], [1], [Define if you have /proc/$pid/fd]) AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) fi # Check whether user wants to use ldns LDNS_MSG="no" AC_ARG_WITH(ldns, [ --with-ldns[[=PATH]] Use ldns for DNSSEC support (optionally in PATH)], [ ldns="" if test "x$withval" = "xyes" ; then AC_PATH_TOOL([LDNSCONFIG], [ldns-config], [no]) if test "x$LDNSCONFIG" = "xno"; then LIBS="-lldns $LIBS" ldns=yes else LIBS="$LIBS `$LDNSCONFIG --libs`" CPPFLAGS="$CPPFLAGS `$LDNSCONFIG --cflags`" ldns=yes fi elif test "x$withval" != "xno" ; then CPPFLAGS="$CPPFLAGS -I${withval}/include" LDFLAGS="$LDFLAGS -L${withval}/lib" LIBS="-lldns $LIBS" ldns=yes fi # Verify that it works. if test "x$ldns" = "xyes" ; then AC_DEFINE(HAVE_LDNS, 1, [Define if you want ldns support]) LDNS_MSG="yes" AC_MSG_CHECKING([for ldns support]) AC_LINK_IFELSE( [AC_LANG_SOURCE([[ #include #include #ifdef HAVE_STDINT_H # include #endif #include int main(void) { ldns_status status = ldns_verify_trusted(NULL, NULL, NULL, NULL); status=LDNS_STATUS_OK; exit(0); } ]]) ], [AC_MSG_RESULT(yes)], [ AC_MSG_RESULT(no) AC_MSG_ERROR([** Incomplete or missing ldns libraries.]) ]) fi ]) # Check whether user wants libedit support LIBEDIT_MSG="no" AC_ARG_WITH([libedit], [ --with-libedit[[=PATH]] Enable libedit support for sftp], [ if test "x$withval" != "xno" ; then if test "x$withval" = "xyes" ; then if test "x$PKGCONFIG" != "xno"; then AC_MSG_CHECKING([if $PKGCONFIG knows about libedit]) if "$PKGCONFIG" libedit; then AC_MSG_RESULT([yes]) use_pkgconfig_for_libedit=yes else AC_MSG_RESULT([no]) fi fi else CPPFLAGS="$CPPFLAGS -I${withval}/include" if test -n "${rpath_opt}"; then LDFLAGS="-L${withval}/lib ${rpath_opt}${withval}/lib ${LDFLAGS}" else LDFLAGS="-L${withval}/lib ${LDFLAGS}" fi fi if test "x$use_pkgconfig_for_libedit" = "xyes"; then LIBEDIT=`$PKGCONFIG --libs libedit` CPPFLAGS="$CPPFLAGS `$PKGCONFIG --cflags libedit`" else LIBEDIT="-ledit -lcurses" fi OTHERLIBS=`echo $LIBEDIT | sed 's/-ledit//'` AC_CHECK_LIB([edit], [el_init], [ AC_DEFINE([USE_LIBEDIT], [1], [Use libedit for sftp]) LIBEDIT_MSG="yes" AC_SUBST([LIBEDIT]) ], [ AC_MSG_ERROR([libedit not found]) ], [ $OTHERLIBS ] ) AC_MSG_CHECKING([if libedit version is compatible]) AC_COMPILE_IFELSE( [AC_LANG_PROGRAM([[ #include #include ]], [[ int i = H_SETSIZE; el_init("", NULL, NULL, NULL); exit(0); ]])], [ AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) AC_MSG_ERROR([libedit version is not compatible]) ] ) fi ] ) AUDIT_MODULE=none AC_ARG_WITH([audit], [ --with-audit=module Enable audit support (modules=debug,bsm,linux)], [ AC_MSG_CHECKING([for supported audit module]) case "$withval" in bsm) AC_MSG_RESULT([bsm]) AUDIT_MODULE=bsm dnl Checks for headers, libs and functions AC_CHECK_HEADERS([bsm/audit.h], [], [AC_MSG_ERROR([BSM enabled and bsm/audit.h not found])], [ #ifdef HAVE_TIME_H # include #endif ] ) AC_CHECK_LIB([bsm], [getaudit], [], [AC_MSG_ERROR([BSM enabled and required library not found])]) AC_CHECK_FUNCS([getaudit], [], [AC_MSG_ERROR([BSM enabled and required function not found])]) # These are optional AC_CHECK_FUNCS([getaudit_addr aug_get_machine]) AC_DEFINE([USE_BSM_AUDIT], [1], [Use BSM audit module]) if test "$sol2ver" -ge 11; then SSHDLIBS="$SSHDLIBS -lscf" AC_DEFINE([BROKEN_BSM_API], [1], [The system has incomplete BSM API]) fi ;; linux) AC_MSG_RESULT([linux]) AUDIT_MODULE=linux dnl Checks for headers, libs and functions AC_CHECK_HEADERS([libaudit.h]) SSHDLIBS="$SSHDLIBS -laudit" AC_DEFINE([USE_LINUX_AUDIT], [1], [Use Linux audit module]) ;; debug) AUDIT_MODULE=debug AC_MSG_RESULT([debug]) AC_DEFINE([SSH_AUDIT_EVENTS], [1], [Use audit debugging module]) ;; no) AC_MSG_RESULT([no]) ;; *) AC_MSG_ERROR([Unknown audit module $withval]) ;; esac ] ) AC_ARG_WITH([pie], [ --with-pie Build Position Independent Executables if possible], [ if test "x$withval" = "xno"; then use_pie=no fi if test "x$withval" = "xyes"; then use_pie=yes fi ] ) if test "x$use_pie" = "x"; then use_pie=no fi if test "x$use_toolchain_hardening" != "x1" && test "x$use_pie" = "xauto"; then # Turn off automatic PIE when toolchain hardening is off. use_pie=no fi if test "x$use_pie" = "xauto"; then # Automatic PIE requires gcc >= 4.x AC_MSG_CHECKING([for gcc >= 4.x]) AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ #if !defined(__GNUC__) || __GNUC__ < 4 #error gcc is too old #endif ]])], [ AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) use_pie=no ] ) fi if test "x$use_pie" != "xno"; then SAVED_CFLAGS="$CFLAGS" SAVED_LDFLAGS="$LDFLAGS" OSSH_CHECK_CFLAG_COMPILE([-fPIE]) OSSH_CHECK_LDFLAG_LINK([-pie]) # We use both -fPIE and -pie or neither. AC_MSG_CHECKING([whether both -fPIE and -pie are supported]) if echo "x $CFLAGS" | grep ' -fPIE' >/dev/null 2>&1 && \ echo "x $LDFLAGS" | grep ' -pie' >/dev/null 2>&1 ; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) CFLAGS="$SAVED_CFLAGS" LDFLAGS="$SAVED_LDFLAGS" fi fi AC_MSG_CHECKING([whether -fPIC is accepted]) SAVED_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -fPIC" AC_COMPILE_IFELSE( [AC_LANG_PROGRAM( [[ #include ]], [[ exit(0); ]] )], [AC_MSG_RESULT([yes]) PICFLAG="-fPIC"; ], [AC_MSG_RESULT([no]) PICFLAG=""; ]) CFLAGS="$SAVED_CFLAGS" AC_SUBST([PICFLAG]) dnl Checks for library functions. Please keep in alphabetical order AC_CHECK_FUNCS([ \ Blowfish_initstate \ Blowfish_expandstate \ Blowfish_expand0state \ Blowfish_stream2word \ SHA256Update \ SHA384Update \ SHA512Update \ asprintf \ b64_ntop \ __b64_ntop \ b64_pton \ __b64_pton \ bcopy \ bcrypt_pbkdf \ bindresvport_sa \ blf_enc \ bzero \ cap_rights_limit \ clock \ closefrom \ close_range \ dirfd \ endgrent \ err \ errx \ explicit_bzero \ explicit_memset \ fchmod \ fchmodat \ fchown \ fchownat \ flock \ fnmatch \ freeaddrinfo \ freezero \ fstatfs \ fstatvfs \ futimes \ getaddrinfo \ getcwd \ getentropy \ getgrouplist \ getline \ getnameinfo \ getopt \ getpagesize \ getpeereid \ getpeerucred \ getpgid \ _getpty \ getrlimit \ getrandom \ getsid \ getttyent \ glob \ group_from_gid \ inet_aton \ inet_ntoa \ inet_ntop \ innetgr \ killpg \ llabs \ localtime_r \ login_getcapbool \ login_getpwclass \ memmem \ memmove \ memset_s \ mkdtemp \ ngetaddrinfo \ nsleep \ ogetaddrinfo \ openlog_r \ pledge \ poll \ ppoll \ prctl \ procctl \ pselect \ pstat \ raise \ readpassphrase \ reallocarray \ realpath \ recvmsg \ recallocarray \ rresvport_af \ sendmsg \ setdtablesize \ setegid \ setenv \ seteuid \ setgroupent \ setgroups \ setlinebuf \ setlogin \ setpassent\ setpcred \ setproctitle \ setregid \ setreuid \ setrlimit \ setsid \ setvbuf \ sigaction \ sigvec \ snprintf \ socketpair \ statfs \ statvfs \ strcasestr \ strdup \ strerror \ strlcat \ strlcpy \ strmode \ strndup \ strnlen \ strnvis \ strptime \ strsignal \ strtonum \ strtoll \ strtoul \ strtoull \ swap32 \ sysconf \ tcgetpgrp \ timegm \ timingsafe_bcmp \ truncate \ unsetenv \ updwtmpx \ utimensat \ user_from_uid \ usleep \ vasprintf \ vsnprintf \ waitpid \ warn \ ]) AC_CHECK_DECLS([bzero, memmem]) dnl Wide character support. AC_CHECK_FUNCS([mblen mbtowc nl_langinfo wcwidth]) TEST_SSH_UTF8=${TEST_SSH_UTF8:=yes} AC_MSG_CHECKING([for utf8 locale support]) AC_RUN_IFELSE( [AC_LANG_PROGRAM([[ #include #include ]], [[ char *loc = setlocale(LC_CTYPE, "en_US.UTF-8"); if (loc != NULL) exit(0); exit(1); ]])], AC_MSG_RESULT(yes), [AC_MSG_RESULT(no) TEST_SSH_UTF8=no], AC_MSG_WARN([cross compiling: assuming yes]) ) AC_LINK_IFELSE( [AC_LANG_PROGRAM( [[ #include ]], [[ return (isblank('a')); ]])], [AC_DEFINE([HAVE_ISBLANK], [1], [Define if you have isblank(3C).]) ]) disable_pkcs11= AC_ARG_ENABLE([pkcs11], [ --disable-pkcs11 disable PKCS#11 support code [no]], [ if test "x$enableval" = "xno" ; then disable_pkcs11=1 fi ] ) disable_sk= AC_ARG_ENABLE([security-key], [ --disable-security-key disable U2F/FIDO support code [no]], [ if test "x$enableval" = "xno" ; then disable_sk=1 fi ] ) enable_sk_internal= AC_ARG_WITH([security-key-builtin], [ --with-security-key-builtin include builtin U2F/FIDO support], [ enable_sk_internal=$withval ] ) AC_SEARCH_LIBS([dlopen], [dl]) AC_CHECK_FUNCS([dlopen]) AC_CHECK_DECL([RTLD_NOW], [], [], [#include ]) # IRIX has a const char return value for gai_strerror() AC_CHECK_FUNCS([gai_strerror], [ AC_DEFINE([HAVE_GAI_STRERROR]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include #include const char *gai_strerror(int); ]], [[ char *str; str = gai_strerror(0); ]])], [ AC_DEFINE([HAVE_CONST_GAI_STRERROR_PROTO], [1], [Define if gai_strerror() returns const char *])], [])]) AC_SEARCH_LIBS([nanosleep], [rt posix4], [AC_DEFINE([HAVE_NANOSLEEP], [1], [Some systems put nanosleep outside of libc])]) AC_SEARCH_LIBS([clock_gettime], [rt], [AC_DEFINE([HAVE_CLOCK_GETTIME], [1], [Have clock_gettime])]) dnl check if we need -D_REENTRANT for localtime_r declaration. AC_CHECK_DECL([localtime_r], [], [ saved_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS -D_REENTRANT" unset ac_cv_have_decl_localtime_r AC_CHECK_DECL([localtime_r], [], [ CPPFLAGS="$saved_CPPFLAGS" ], [ #include ] ) ], [ #include ] ) dnl Make sure prototypes are defined for these before using them. AC_CHECK_DECL([strsep], [AC_CHECK_FUNCS([strsep])], [], [ #ifdef HAVE_STRING_H # include #endif ]) dnl tcsendbreak might be a macro AC_CHECK_DECL([tcsendbreak], [AC_DEFINE([HAVE_TCSENDBREAK])], [AC_CHECK_FUNCS([tcsendbreak])], [#include ] ) AC_CHECK_DECLS([h_errno], , ,[#include ]) AC_CHECK_DECLS([SHUT_RD, getpeereid], , , [ #include #include #include ]) AC_CHECK_DECLS([O_NONBLOCK], , , [ #include #ifdef HAVE_SYS_STAT_H # include #endif #ifdef HAVE_FCNTL_H # include #endif ]) AC_CHECK_DECLS([ftruncate, getentropy], , , [ #include #include ]) AC_CHECK_DECLS([readv, writev], , , [ #include #include #include ]) AC_CHECK_DECLS([MAXSYMLINKS], , , [ #include ]) AC_CHECK_DECLS([offsetof], , , [ #include ]) # extra bits for select(2) AC_CHECK_DECLS([howmany, NFDBITS], [], [], [[ #include #include #ifdef HAVE_SYS_SYSMACROS_H #include #endif #ifdef HAVE_SYS_SELECT_H #include #endif #ifdef HAVE_SYS_TIME_H #include #endif #ifdef HAVE_UNISTD_H #include #endif ]]) AC_CHECK_TYPES([fd_mask], [], [], [[ #include #include #ifdef HAVE_SYS_SELECT_H #include #endif #ifdef HAVE_SYS_TIME_H #include #endif #ifdef HAVE_UNISTD_H #include #endif ]]) AC_CHECK_FUNCS([setresuid], [ dnl Some platorms have setresuid that isn't implemented, test for this AC_MSG_CHECKING([if setresuid seems to work]) AC_RUN_IFELSE( [AC_LANG_PROGRAM([[ #include #include #include ]], [[ errno=0; setresuid(0,0,0); if (errno==ENOSYS) exit(1); else exit(0); ]])], [AC_MSG_RESULT([yes])], [AC_DEFINE([BROKEN_SETRESUID], [1], [Define if your setresuid() is broken]) AC_MSG_RESULT([not implemented])], [AC_MSG_WARN([cross compiling: not checking setresuid])] ) ]) AC_CHECK_FUNCS([setresgid], [ dnl Some platorms have setresgid that isn't implemented, test for this AC_MSG_CHECKING([if setresgid seems to work]) AC_RUN_IFELSE( [AC_LANG_PROGRAM([[ #include #include #include ]], [[ errno=0; setresgid(0,0,0); if (errno==ENOSYS) exit(1); else exit(0); ]])], [AC_MSG_RESULT([yes])], [AC_DEFINE([BROKEN_SETRESGID], [1], [Define if your setresgid() is broken]) AC_MSG_RESULT([not implemented])], [AC_MSG_WARN([cross compiling: not checking setresuid])] ) ]) AC_MSG_CHECKING([for working fflush(NULL)]) AC_RUN_IFELSE( [AC_LANG_PROGRAM([[ #include #include ]], [[fflush(NULL); exit(0);]])], AC_MSG_RESULT([yes]), [AC_MSG_RESULT([no]) AC_DEFINE([FFLUSH_NULL_BUG], [1], [define if fflush(NULL) does not work])], AC_MSG_WARN([cross compiling: assuming working]) ) dnl Checks for time functions AC_CHECK_FUNCS([gettimeofday time]) dnl Checks for utmp functions AC_CHECK_FUNCS([endutent getutent getutid getutline pututline setutent]) AC_CHECK_FUNCS([utmpname]) dnl Checks for utmpx functions AC_CHECK_FUNCS([endutxent getutxent getutxid getutxline getutxuser pututxline]) AC_CHECK_FUNCS([setutxdb setutxent utmpxname]) dnl Checks for lastlog functions AC_CHECK_FUNCS([getlastlogxbyname]) AC_CHECK_FUNC([daemon], [AC_DEFINE([HAVE_DAEMON], [1], [Define if your libraries define daemon()])], [AC_CHECK_LIB([bsd], [daemon], [LIBS="$LIBS -lbsd"; AC_DEFINE([HAVE_DAEMON])])] ) AC_CHECK_FUNC([getpagesize], [AC_DEFINE([HAVE_GETPAGESIZE], [1], [Define if your libraries define getpagesize()])], [AC_CHECK_LIB([ucb], [getpagesize], [LIBS="$LIBS -lucb"; AC_DEFINE([HAVE_GETPAGESIZE])])] ) # Check for broken snprintf if test "x$ac_cv_func_snprintf" = "xyes" ; then AC_MSG_CHECKING([whether snprintf correctly terminates long strings]) AC_RUN_IFELSE( [AC_LANG_PROGRAM([[ #include #include ]], [[ char b[5]; snprintf(b,5,"123456789"); exit(b[4]!='\0'); ]])], [AC_MSG_RESULT([yes])], [ AC_MSG_RESULT([no]) AC_DEFINE([BROKEN_SNPRINTF], [1], [Define if your snprintf is busted]) AC_MSG_WARN([****** Your snprintf() function is broken, complain to your vendor]) ], [ AC_MSG_WARN([cross compiling: Assuming working snprintf()]) ] ) fi if test "x$ac_cv_func_snprintf" = "xyes" ; then AC_MSG_CHECKING([whether snprintf understands %zu]) AC_RUN_IFELSE( [AC_LANG_PROGRAM([[ #include #include #include #include ]], [[ size_t a = 1, b = 2; char z[128]; snprintf(z, sizeof z, "%zu%zu", a, b); exit(strcmp(z, "12")); ]])], [AC_MSG_RESULT([yes])], [ AC_MSG_RESULT([no]) AC_DEFINE([BROKEN_SNPRINTF], [1], [snprintf does not understand %zu]) ], [ AC_MSG_WARN([cross compiling: Assuming working snprintf()]) ] ) fi # We depend on vsnprintf returning the right thing on overflow: the # number of characters it tried to create (as per SUSv3) if test "x$ac_cv_func_vsnprintf" = "xyes" ; then AC_MSG_CHECKING([whether vsnprintf returns correct values on overflow]) AC_RUN_IFELSE( [AC_LANG_PROGRAM([[ #include #include #include int x_snprintf(char *str, size_t count, const char *fmt, ...) { size_t ret; va_list ap; va_start(ap, fmt); ret = vsnprintf(str, count, fmt, ap); va_end(ap); return ret; } ]], [[ char x[1]; if (x_snprintf(x, 1, "%s %d", "hello", 12345) != 11) return 1; if (x_snprintf(NULL, 0, "%s %d", "hello", 12345) != 11) return 1; return 0; ]])], [AC_MSG_RESULT([yes])], [ AC_MSG_RESULT([no]) AC_DEFINE([BROKEN_SNPRINTF], [1], [Define if your snprintf is busted]) AC_MSG_WARN([****** Your vsnprintf() function is broken, complain to your vendor]) ], [ AC_MSG_WARN([cross compiling: Assuming working vsnprintf()]) ] ) fi # On systems where [v]snprintf is broken, but is declared in stdio, # check that the fmt argument is const char * or just char *. # This is only useful for when BROKEN_SNPRINTF AC_MSG_CHECKING([whether snprintf can declare const char *fmt]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include int snprintf(char *a, size_t b, const char *c, ...) { return 0; } ]], [[ snprintf(0, 0, 0); ]])], [AC_MSG_RESULT([yes]) AC_DEFINE([SNPRINTF_CONST], [const], [Define as const if snprintf() can declare const char *fmt])], [AC_MSG_RESULT([no]) AC_DEFINE([SNPRINTF_CONST], [/* not const */])]) # Check for missing getpeereid (or equiv) support NO_PEERCHECK="" if test "x$ac_cv_func_getpeereid" != "xyes" -a "x$ac_cv_func_getpeerucred" != "xyes"; then AC_MSG_CHECKING([whether system supports SO_PEERCRED getsockopt]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include ]], [[int i = SO_PEERCRED;]])], [ AC_MSG_RESULT([yes]) AC_DEFINE([HAVE_SO_PEERCRED], [1], [Have PEERCRED socket option]) ], [AC_MSG_RESULT([no]) NO_PEERCHECK=1 ]) fi dnl make sure that openpty does not reacquire controlling terminal if test ! -z "$check_for_openpty_ctty_bug"; then AC_MSG_CHECKING([if openpty correctly handles controlling tty]) AC_RUN_IFELSE( [AC_LANG_PROGRAM([[ #include #include #include #ifdef HAVE_PTY_H # include #endif #include #include #include ]], [[ pid_t pid; int fd, ptyfd, ttyfd, status; pid = fork(); if (pid < 0) { /* failed */ exit(1); } else if (pid > 0) { /* parent */ waitpid(pid, &status, 0); if (WIFEXITED(status)) exit(WEXITSTATUS(status)); else exit(2); } else { /* child */ close(0); close(1); close(2); setsid(); openpty(&ptyfd, &ttyfd, NULL, NULL, NULL); fd = open("/dev/tty", O_RDWR | O_NOCTTY); if (fd >= 0) exit(3); /* Acquired ctty: broken */ else exit(0); /* Did not acquire ctty: OK */ } ]])], [ AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) AC_DEFINE([SSHD_ACQUIRES_CTTY]) ], [ AC_MSG_RESULT([cross-compiling, assuming yes]) ] ) fi if test "x$ac_cv_func_getaddrinfo" = "xyes" && \ test "x$check_for_hpux_broken_getaddrinfo" = "x1"; then AC_MSG_CHECKING([if getaddrinfo seems to work]) AC_RUN_IFELSE( [AC_LANG_PROGRAM([[ #include #include #include #include #include #include #define TEST_PORT "2222" ]], [[ int err, sock; struct addrinfo *gai_ai, *ai, hints; char ntop[NI_MAXHOST], strport[NI_MAXSERV], *name = NULL; memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; err = getaddrinfo(name, TEST_PORT, &hints, &gai_ai); if (err != 0) { fprintf(stderr, "getaddrinfo failed (%s)", gai_strerror(err)); exit(1); } for (ai = gai_ai; ai != NULL; ai = ai->ai_next) { if (ai->ai_family != AF_INET6) continue; err = getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV); if (err != 0) { if (err == EAI_SYSTEM) perror("getnameinfo EAI_SYSTEM"); else fprintf(stderr, "getnameinfo failed: %s\n", gai_strerror(err)); exit(2); } sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (sock < 0) perror("socket"); if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { if (errno == EBADF) exit(3); } } exit(0); ]])], [ AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) AC_DEFINE([BROKEN_GETADDRINFO]) ], [ AC_MSG_RESULT([cross-compiling, assuming yes]) ] ) fi if test "x$ac_cv_func_getaddrinfo" = "xyes" && \ test "x$check_for_aix_broken_getaddrinfo" = "x1"; then AC_MSG_CHECKING([if getaddrinfo seems to work]) AC_RUN_IFELSE( [AC_LANG_PROGRAM([[ #include #include #include #include #include #include #define TEST_PORT "2222" ]], [[ int err, sock; struct addrinfo *gai_ai, *ai, hints; char ntop[NI_MAXHOST], strport[NI_MAXSERV], *name = NULL; memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; err = getaddrinfo(name, TEST_PORT, &hints, &gai_ai); if (err != 0) { fprintf(stderr, "getaddrinfo failed (%s)", gai_strerror(err)); exit(1); } for (ai = gai_ai; ai != NULL; ai = ai->ai_next) { if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) continue; err = getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV); if (ai->ai_family == AF_INET && err != 0) { perror("getnameinfo"); exit(2); } } exit(0); ]])], [ AC_MSG_RESULT([yes]) AC_DEFINE([AIX_GETNAMEINFO_HACK], [1], [Define if you have a getaddrinfo that fails for the all-zeros IPv6 address]) ], [ AC_MSG_RESULT([no]) AC_DEFINE([BROKEN_GETADDRINFO]) ], [ AC_MSG_RESULT([cross-compiling, assuming no]) ] ) fi if test "x$ac_cv_func_getaddrinfo" = "xyes"; then AC_CHECK_DECLS(AI_NUMERICSERV, , , [#include #include #include ]) fi if test "x$check_for_conflicting_getspnam" = "x1"; then AC_MSG_CHECKING([for conflicting getspnam in shadow.h]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include ]], [[ exit(0); ]])], [ AC_MSG_RESULT([no]) ], [ AC_MSG_RESULT([yes]) AC_DEFINE([GETSPNAM_CONFLICTING_DEFS], [1], [Conflicting defs for getspnam]) ] ) fi dnl NetBSD added an strnvis and unfortunately made it incompatible with the dnl existing one in OpenBSD and Linux's libbsd (the former having existed dnl for over ten years). Despite this incompatibility being reported during dnl development (see http://gnats.netbsd.org/44977) they still shipped it. dnl Even more unfortunately FreeBSD and later MacOS picked up this incompatible dnl implementation. Try to detect this mess, and assume the only safe option dnl if we're cross compiling. dnl dnl OpenBSD, 2001: strnvis(char *dst, const char *src, size_t dlen, int flag); dnl NetBSD: 2012, strnvis(char *dst, size_t dlen, const char *src, int flag); if test "x$ac_cv_func_strnvis" = "xyes"; then AC_MSG_CHECKING([for working strnvis]) AC_RUN_IFELSE( [AC_LANG_PROGRAM([[ #include #include #include #include #include static void sighandler(int sig) { _exit(1); } ]], [[ char dst[16]; signal(SIGSEGV, sighandler); if (strnvis(dst, "src", 4, 0) && strcmp(dst, "src") == 0) exit(0); exit(1) ]])], [AC_MSG_RESULT([yes])], [AC_MSG_RESULT([no]) AC_DEFINE([BROKEN_STRNVIS], [1], [strnvis detected broken])], [AC_MSG_WARN([cross compiling: assuming broken]) AC_DEFINE([BROKEN_STRNVIS], [1], [strnvis assumed broken])] ) fi AC_MSG_CHECKING([if SA_RESTARTed signals interrupt select()]) AC_RUN_IFELSE( [AC_LANG_PROGRAM([[ #ifdef HAVE_SYS_SELECT # include #endif #include #include #include #include #include static void sighandler(int sig) { } ]], [[ int r; pid_t pid; struct sigaction sa; sa.sa_handler = sighandler; sa.sa_flags = SA_RESTART; (void)sigaction(SIGTERM, &sa, NULL); if ((pid = fork()) == 0) { /* child */ pid = getppid(); sleep(1); kill(pid, SIGTERM); sleep(1); if (getppid() == pid) /* if parent did not exit, shoot it */ kill(pid, SIGKILL); exit(0); } else { /* parent */ r = select(0, NULL, NULL, NULL, NULL); } exit(r == -1 ? 0 : 1); ]])], [AC_MSG_RESULT([yes])], [AC_MSG_RESULT([no]) AC_DEFINE([NO_SA_RESTART], [1], [SA_RESTARTed signals do no interrupt select])], [AC_MSG_WARN([cross compiling: assuming yes])] ) AC_CHECK_FUNCS([getpgrp],[ AC_MSG_CHECKING([if getpgrp accepts zero args]) AC_COMPILE_IFELSE( [AC_LANG_PROGRAM([[$ac_includes_default]], [[ getpgrp(); ]])], [ AC_MSG_RESULT([yes]) AC_DEFINE([GETPGRP_VOID], [1], [getpgrp takes zero args])], [ AC_MSG_RESULT([no]) AC_DEFINE([GETPGRP_VOID], [0], [getpgrp takes one arg])] ) ]) # Search for OpenSSL saved_CPPFLAGS="$CPPFLAGS" saved_LDFLAGS="$LDFLAGS" openssl_bin_PATH="$PATH" AC_ARG_WITH([ssl-dir], [ --with-ssl-dir=PATH Specify path to OpenSSL installation ], [ if test "x$openssl" = "xno" ; then AC_MSG_ERROR([cannot use --with-ssl-dir when OpenSSL disabled]) fi if test "x$withval" != "xno" ; then case "$withval" in # Relative paths ./*|../*) withval="`pwd`/$withval" esac if test -d "$withval/lib"; then libcrypto_path="${withval}/lib" elif test -d "$withval/lib64"; then libcrypto_path="$withval/lib64" else # Built but not installed libcrypto_path="${withval}" fi if test -n "${rpath_opt}"; then LDFLAGS="-L${libcrypto_path} ${rpath_opt}${libcrypto_path} ${LDFLAGS}" else LDFLAGS="-L${libcrypto_path} ${LDFLAGS}" fi if test -d "$withval/include"; then CPPFLAGS="-I${withval}/include ${CPPFLAGS}" else CPPFLAGS="-I${withval} ${CPPFLAGS}" fi openssl_bin_PATH="${PATH}${PATH_SEPARATOR}${withval}/bin${PATH_SEPARATOR}${withval}/apps" fi ] ) AC_PATH_PROGS([openssl_bin], openssl, [], [$openssl_bin_PATH]) AC_SUBST(OPENSSL_BIN, [${openssl_bin}]) AC_ARG_WITH([openssl-header-check], [ --without-openssl-header-check Disable OpenSSL version consistency check], [ if test "x$withval" = "xno" ; then openssl_check_nonfatal=1 fi ] ) openssl_engine=no AC_ARG_WITH([ssl-engine], [ --with-ssl-engine Enable OpenSSL (hardware) ENGINE support ], [ if test "x$withval" != "xno" ; then if test "x$openssl" = "xno" ; then AC_MSG_ERROR([cannot use --with-ssl-engine when OpenSSL disabled]) fi openssl_engine=yes fi ] ) nocrypto_saved_LIBS="$LIBS" if test "x$openssl" = "xyes" ; then LIBS="-lcrypto $LIBS" CHANNELLIBS="-lcrypto $CHANNELLIBS" AC_TRY_LINK_FUNC([RAND_add], , [AC_MSG_ERROR([*** working libcrypto not found, check config.log])]) AC_CHECK_HEADER([openssl/opensslv.h], , [AC_MSG_ERROR([*** OpenSSL headers missing - please install first or check config.log ***])]) # Determine OpenSSL header version AC_MSG_CHECKING([OpenSSL header version]) AC_RUN_IFELSE( [AC_LANG_PROGRAM([[ #include #include #include #include #define DATA "conftest.sslincver" ]], [[ FILE *fd; int rc; fd = fopen(DATA,"w"); if(fd == NULL) exit(1); if ((rc = fprintf(fd, "%08lx (%s)\n", (unsigned long)OPENSSL_VERSION_NUMBER, OPENSSL_VERSION_TEXT)) < 0) exit(1); exit(0); ]])], [ ssl_header_ver=`cat conftest.sslincver` AC_MSG_RESULT([$ssl_header_ver]) ], [ AC_MSG_RESULT([not found]) AC_MSG_ERROR([OpenSSL version header not found.]) ], [ AC_MSG_WARN([cross compiling: not checking]) ] ) # Determining OpenSSL library version is version dependent. AC_CHECK_FUNCS([OpenSSL_version OpenSSL_version_num]) # Determine OpenSSL library version AC_MSG_CHECKING([OpenSSL library version]) AC_RUN_IFELSE( [AC_LANG_PROGRAM([[ #include #include #include #include #include #define DATA "conftest.ssllibver" ]], [[ FILE *f; /* We need these legacy bits to warn for old libcrypto */ #ifndef OPENSSL_VERSION # define OPENSSL_VERSION SSLEAY_VERSION #endif #ifndef HAVE_OPENSSL_VERSION # define OpenSSL_version SSLeay_version #endif #ifndef HAVE_OPENSSL_VERSION_NUM # define OpenSSL_version_num SSLeay #endif if ((f = fopen(DATA, "w")) == NULL) exit(1); if (fprintf(f, "%08lx (%s)", (unsigned long)OpenSSL_version_num(), OpenSSL_version(OPENSSL_VERSION)) < 0) exit(1); #ifdef LIBRESSL_VERSION_NUMBER if (fprintf(f, " libressl-%08lx", LIBRESSL_VERSION_NUMBER) < 0) exit(1); #endif if (fputc('\n', f) == EOF || fclose(f) == EOF) exit(1); exit(0); ]])], [ sslver=`cat conftest.ssllibver` ssl_showver=`echo "$sslver" | sed 's/ libressl-.*//'` # Check version is supported. case "$sslver" in 100*|10100*) # 1.0.x, 1.1.0x AC_MSG_ERROR([OpenSSL >= 1.1.1 required (have "$ssl_showver")]) ;; 101*) ;; # 1.1.x 200*) # LibreSSL lver=`echo "$sslver" | sed 's/.*libressl-//'` case "$lver" in 2*|300*) # 2.x, 3.0.0 AC_MSG_ERROR([LibreSSL >= 3.1.0 required (have "$ssl_showver")]) ;; *) ;; # Assume all other versions are good. esac ;; 300*) # OpenSSL 3; we use the 1.1x API CPPFLAGS="$CPPFLAGS -DOPENSSL_API_COMPAT=0x10100000L" ;; 301*|302*) # OpenSSL development branch; request 1.1x API CPPFLAGS="$CPPFLAGS -DOPENSSL_API_COMPAT=0x10100000L" ;; *) AC_MSG_ERROR([Unknown/unsupported OpenSSL version ("$ssl_showver")]) ;; esac AC_MSG_RESULT([$ssl_showver]) ], [ AC_MSG_RESULT([not found]) AC_MSG_ERROR([OpenSSL library not found.]) ], [ AC_MSG_WARN([cross compiling: not checking]) ] ) case "$host" in x86_64-*) case "$sslver" in 3000004*) AC_MSG_ERROR([OpenSSL 3.0.4 has a potential RCE in its RSA implementation (CVE-2022-2274)]) ;; esac esac # Sanity check OpenSSL headers AC_MSG_CHECKING([whether OpenSSL's headers match the library]) AC_RUN_IFELSE( [AC_LANG_PROGRAM([[ #include #include #include #include ]], [[ exit(OpenSSL_version_num() == OPENSSL_VERSION_NUMBER ? 0 : 1); ]])], [ AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) if test "x$openssl_check_nonfatal" = "x"; then AC_MSG_ERROR([Your OpenSSL headers do not match your library. Check config.log for details. If you are sure your installation is consistent, you can disable the check by running "./configure --without-openssl-header-check". Also see contrib/findssl.sh for help identifying header/library mismatches. ]) else AC_MSG_WARN([Your OpenSSL headers do not match your library. Check config.log for details. Also see contrib/findssl.sh for help identifying header/library mismatches.]) fi ], [ AC_MSG_WARN([cross compiling: not checking]) ] ) AC_MSG_CHECKING([if programs using OpenSSL functions will link]) AC_LINK_IFELSE( [AC_LANG_PROGRAM([[ #include ]], [[ ERR_load_crypto_strings(); ]])], [ AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) LIBS="$LIBS -ldl" AC_MSG_CHECKING([if programs using OpenSSL need -ldl]) AC_LINK_IFELSE( [AC_LANG_PROGRAM([[ #include ]], [[ ERR_load_crypto_strings(); ]])], [ AC_MSG_RESULT([yes]) CHANNELLIBS="$CHANNELLIBS -ldl" ], [ AC_MSG_RESULT([no]) ] ) ] ) AC_CHECK_FUNCS([ \ BN_is_prime_ex \ DES_crypt \ DSA_generate_parameters_ex \ EVP_DigestFinal_ex \ EVP_DigestInit_ex \ EVP_MD_CTX_cleanup \ EVP_MD_CTX_copy_ex \ EVP_MD_CTX_init \ HMAC_CTX_init \ RSA_generate_key_ex \ RSA_get_default_method \ ]) # OpenSSL_add_all_algorithms may be a macro. AC_CHECK_FUNC(OpenSSL_add_all_algorithms, AC_DEFINE(HAVE_OPENSSL_ADD_ALL_ALGORITHMS, 1, [as a function]), AC_CHECK_DECL(OpenSSL_add_all_algorithms, AC_DEFINE(HAVE_OPENSSL_ADD_ALL_ALGORITHMS, 1, [as a macro]), , [[#include ]] ) ) # LibreSSL/OpenSSL API differences AC_CHECK_FUNCS([ \ EVP_CIPHER_CTX_iv \ EVP_CIPHER_CTX_iv_noconst \ EVP_CIPHER_CTX_get_iv \ EVP_CIPHER_CTX_get_updated_iv \ EVP_CIPHER_CTX_set_iv \ ]) if test "x$openssl_engine" = "xyes" ; then AC_MSG_CHECKING([for OpenSSL ENGINE support]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ ENGINE_load_builtin_engines(); ENGINE_register_all_complete(); ]])], [ AC_MSG_RESULT([yes]) AC_DEFINE([USE_OPENSSL_ENGINE], [1], [Enable OpenSSL engine support]) ], [ AC_MSG_ERROR([OpenSSL ENGINE support not found]) ]) fi # Check for OpenSSL without EVP_aes_{192,256}_cbc AC_MSG_CHECKING([whether OpenSSL has crippled AES support]) AC_LINK_IFELSE( [AC_LANG_PROGRAM([[ #include #include #include ]], [[ exit(EVP_aes_192_cbc() == NULL || EVP_aes_256_cbc() == NULL); ]])], [ AC_MSG_RESULT([no]) ], [ AC_MSG_RESULT([yes]) AC_DEFINE([OPENSSL_LOBOTOMISED_AES], [1], [libcrypto is missing AES 192 and 256 bit functions]) ] ) AC_MSG_CHECKING([if EVP_DigestUpdate returns an int]) AC_LINK_IFELSE( [AC_LANG_PROGRAM([[ #include #include #include ]], [[ if(EVP_DigestUpdate(NULL, NULL,0)) exit(0); ]])], [ AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) AC_DEFINE([OPENSSL_EVP_DIGESTUPDATE_VOID], [1], [Define if EVP_DigestUpdate returns void]) ] ) # Check for various EVP support in OpenSSL AC_CHECK_FUNCS([EVP_sha256 EVP_sha384 EVP_sha512 EVP_chacha20]) # Check complete ECC support in OpenSSL AC_MSG_CHECKING([whether OpenSSL has NID_X9_62_prime256v1]) AC_LINK_IFELSE( [AC_LANG_PROGRAM([[ #include #include #include #include #include #include ]], [[ EC_KEY *e = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); const EVP_MD *m = EVP_sha256(); /* We need this too */ ]])], [ AC_MSG_RESULT([yes]) enable_nistp256=1 ], [ AC_MSG_RESULT([no]) ] ) AC_MSG_CHECKING([whether OpenSSL has NID_secp384r1]) AC_LINK_IFELSE( [AC_LANG_PROGRAM([[ #include #include #include #include #include #include ]], [[ EC_KEY *e = EC_KEY_new_by_curve_name(NID_secp384r1); const EVP_MD *m = EVP_sha384(); /* We need this too */ ]])], [ AC_MSG_RESULT([yes]) enable_nistp384=1 ], [ AC_MSG_RESULT([no]) ] ) AC_MSG_CHECKING([whether OpenSSL has NID_secp521r1]) AC_LINK_IFELSE( [AC_LANG_PROGRAM([[ #include #include #include #include #include #include ]], [[ EC_KEY *e = EC_KEY_new_by_curve_name(NID_secp521r1); const EVP_MD *m = EVP_sha512(); /* We need this too */ ]])], [ AC_MSG_RESULT([yes]) AC_MSG_CHECKING([if OpenSSL's NID_secp521r1 is functional]) AC_RUN_IFELSE( [AC_LANG_PROGRAM([[ #include #include #include #include #include #include #include ]],[[ EC_KEY *e = EC_KEY_new_by_curve_name(NID_secp521r1); const EVP_MD *m = EVP_sha512(); /* We need this too */ exit(e == NULL || m == NULL); ]])], [ AC_MSG_RESULT([yes]) enable_nistp521=1 ], [ AC_MSG_RESULT([no]) ], [ AC_MSG_WARN([cross-compiling: assuming yes]) enable_nistp521=1 ] )], AC_MSG_RESULT([no]) ) if test x$enable_nistp256 = x1 || test x$enable_nistp384 = x1 || \ test x$enable_nistp521 = x1; then AC_DEFINE(OPENSSL_HAS_ECC, [1], [OpenSSL has ECC]) AC_CHECK_FUNCS([EC_KEY_METHOD_new]) openssl_ecc=yes else openssl_ecc=no fi if test x$enable_nistp256 = x1; then AC_DEFINE([OPENSSL_HAS_NISTP256], [1], [libcrypto has NID_X9_62_prime256v1]) else unsupported_algorithms="$unsupported_algorithms \ ecdsa-sha2-nistp256 \ ecdh-sha2-nistp256 \ ecdsa-sha2-nistp256-cert-v01@openssh.com" fi if test x$enable_nistp384 = x1; then AC_DEFINE([OPENSSL_HAS_NISTP384], [1], [libcrypto has NID_secp384r1]) else unsupported_algorithms="$unsupported_algorithms \ ecdsa-sha2-nistp384 \ ecdh-sha2-nistp384 \ ecdsa-sha2-nistp384-cert-v01@openssh.com" fi if test x$enable_nistp521 = x1; then AC_DEFINE([OPENSSL_HAS_NISTP521], [1], [libcrypto has NID_secp521r1]) else unsupported_algorithms="$unsupported_algorithms \ ecdh-sha2-nistp521 \ ecdsa-sha2-nistp521 \ ecdsa-sha2-nistp521-cert-v01@openssh.com" fi fi # PKCS11/U2F depend on OpenSSL and dlopen(). enable_pkcs11=yes enable_sk=yes if test "x$openssl" != "xyes" ; then enable_pkcs11="disabled; missing libcrypto" fi if test "x$ac_cv_func_dlopen" != "xyes" ; then enable_pkcs11="disabled; missing dlopen(3)" enable_sk="disabled; missing dlopen(3)" fi if test "x$ac_cv_have_decl_RTLD_NOW" != "xyes" ; then enable_pkcs11="disabled; missing RTLD_NOW" enable_sk="disabled; missing RTLD_NOW" fi if test ! -z "$disable_pkcs11" ; then enable_pkcs11="disabled by user" fi if test ! -z "$disable_sk" ; then enable_sk="disabled by user" fi AC_MSG_CHECKING([whether to enable PKCS11]) if test "x$enable_pkcs11" = "xyes" ; then AC_DEFINE([ENABLE_PKCS11], [], [Enable for PKCS#11 support]) fi AC_MSG_RESULT([$enable_pkcs11]) AC_MSG_CHECKING([whether to enable U2F]) if test "x$enable_sk" = "xyes" ; then AC_DEFINE([ENABLE_SK], [], [Enable for U2F/FIDO support]) AC_SUBST(SK_DUMMY_LIBRARY, [regress/misc/sk-dummy/sk-dummy.so]) else # Do not try to build sk-dummy library. AC_SUBST(SK_DUMMY_LIBRARY, [""]) fi AC_MSG_RESULT([$enable_sk]) # Now check for built-in security key support. if test "x$enable_sk" = "xyes" -a "x$enable_sk_internal" != "xno" ; then use_pkgconfig_for_libfido2= if test "x$PKGCONFIG" != "xno"; then AC_MSG_CHECKING([if $PKGCONFIG knows about libfido2]) if "$PKGCONFIG" libfido2; then AC_MSG_RESULT([yes]) use_pkgconfig_for_libfido2=yes else AC_MSG_RESULT([no]) fi fi if test "x$use_pkgconfig_for_libfido2" = "xyes"; then LIBFIDO2=`$PKGCONFIG --libs libfido2` CPPFLAGS="$CPPFLAGS `$PKGCONFIG --cflags libfido2`" else LIBFIDO2="-lfido2 -lcbor" fi OTHERLIBS=`echo $LIBFIDO2 | sed 's/-lfido2//'` fido2_error= AC_CHECK_LIB([fido2], [fido_init], [ ], [ fido2_error="missing/unusable libfido2" ], [ $OTHERLIBS ] ) AC_CHECK_HEADER([fido.h], [], [ fido2_error="missing fido.h from libfido2" ]) AC_CHECK_HEADER([fido/credman.h], [], [ fido2_error="missing fido/credman.h from libfido2" ], [ #include ] ) AC_MSG_CHECKING([for usable libfido2 installation]) if test ! -z "$fido2_error" ; then AC_MSG_RESULT([$fido2_error]) if test "x$enable_sk_internal" = "xyes" ; then AC_MSG_ERROR([No usable libfido2 library/headers found]) fi LIBFIDO2="" else AC_MSG_RESULT([yes]) AC_SUBST([LIBFIDO2]) AC_DEFINE([ENABLE_SK_INTERNAL], [], [Enable for built-in U2F/FIDO support]) enable_sk="built-in" saved_LIBS="$LIBS" LIBS="$LIBFIDO2 $LIBS" AC_CHECK_FUNCS([ \ fido_assert_set_clientdata \ fido_cred_prot \ fido_cred_set_prot \ fido_cred_set_clientdata \ fido_dev_get_touch_begin \ fido_dev_get_touch_status \ fido_dev_supports_cred_prot \ fido_dev_is_winhello \ ]) LIBS="$saved_LIBS" fi fi AC_CHECK_FUNCS([ \ arc4random \ arc4random_buf \ arc4random_stir \ arc4random_uniform \ ]) ### Configure cryptographic random number support # Check whether OpenSSL seeds itself if test "x$openssl" = "xyes" ; then AC_MSG_CHECKING([whether OpenSSL's PRNG is internally seeded]) AC_RUN_IFELSE( [AC_LANG_PROGRAM([[ #include #include #include ]], [[ exit(RAND_status() == 1 ? 0 : 1); ]])], [ OPENSSL_SEEDS_ITSELF=yes AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) ], [ AC_MSG_WARN([cross compiling: assuming yes]) # This is safe, since we will fatal() at runtime if # OpenSSL is not seeded correctly. OPENSSL_SEEDS_ITSELF=yes ] ) fi # PRNGD TCP socket AC_ARG_WITH([prngd-port], [ --with-prngd-port=PORT read entropy from PRNGD/EGD TCP localhost:PORT], [ case "$withval" in no) withval="" ;; [[0-9]]*) ;; *) AC_MSG_ERROR([You must specify a numeric port number for --with-prngd-port]) ;; esac if test ! -z "$withval" ; then PRNGD_PORT="$withval" AC_DEFINE_UNQUOTED([PRNGD_PORT], [$PRNGD_PORT], [Port number of PRNGD/EGD random number socket]) fi ] ) # PRNGD Unix domain socket AC_ARG_WITH([prngd-socket], [ --with-prngd-socket=FILE read entropy from PRNGD/EGD socket FILE (default=/var/run/egd-pool)], [ case "$withval" in yes) withval="/var/run/egd-pool" ;; no) withval="" ;; /*) ;; *) AC_MSG_ERROR([You must specify an absolute path to the entropy socket]) ;; esac if test ! -z "$withval" ; then if test ! -z "$PRNGD_PORT" ; then AC_MSG_ERROR([You may not specify both a PRNGD/EGD port and socket]) fi if test ! -r "$withval" ; then AC_MSG_WARN([Entropy socket is not readable]) fi PRNGD_SOCKET="$withval" AC_DEFINE_UNQUOTED([PRNGD_SOCKET], ["$PRNGD_SOCKET"], [Location of PRNGD/EGD random number socket]) fi ], [ # Check for existing socket only if we don't have a random device already if test "x$OPENSSL_SEEDS_ITSELF" != "xyes" ; then AC_MSG_CHECKING([for PRNGD/EGD socket]) # Insert other locations here for sock in /var/run/egd-pool /dev/egd-pool /etc/entropy; do if test -r $sock && $TEST_MINUS_S_SH -c "test -S $sock -o -p $sock" ; then PRNGD_SOCKET="$sock" AC_DEFINE_UNQUOTED([PRNGD_SOCKET], ["$PRNGD_SOCKET"]) break; fi done if test ! -z "$PRNGD_SOCKET" ; then AC_MSG_RESULT([$PRNGD_SOCKET]) else AC_MSG_RESULT([not found]) fi fi ] ) # Which randomness source do we use? if test ! -z "$PRNGD_PORT" ; then RAND_MSG="PRNGd port $PRNGD_PORT" elif test ! -z "$PRNGD_SOCKET" ; then RAND_MSG="PRNGd socket $PRNGD_SOCKET" elif test ! -z "$OPENSSL_SEEDS_ITSELF" ; then AC_DEFINE([OPENSSL_PRNG_ONLY], [1], [Define if you want the OpenSSL internally seeded PRNG only]) RAND_MSG="OpenSSL internal ONLY" elif test "x$openssl" = "xno" ; then AC_MSG_WARN([OpenSSH will use /dev/urandom as a source of random numbers. It will fail if this device is not supported or accessible]) else AC_MSG_ERROR([OpenSSH has no source of random numbers. Please configure OpenSSL with an entropy source or re-run configure using one of the --with-prngd-port or --with-prngd-socket options]) fi LIBS="$nocrypto_saved_LIBS" saved_LIBS="$LIBS" AC_CHECK_LIB([iaf], [ia_openinfo], [ LIBS="$LIBS -liaf" AC_CHECK_FUNCS([set_id], [SSHDLIBS="$SSHDLIBS -liaf" AC_DEFINE([HAVE_LIBIAF], [1], [Define if system has libiaf that supports set_id]) ]) ]) LIBS="$saved_LIBS" # Check for crypt() in libcrypt. If we have it, we only need it for sshd. saved_LIBS="$LIBS" AC_CHECK_LIB([crypt], [crypt], [ LIBS="-lcrypt $LIBS" SSHDLIBS="-lcrypt $SSHDLIBS" ]) AC_CHECK_FUNCS([crypt]) LIBS="$saved_LIBS" # Check for PAM libs PAM_MSG="no" AC_ARG_WITH([pam], [ --with-pam Enable PAM support ], [ if test "x$withval" != "xno" ; then if test "x$ac_cv_header_security_pam_appl_h" != "xyes" && \ test "x$ac_cv_header_pam_pam_appl_h" != "xyes" ; then AC_MSG_ERROR([PAM headers not found]) fi saved_LIBS="$LIBS" AC_CHECK_LIB([dl], [dlopen], , ) AC_CHECK_LIB([pam], [pam_set_item], , [AC_MSG_ERROR([*** libpam missing])]) AC_CHECK_FUNCS([pam_getenvlist]) AC_CHECK_FUNCS([pam_putenv]) LIBS="$saved_LIBS" PAM_MSG="yes" SSHDLIBS="$SSHDLIBS -lpam" AC_DEFINE([USE_PAM], [1], [Define if you want to enable PAM support]) if test $ac_cv_lib_dl_dlopen = yes; then case "$LIBS" in *-ldl*) # libdl already in LIBS ;; *) SSHDLIBS="$SSHDLIBS -ldl" ;; esac fi fi ] ) AC_ARG_WITH([pam-service], [ --with-pam-service=name Specify PAM service name ], [ if test "x$withval" != "xno" && \ test "x$withval" != "xyes" ; then AC_DEFINE_UNQUOTED([SSHD_PAM_SERVICE], ["$withval"], [sshd PAM service name]) fi ] ) # Check for older PAM if test "x$PAM_MSG" = "xyes" ; then # Check PAM strerror arguments (old PAM) AC_MSG_CHECKING([whether pam_strerror takes only one argument]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #if defined(HAVE_SECURITY_PAM_APPL_H) #include #elif defined (HAVE_PAM_PAM_APPL_H) #include #endif ]], [[ (void)pam_strerror((pam_handle_t *)NULL, -1); ]])], [AC_MSG_RESULT([no])], [ AC_DEFINE([HAVE_OLD_PAM], [1], [Define if you have an old version of PAM which takes only one argument to pam_strerror]) AC_MSG_RESULT([yes]) PAM_MSG="yes (old library)" ]) fi case "$host" in *-*-cygwin*) SSH_PRIVSEP_USER=CYGWIN_SSH_PRIVSEP_USER ;; *) SSH_PRIVSEP_USER=sshd ;; esac AC_ARG_WITH([privsep-user], [ --with-privsep-user=user Specify non-privileged user for privilege separation], [ if test -n "$withval" && test "x$withval" != "xno" && \ test "x${withval}" != "xyes"; then SSH_PRIVSEP_USER=$withval fi ] ) if test "x$SSH_PRIVSEP_USER" = "xCYGWIN_SSH_PRIVSEP_USER" ; then AC_DEFINE_UNQUOTED([SSH_PRIVSEP_USER], [CYGWIN_SSH_PRIVSEP_USER], [Cygwin function to fetch non-privileged user for privilege separation]) else AC_DEFINE_UNQUOTED([SSH_PRIVSEP_USER], ["$SSH_PRIVSEP_USER"], [non-privileged user for privilege separation]) fi AC_SUBST([SSH_PRIVSEP_USER]) if test "x$have_linux_no_new_privs" = "x1" ; then AC_CHECK_DECL([SECCOMP_MODE_FILTER], [have_seccomp_filter=1], , [ #include #include ]) fi if test "x$have_seccomp_filter" = "x1" ; then AC_MSG_CHECKING([kernel for seccomp_filter support]) AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include #include #include #include #include #include ]], [[ int i = $seccomp_audit_arch; errno = 0; prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, NULL, 0, 0); exit(errno == EFAULT ? 0 : 1); ]])], [ AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) # Disable seccomp filter as a target have_seccomp_filter=0 ] ) fi AC_CHECK_MEMBERS([struct pollfd.fd], [], [], [[ #include #ifdef HAVE_POLL_H #include #endif #ifdef HAVE_SYS_POLL_H #include #endif ]]) AC_CHECK_TYPES([nfds_t], , , [ #include #ifdef HAVE_POLL_H #include #endif #ifdef HAVE_SYS_POLL_H #include #endif ]) # Decide which sandbox style to use sandbox_arg="" AC_ARG_WITH([sandbox], [ --with-sandbox=style Specify privilege separation sandbox (no, capsicum, darwin, rlimit, seccomp_filter, systrace, pledge)], [ if test "x$withval" = "xyes" ; then sandbox_arg="" else sandbox_arg="$withval" fi ] ) if test "x$sandbox_arg" != "xno"; then # POSIX specifies that poll() "shall fail with EINVAL if the nfds argument # is greater than OPEN_MAX". On some platforms that includes implementions # of select in userspace on top of poll() so check both work with rlimit # NOFILES so check that both work before enabling the rlimit sandbox. AC_MSG_CHECKING([if select and/or poll works with descriptor rlimit]) AC_RUN_IFELSE( [AC_LANG_PROGRAM([[ #include #ifdef HAVE_SYS_TIME_H # include #endif #include #ifdef HAVE_SYS_SELECT_H # include #endif #ifdef HAVE_POLL_H # include #elif HAVE_SYS_POLL_H # include #endif #include #include #include ]],[[ struct rlimit rl_zero; int fd, r; fd_set fds; struct timeval tv; #ifdef HAVE_POLL struct pollfd pfd; #endif fd = open("/dev/null", O_RDONLY); FD_ZERO(&fds); FD_SET(fd, &fds); rl_zero.rlim_cur = rl_zero.rlim_max = 0; setrlimit(RLIMIT_FSIZE, &rl_zero); setrlimit(RLIMIT_NOFILE, &rl_zero); tv.tv_sec = 1; tv.tv_usec = 0; r = select(fd+1, &fds, NULL, NULL, &tv); if (r == -1) exit(1); #ifdef HAVE_POLL pfd.fd = fd; pfd.events = POLLIN; r = poll(&pfd, 1, 1); if (r == -1) exit(2); #endif exit(0); ]])], [AC_MSG_RESULT([yes]) select_works_with_rlimit=yes], [AC_MSG_RESULT([no]) select_works_with_rlimit=no], [AC_MSG_WARN([cross compiling: assuming no]) select_works_with_rlimit=no] ) AC_MSG_CHECKING([if setrlimit(RLIMIT_NOFILE,{0,0}) works]) AC_RUN_IFELSE( [AC_LANG_PROGRAM([[ #include #ifdef HAVE_SYS_TIME_H # include #endif #include #include #include ]],[[ struct rlimit rl_zero; int r; rl_zero.rlim_cur = rl_zero.rlim_max = 0; r = setrlimit(RLIMIT_NOFILE, &rl_zero); exit (r == -1 ? 1 : 0); ]])], [AC_MSG_RESULT([yes]) rlimit_nofile_zero_works=yes], [AC_MSG_RESULT([no]) rlimit_nofile_zero_works=no], [AC_MSG_WARN([cross compiling: assuming yes]) rlimit_nofile_zero_works=yes] ) AC_MSG_CHECKING([if setrlimit RLIMIT_FSIZE works]) AC_RUN_IFELSE( [AC_LANG_PROGRAM([[ #include #include #include ]],[[ struct rlimit rl_zero; rl_zero.rlim_cur = rl_zero.rlim_max = 0; exit(setrlimit(RLIMIT_FSIZE, &rl_zero) != 0); ]])], [AC_MSG_RESULT([yes])], [AC_MSG_RESULT([no]) AC_DEFINE(SANDBOX_SKIP_RLIMIT_FSIZE, 1, [setrlimit RLIMIT_FSIZE works])], [AC_MSG_WARN([cross compiling: assuming yes])] ) fi if test "x$sandbox_arg" = "xpledge" || \ ( test -z "$sandbox_arg" && test "x$ac_cv_func_pledge" = "xyes" ) ; then test "x$ac_cv_func_pledge" != "xyes" && \ AC_MSG_ERROR([pledge sandbox requires pledge(2) support]) SANDBOX_STYLE="pledge" AC_DEFINE([SANDBOX_PLEDGE], [1], [Sandbox using pledge(2)]) elif test "x$sandbox_arg" = "xsystrace" || \ ( test -z "$sandbox_arg" && test "x$have_systr_policy_kill" = "x1" ) ; then test "x$have_systr_policy_kill" != "x1" && \ AC_MSG_ERROR([systrace sandbox requires systrace headers and SYSTR_POLICY_KILL support]) SANDBOX_STYLE="systrace" AC_DEFINE([SANDBOX_SYSTRACE], [1], [Sandbox using systrace(4)]) elif test "x$sandbox_arg" = "xdarwin" || \ ( test -z "$sandbox_arg" && test "x$ac_cv_func_sandbox_init" = "xyes" && \ test "x$ac_cv_header_sandbox_h" = "xyes") ; then test "x$ac_cv_func_sandbox_init" != "xyes" -o \ "x$ac_cv_header_sandbox_h" != "xyes" && \ AC_MSG_ERROR([Darwin seatbelt sandbox requires sandbox.h and sandbox_init function]) SANDBOX_STYLE="darwin" AC_DEFINE([SANDBOX_DARWIN], [1], [Sandbox using Darwin sandbox_init(3)]) elif test "x$sandbox_arg" = "xseccomp_filter" || \ ( test -z "$sandbox_arg" && \ test "x$have_seccomp_filter" = "x1" && \ test "x$ac_cv_header_elf_h" = "xyes" && \ test "x$ac_cv_header_linux_audit_h" = "xyes" && \ test "x$ac_cv_header_linux_filter_h" = "xyes" && \ test "x$seccomp_audit_arch" != "x" && \ test "x$have_linux_no_new_privs" = "x1" && \ test "x$ac_cv_func_prctl" = "xyes" ) ; then test "x$seccomp_audit_arch" = "x" && \ AC_MSG_ERROR([seccomp_filter sandbox not supported on $host]) test "x$have_linux_no_new_privs" != "x1" && \ AC_MSG_ERROR([seccomp_filter sandbox requires PR_SET_NO_NEW_PRIVS]) test "x$have_seccomp_filter" != "x1" && \ AC_MSG_ERROR([seccomp_filter sandbox requires seccomp headers]) test "x$ac_cv_func_prctl" != "xyes" && \ AC_MSG_ERROR([seccomp_filter sandbox requires prctl function]) SANDBOX_STYLE="seccomp_filter" AC_DEFINE([SANDBOX_SECCOMP_FILTER], [1], [Sandbox using seccomp filter]) elif test "x$sandbox_arg" = "xcapsicum" || \ ( test -z "$sandbox_arg" && \ test "x$disable_capsicum" != "xyes" && \ test "x$ac_cv_header_sys_capsicum_h" = "xyes" && \ test "x$ac_cv_func_cap_rights_limit" = "xyes") ; then test "x$ac_cv_header_sys_capsicum_h" != "xyes" && \ AC_MSG_ERROR([capsicum sandbox requires sys/capsicum.h header]) test "x$ac_cv_func_cap_rights_limit" != "xyes" && \ AC_MSG_ERROR([capsicum sandbox requires cap_rights_limit function]) SANDBOX_STYLE="capsicum" AC_DEFINE([SANDBOX_CAPSICUM], [1], [Sandbox using capsicum]) elif test "x$sandbox_arg" = "xrlimit" || \ ( test -z "$sandbox_arg" && test "x$ac_cv_func_setrlimit" = "xyes" && \ test "x$select_works_with_rlimit" = "xyes" && \ test "x$rlimit_nofile_zero_works" = "xyes" ) ; then test "x$ac_cv_func_setrlimit" != "xyes" && \ AC_MSG_ERROR([rlimit sandbox requires setrlimit function]) test "x$select_works_with_rlimit" != "xyes" && \ AC_MSG_ERROR([rlimit sandbox requires select to work with rlimit]) SANDBOX_STYLE="rlimit" AC_DEFINE([SANDBOX_RLIMIT], [1], [Sandbox using setrlimit(2)]) elif test "x$sandbox_arg" = "xsolaris" || \ ( test -z "$sandbox_arg" && test "x$SOLARIS_PRIVS" = "xyes" ) ; then SANDBOX_STYLE="solaris" AC_DEFINE([SANDBOX_SOLARIS], [1], [Sandbox using Solaris/Illumos privileges]) elif test -z "$sandbox_arg" || test "x$sandbox_arg" = "xno" || \ test "x$sandbox_arg" = "xnone" || test "x$sandbox_arg" = "xnull" ; then SANDBOX_STYLE="none" AC_DEFINE([SANDBOX_NULL], [1], [no privsep sandboxing]) else AC_MSG_ERROR([unsupported --with-sandbox]) fi # Cheap hack to ensure NEWS-OS libraries are arranged right. if test ! -z "$SONY" ; then LIBS="$LIBS -liberty"; fi # Check for long long datatypes AC_CHECK_TYPES([long long, unsigned long long, long double]) # Check datatype sizes AC_CHECK_SIZEOF([short int]) AC_CHECK_SIZEOF([int]) AC_CHECK_SIZEOF([long int]) AC_CHECK_SIZEOF([long long int]) AC_CHECK_SIZEOF([time_t], [], [[ #include #ifdef HAVE_SYS_TIME_H # include #endif #ifdef HAVE_TIME_H # include #endif ]] ) # Sanity check long long for some platforms (AIX) if test "x$ac_cv_sizeof_long_long_int" = "x4" ; then ac_cv_sizeof_long_long_int=0 fi # compute LLONG_MIN and LLONG_MAX if we don't know them. if test -z "$have_llong_max" && test -z "$have_long_long_max"; then AC_MSG_CHECKING([for max value of long long]) AC_RUN_IFELSE( [AC_LANG_PROGRAM([[ #include #include /* Why is this so damn hard? */ #ifdef __GNUC__ # undef __GNUC__ #endif #define __USE_ISOC99 #include #define DATA "conftest.llminmax" #define my_abs(a) ((a) < 0 ? ((a) * -1) : (a)) /* * printf in libc on some platforms (eg old Tru64) does not understand %lld so * we do this the hard way. */ static int fprint_ll(FILE *f, long long n) { unsigned int i; int l[sizeof(long long) * 8]; if (n < 0) if (fprintf(f, "-") < 0) return -1; for (i = 0; n != 0; i++) { l[i] = my_abs(n % 10); n /= 10; } do { if (fprintf(f, "%d", l[--i]) < 0) return -1; } while (i != 0); if (fprintf(f, " ") < 0) return -1; return 0; } ]], [[ FILE *f; long long i, llmin, llmax = 0; if((f = fopen(DATA,"w")) == NULL) exit(1); #if defined(LLONG_MIN) && defined(LLONG_MAX) fprintf(stderr, "Using system header for LLONG_MIN and LLONG_MAX\n"); llmin = LLONG_MIN; llmax = LLONG_MAX; #else fprintf(stderr, "Calculating LLONG_MIN and LLONG_MAX\n"); /* This will work on one's complement and two's complement */ for (i = 1; i > llmax; i <<= 1, i++) llmax = i; llmin = llmax + 1LL; /* wrap */ #endif /* Sanity check */ if (llmin + 1 < llmin || llmin - 1 < llmin || llmax + 1 > llmax || llmax - 1 > llmax || llmin == llmax || llmin == 0 || llmax == 0 || llmax < LONG_MAX || llmin > LONG_MIN) { fprintf(f, "unknown unknown\n"); exit(2); } if (fprint_ll(f, llmin) < 0) exit(3); if (fprint_ll(f, llmax) < 0) exit(4); if (fclose(f) < 0) exit(5); exit(0); ]])], [ llong_min=`$AWK '{print $1}' conftest.llminmax` llong_max=`$AWK '{print $2}' conftest.llminmax` AC_MSG_RESULT([$llong_max]) AC_DEFINE_UNQUOTED([LLONG_MAX], [${llong_max}LL], [max value of long long calculated by configure]) AC_MSG_CHECKING([for min value of long long]) AC_MSG_RESULT([$llong_min]) AC_DEFINE_UNQUOTED([LLONG_MIN], [${llong_min}LL], [min value of long long calculated by configure]) ], [ AC_MSG_RESULT([not found]) ], [ AC_MSG_WARN([cross compiling: not checking]) ] ) fi AC_CHECK_DECLS([UINT32_MAX], , , [[ #ifdef HAVE_SYS_LIMITS_H # include #endif #ifdef HAVE_LIMITS_H # include #endif #ifdef HAVE_STDINT_H # include #endif ]]) # More checks for data types AC_CACHE_CHECK([for u_int type], ac_cv_have_u_int, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ u_int a; a = 1;]])], [ ac_cv_have_u_int="yes" ], [ ac_cv_have_u_int="no" ]) ]) if test "x$ac_cv_have_u_int" = "xyes" ; then AC_DEFINE([HAVE_U_INT], [1], [define if you have u_int data type]) have_u_int=1 fi AC_CACHE_CHECK([for intXX_t types], ac_cv_have_intxx_t, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ int8_t a; int16_t b; int32_t c; a = b = c = 1;]])], [ ac_cv_have_intxx_t="yes" ], [ ac_cv_have_intxx_t="no" ]) ]) if test "x$ac_cv_have_intxx_t" = "xyes" ; then AC_DEFINE([HAVE_INTXX_T], [1], [define if you have intxx_t data type]) have_intxx_t=1 fi if (test -z "$have_intxx_t" && \ test "x$ac_cv_header_stdint_h" = "xyes") then AC_MSG_CHECKING([for intXX_t types in stdint.h]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ int8_t a; int16_t b; int32_t c; a = b = c = 1;]])], [ AC_DEFINE([HAVE_INTXX_T]) AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) ]) fi AC_CACHE_CHECK([for int64_t type], ac_cv_have_int64_t, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #ifdef HAVE_STDINT_H # include #endif #include #ifdef HAVE_SYS_BITYPES_H # include #endif ]], [[ int64_t a; a = 1; ]])], [ ac_cv_have_int64_t="yes" ], [ ac_cv_have_int64_t="no" ]) ]) if test "x$ac_cv_have_int64_t" = "xyes" ; then AC_DEFINE([HAVE_INT64_T], [1], [define if you have int64_t data type]) fi AC_CACHE_CHECK([for u_intXX_t types], ac_cv_have_u_intxx_t, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ u_int8_t a; u_int16_t b; u_int32_t c; a = b = c = 1;]])], [ ac_cv_have_u_intxx_t="yes" ], [ ac_cv_have_u_intxx_t="no" ]) ]) if test "x$ac_cv_have_u_intxx_t" = "xyes" ; then AC_DEFINE([HAVE_U_INTXX_T], [1], [define if you have u_intxx_t data type]) have_u_intxx_t=1 fi if test -z "$have_u_intxx_t" ; then AC_MSG_CHECKING([for u_intXX_t types in sys/socket.h]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ u_int8_t a; u_int16_t b; u_int32_t c; a = b = c = 1;]])], [ AC_DEFINE([HAVE_U_INTXX_T]) AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) ]) fi AC_CACHE_CHECK([for u_int64_t types], ac_cv_have_u_int64_t, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ u_int64_t a; a = 1;]])], [ ac_cv_have_u_int64_t="yes" ], [ ac_cv_have_u_int64_t="no" ]) ]) if test "x$ac_cv_have_u_int64_t" = "xyes" ; then AC_DEFINE([HAVE_U_INT64_T], [1], [define if you have u_int64_t data type]) have_u_int64_t=1 fi if (test -z "$have_u_int64_t" && \ test "x$ac_cv_header_sys_bitypes_h" = "xyes") then AC_MSG_CHECKING([for u_int64_t type in sys/bitypes.h]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ u_int64_t a; a = 1]])], [ AC_DEFINE([HAVE_U_INT64_T]) AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) ]) fi if test -z "$have_u_intxx_t" ; then AC_CACHE_CHECK([for uintXX_t types], ac_cv_have_uintxx_t, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ uint8_t a; uint16_t b; uint32_t c; a = b = c = 1; ]])], [ ac_cv_have_uintxx_t="yes" ], [ ac_cv_have_uintxx_t="no" ]) ]) if test "x$ac_cv_have_uintxx_t" = "xyes" ; then AC_DEFINE([HAVE_UINTXX_T], [1], [define if you have uintxx_t data type]) fi fi if (test -z "$have_uintxx_t" && \ test "x$ac_cv_header_stdint_h" = "xyes") then AC_MSG_CHECKING([for uintXX_t types in stdint.h]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ uint8_t a; uint16_t b; uint32_t c; a = b = c = 1;]])], [ AC_DEFINE([HAVE_UINTXX_T]) AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) ]) fi if (test -z "$have_uintxx_t" && \ test "x$ac_cv_header_inttypes_h" = "xyes") then AC_MSG_CHECKING([for uintXX_t types in inttypes.h]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ uint8_t a; uint16_t b; uint32_t c; a = b = c = 1;]])], [ AC_DEFINE([HAVE_UINTXX_T]) AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) ]) fi if (test -z "$have_u_intxx_t" || test -z "$have_intxx_t" && \ test "x$ac_cv_header_sys_bitypes_h" = "xyes") then AC_MSG_CHECKING([for intXX_t and u_intXX_t types in sys/bitypes.h]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ int8_t a; int16_t b; int32_t c; u_int8_t e; u_int16_t f; u_int32_t g; a = b = c = e = f = g = 1; ]])], [ AC_DEFINE([HAVE_U_INTXX_T]) AC_DEFINE([HAVE_INTXX_T]) AC_MSG_RESULT([yes]) ], [AC_MSG_RESULT([no]) ]) fi AC_CACHE_CHECK([for u_char], ac_cv_have_u_char, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ u_char foo; foo = 125; ]])], [ ac_cv_have_u_char="yes" ], [ ac_cv_have_u_char="no" ]) ]) if test "x$ac_cv_have_u_char" = "xyes" ; then AC_DEFINE([HAVE_U_CHAR], [1], [define if you have u_char data type]) fi AC_CHECK_TYPES([intmax_t, uintmax_t], , , [ #include #ifdef HAVE_STDINT_H # include #endif ]) TYPE_SOCKLEN_T AC_CHECK_TYPES([sig_atomic_t, sighandler_t], , , [#include ]) AC_CHECK_TYPES([fsblkcnt_t, fsfilcnt_t], , , [ #include #ifdef HAVE_SYS_BITYPES_H #include #endif #ifdef HAVE_SYS_STATFS_H #include #endif #ifdef HAVE_SYS_STATVFS_H #include #endif ]) AC_CHECK_MEMBERS([struct statfs.f_files, struct statfs.f_flags], [], [], [[ #include #include #ifdef HAVE_SYS_BITYPES_H #include #endif #ifdef HAVE_SYS_STATFS_H #include #endif #ifdef HAVE_SYS_STATVFS_H #include #endif #ifdef HAVE_SYS_VFS_H #include #endif #ifdef HAVE_SYS_MOUNT_H #include #endif ]]) AC_CHECK_TYPES([in_addr_t, in_port_t], , , [#include #include ]) AC_CACHE_CHECK([for size_t], ac_cv_have_size_t, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ size_t foo; foo = 1235; ]])], [ ac_cv_have_size_t="yes" ], [ ac_cv_have_size_t="no" ]) ]) if test "x$ac_cv_have_size_t" = "xyes" ; then AC_DEFINE([HAVE_SIZE_T], [1], [define if you have size_t data type]) fi AC_CACHE_CHECK([for ssize_t], ac_cv_have_ssize_t, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ ssize_t foo; foo = 1235; ]])], [ ac_cv_have_ssize_t="yes" ], [ ac_cv_have_ssize_t="no" ]) ]) if test "x$ac_cv_have_ssize_t" = "xyes" ; then AC_DEFINE([HAVE_SSIZE_T], [1], [define if you have ssize_t data type]) fi AC_CACHE_CHECK([for clock_t], ac_cv_have_clock_t, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ clock_t foo; foo = 1235; ]])], [ ac_cv_have_clock_t="yes" ], [ ac_cv_have_clock_t="no" ]) ]) if test "x$ac_cv_have_clock_t" = "xyes" ; then AC_DEFINE([HAVE_CLOCK_T], [1], [define if you have clock_t data type]) fi AC_CACHE_CHECK([for sa_family_t], ac_cv_have_sa_family_t, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include ]], [[ sa_family_t foo; foo = 1235; ]])], [ ac_cv_have_sa_family_t="yes" ], [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include #include ]], [[ sa_family_t foo; foo = 1235; ]])], [ ac_cv_have_sa_family_t="yes" ], [ ac_cv_have_sa_family_t="no" ] ) ]) ]) if test "x$ac_cv_have_sa_family_t" = "xyes" ; then AC_DEFINE([HAVE_SA_FAMILY_T], [1], [define if you have sa_family_t data type]) fi AC_CACHE_CHECK([for pid_t], ac_cv_have_pid_t, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ pid_t foo; foo = 1235; ]])], [ ac_cv_have_pid_t="yes" ], [ ac_cv_have_pid_t="no" ]) ]) if test "x$ac_cv_have_pid_t" = "xyes" ; then AC_DEFINE([HAVE_PID_T], [1], [define if you have pid_t data type]) fi AC_CACHE_CHECK([for mode_t], ac_cv_have_mode_t, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ mode_t foo; foo = 1235; ]])], [ ac_cv_have_mode_t="yes" ], [ ac_cv_have_mode_t="no" ]) ]) if test "x$ac_cv_have_mode_t" = "xyes" ; then AC_DEFINE([HAVE_MODE_T], [1], [define if you have mode_t data type]) fi AC_CACHE_CHECK([for struct sockaddr_storage], ac_cv_have_struct_sockaddr_storage, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include ]], [[ struct sockaddr_storage s; ]])], [ ac_cv_have_struct_sockaddr_storage="yes" ], [ ac_cv_have_struct_sockaddr_storage="no" ]) ]) if test "x$ac_cv_have_struct_sockaddr_storage" = "xyes" ; then AC_DEFINE([HAVE_STRUCT_SOCKADDR_STORAGE], [1], [define if you have struct sockaddr_storage data type]) fi AC_CACHE_CHECK([for struct sockaddr_in6], ac_cv_have_struct_sockaddr_in6, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include ]], [[ struct sockaddr_in6 s; s.sin6_family = 0; ]])], [ ac_cv_have_struct_sockaddr_in6="yes" ], [ ac_cv_have_struct_sockaddr_in6="no" ]) ]) if test "x$ac_cv_have_struct_sockaddr_in6" = "xyes" ; then AC_DEFINE([HAVE_STRUCT_SOCKADDR_IN6], [1], [define if you have struct sockaddr_in6 data type]) fi AC_CACHE_CHECK([for struct in6_addr], ac_cv_have_struct_in6_addr, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include ]], [[ struct in6_addr s; s.s6_addr[0] = 0; ]])], [ ac_cv_have_struct_in6_addr="yes" ], [ ac_cv_have_struct_in6_addr="no" ]) ]) if test "x$ac_cv_have_struct_in6_addr" = "xyes" ; then AC_DEFINE([HAVE_STRUCT_IN6_ADDR], [1], [define if you have struct in6_addr data type]) dnl Now check for sin6_scope_id AC_CHECK_MEMBERS([struct sockaddr_in6.sin6_scope_id], , , [ #ifdef HAVE_SYS_TYPES_H #include #endif #include ]) fi AC_CACHE_CHECK([for struct addrinfo], ac_cv_have_struct_addrinfo, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include #include ]], [[ struct addrinfo s; s.ai_flags = AI_PASSIVE; ]])], [ ac_cv_have_struct_addrinfo="yes" ], [ ac_cv_have_struct_addrinfo="no" ]) ]) if test "x$ac_cv_have_struct_addrinfo" = "xyes" ; then AC_DEFINE([HAVE_STRUCT_ADDRINFO], [1], [define if you have struct addrinfo data type]) fi AC_CACHE_CHECK([for struct timeval], ac_cv_have_struct_timeval, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ struct timeval tv; tv.tv_sec = 1;]])], [ ac_cv_have_struct_timeval="yes" ], [ ac_cv_have_struct_timeval="no" ]) ]) if test "x$ac_cv_have_struct_timeval" = "xyes" ; then AC_DEFINE([HAVE_STRUCT_TIMEVAL], [1], [define if you have struct timeval]) have_struct_timeval=1 fi AC_CACHE_CHECK([for struct timespec], ac_cv_have_struct_timespec, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #ifdef HAVE_SYS_TIME_H # include #endif #ifdef HAVE_TIME_H # include #endif ]], [[ struct timespec ts; ts.tv_sec = 1;]])], [ ac_cv_have_struct_timespec="yes" ], [ ac_cv_have_struct_timespec="no" ]) ]) if test "x$ac_cv_have_struct_timespec" = "xyes" ; then AC_DEFINE([HAVE_STRUCT_TIMESPEC], [1], [define if you have struct timespec]) have_struct_timespec=1 fi # We need int64_t or else certain parts of the compile will fail. if test "x$ac_cv_have_int64_t" = "xno" && \ test "x$ac_cv_sizeof_long_int" != "x8" && \ test "x$ac_cv_sizeof_long_long_int" = "x0" ; then echo "OpenSSH requires int64_t support. Contact your vendor or install" echo "an alternative compiler (I.E., GCC) before continuing." echo "" exit 1; else dnl test snprintf (broken on SCO w/gcc) AC_RUN_IFELSE( [AC_LANG_SOURCE([[ #include #include #include #ifdef HAVE_SNPRINTF int main(void) { char buf[50]; char expected_out[50]; int mazsize = 50 ; #if (SIZEOF_LONG_INT == 8) long int num = 0x7fffffffffffffff; #else long long num = 0x7fffffffffffffffll; #endif strcpy(expected_out, "9223372036854775807"); snprintf(buf, mazsize, "%lld", num); if(strcmp(buf, expected_out) != 0) exit(1); exit(0); } #else int main(void) { exit(0); } #endif ]])], [ true ], [ AC_DEFINE([BROKEN_SNPRINTF]) ], AC_MSG_WARN([cross compiling: Assuming working snprintf()]) ) fi dnl Checks for structure members OSSH_CHECK_HEADER_FOR_FIELD([ut_host], [utmp.h], [HAVE_HOST_IN_UTMP]) OSSH_CHECK_HEADER_FOR_FIELD([ut_host], [utmpx.h], [HAVE_HOST_IN_UTMPX]) OSSH_CHECK_HEADER_FOR_FIELD([syslen], [utmpx.h], [HAVE_SYSLEN_IN_UTMPX]) OSSH_CHECK_HEADER_FOR_FIELD([ut_pid], [utmp.h], [HAVE_PID_IN_UTMP]) OSSH_CHECK_HEADER_FOR_FIELD([ut_type], [utmp.h], [HAVE_TYPE_IN_UTMP]) OSSH_CHECK_HEADER_FOR_FIELD([ut_type], [utmpx.h], [HAVE_TYPE_IN_UTMPX]) OSSH_CHECK_HEADER_FOR_FIELD([ut_tv], [utmp.h], [HAVE_TV_IN_UTMP]) OSSH_CHECK_HEADER_FOR_FIELD([ut_id], [utmp.h], [HAVE_ID_IN_UTMP]) OSSH_CHECK_HEADER_FOR_FIELD([ut_id], [utmpx.h], [HAVE_ID_IN_UTMPX]) OSSH_CHECK_HEADER_FOR_FIELD([ut_addr], [utmp.h], [HAVE_ADDR_IN_UTMP]) OSSH_CHECK_HEADER_FOR_FIELD([ut_addr], [utmpx.h], [HAVE_ADDR_IN_UTMPX]) OSSH_CHECK_HEADER_FOR_FIELD([ut_addr_v6], [utmp.h], [HAVE_ADDR_V6_IN_UTMP]) OSSH_CHECK_HEADER_FOR_FIELD([ut_addr_v6], [utmpx.h], [HAVE_ADDR_V6_IN_UTMPX]) OSSH_CHECK_HEADER_FOR_FIELD([ut_exit], [utmp.h], [HAVE_EXIT_IN_UTMP]) OSSH_CHECK_HEADER_FOR_FIELD([ut_time], [utmp.h], [HAVE_TIME_IN_UTMP]) OSSH_CHECK_HEADER_FOR_FIELD([ut_time], [utmpx.h], [HAVE_TIME_IN_UTMPX]) OSSH_CHECK_HEADER_FOR_FIELD([ut_tv], [utmpx.h], [HAVE_TV_IN_UTMPX]) OSSH_CHECK_HEADER_FOR_FIELD([ut_ss], [utmpx.h], [HAVE_SS_IN_UTMPX]) AC_CHECK_MEMBERS([struct stat.st_blksize]) AC_CHECK_MEMBERS([struct stat.st_mtim]) AC_CHECK_MEMBERS([struct stat.st_mtime]) AC_CHECK_MEMBERS([struct passwd.pw_gecos, struct passwd.pw_class, struct passwd.pw_change, struct passwd.pw_expire], [], [], [[ #include #include ]]) AC_CHECK_MEMBER([struct __res_state.retrans], [], [AC_DEFINE([__res_state], [state], [Define if we don't have struct __res_state in resolv.h])], [[ #include #if HAVE_SYS_TYPES_H # include #endif #include #include #include ]]) AC_CHECK_MEMBER([struct sockaddr_in.sin_len], [AC_DEFINE([SOCK_HAS_LEN], [1], [sockaddr_in has sin_len])], [], [AC_LANG_SOURCE([[ #include #include #include ]])] ) AC_CACHE_CHECK([for ss_family field in struct sockaddr_storage], ac_cv_have_ss_family_in_struct_ss, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include ]], [[ struct sockaddr_storage s; s.ss_family = 1; ]])], [ ac_cv_have_ss_family_in_struct_ss="yes" ], [ ac_cv_have_ss_family_in_struct_ss="no" ]) ]) if test "x$ac_cv_have_ss_family_in_struct_ss" = "xyes" ; then AC_DEFINE([HAVE_SS_FAMILY_IN_SS], [1], [Fields in struct sockaddr_storage]) fi AC_CACHE_CHECK([for __ss_family field in struct sockaddr_storage], ac_cv_have___ss_family_in_struct_ss, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include ]], [[ struct sockaddr_storage s; s.__ss_family = 1; ]])], [ ac_cv_have___ss_family_in_struct_ss="yes" ], [ ac_cv_have___ss_family_in_struct_ss="no" ]) ]) if test "x$ac_cv_have___ss_family_in_struct_ss" = "xyes" ; then AC_DEFINE([HAVE___SS_FAMILY_IN_SS], [1], [Fields in struct sockaddr_storage]) fi dnl make sure we're using the real structure members and not defines AC_CACHE_CHECK([for msg_accrights field in struct msghdr], ac_cv_have_accrights_in_msghdr, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include #include #include ]], [[ #ifdef msg_accrights #error "msg_accrights is a macro" exit(1); #endif struct msghdr m; m.msg_accrights = 0; exit(0); ]])], [ ac_cv_have_accrights_in_msghdr="yes" ], [ ac_cv_have_accrights_in_msghdr="no" ] ) ]) if test "x$ac_cv_have_accrights_in_msghdr" = "xyes" ; then AC_DEFINE([HAVE_ACCRIGHTS_IN_MSGHDR], [1], [Define if your system uses access rights style file descriptor passing]) fi AC_MSG_CHECKING([if struct statvfs.f_fsid is integral type]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include #ifdef HAVE_SYS_TIME_H # include #endif #ifdef HAVE_SYS_MOUNT_H #include #endif #ifdef HAVE_SYS_STATVFS_H #include #endif ]], [[ struct statvfs s; s.f_fsid = 0; ]])], [ AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) AC_MSG_CHECKING([if fsid_t has member val]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include ]], [[ fsid_t t; t.val[0] = 0; ]])], [ AC_MSG_RESULT([yes]) AC_DEFINE([FSID_HAS_VAL], [1], [fsid_t has member val]) ], [ AC_MSG_RESULT([no]) ]) AC_MSG_CHECKING([if f_fsid has member __val]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include ]], [[ fsid_t t; t.__val[0] = 0; ]])], [ AC_MSG_RESULT([yes]) AC_DEFINE([FSID_HAS___VAL], [1], [fsid_t has member __val]) ], [ AC_MSG_RESULT([no]) ]) ]) AC_CACHE_CHECK([for msg_control field in struct msghdr], ac_cv_have_control_in_msghdr, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include #include #include ]], [[ #ifdef msg_control #error "msg_control is a macro" exit(1); #endif struct msghdr m; m.msg_control = 0; exit(0); ]])], [ ac_cv_have_control_in_msghdr="yes" ], [ ac_cv_have_control_in_msghdr="no" ] ) ]) if test "x$ac_cv_have_control_in_msghdr" = "xyes" ; then AC_DEFINE([HAVE_CONTROL_IN_MSGHDR], [1], [Define if your system uses ancillary data style file descriptor passing]) fi AC_CACHE_CHECK([if libc defines __progname], ac_cv_libc_defines___progname, [ AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ extern char *__progname; printf("%s", __progname); ]])], [ ac_cv_libc_defines___progname="yes" ], [ ac_cv_libc_defines___progname="no" ]) ]) if test "x$ac_cv_libc_defines___progname" = "xyes" ; then AC_DEFINE([HAVE___PROGNAME], [1], [Define if libc defines __progname]) fi AC_CACHE_CHECK([whether $CC implements __FUNCTION__], ac_cv_cc_implements___FUNCTION__, [ AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ printf("%s", __FUNCTION__); ]])], [ ac_cv_cc_implements___FUNCTION__="yes" ], [ ac_cv_cc_implements___FUNCTION__="no" ]) ]) if test "x$ac_cv_cc_implements___FUNCTION__" = "xyes" ; then AC_DEFINE([HAVE___FUNCTION__], [1], [Define if compiler implements __FUNCTION__]) fi AC_CACHE_CHECK([whether $CC implements __func__], ac_cv_cc_implements___func__, [ AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ printf("%s", __func__); ]])], [ ac_cv_cc_implements___func__="yes" ], [ ac_cv_cc_implements___func__="no" ]) ]) if test "x$ac_cv_cc_implements___func__" = "xyes" ; then AC_DEFINE([HAVE___func__], [1], [Define if compiler implements __func__]) fi AC_CACHE_CHECK([whether va_copy exists], ac_cv_have_va_copy, [ AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include va_list x,y; ]], [[ va_copy(x,y); ]])], [ ac_cv_have_va_copy="yes" ], [ ac_cv_have_va_copy="no" ]) ]) if test "x$ac_cv_have_va_copy" = "xyes" ; then AC_DEFINE([HAVE_VA_COPY], [1], [Define if va_copy exists]) fi AC_CACHE_CHECK([whether __va_copy exists], ac_cv_have___va_copy, [ AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include va_list x,y; ]], [[ __va_copy(x,y); ]])], [ ac_cv_have___va_copy="yes" ], [ ac_cv_have___va_copy="no" ]) ]) if test "x$ac_cv_have___va_copy" = "xyes" ; then AC_DEFINE([HAVE___VA_COPY], [1], [Define if __va_copy exists]) fi AC_CACHE_CHECK([whether getopt has optreset support], ac_cv_have_getopt_optreset, [ AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ extern int optreset; optreset = 0; ]])], [ ac_cv_have_getopt_optreset="yes" ], [ ac_cv_have_getopt_optreset="no" ]) ]) if test "x$ac_cv_have_getopt_optreset" = "xyes" ; then AC_DEFINE([HAVE_GETOPT_OPTRESET], [1], [Define if your getopt(3) defines and uses optreset]) fi AC_CACHE_CHECK([if libc defines sys_errlist], ac_cv_libc_defines_sys_errlist, [ AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ extern const char *const sys_errlist[]; printf("%s", sys_errlist[0]);]])], [ ac_cv_libc_defines_sys_errlist="yes" ], [ ac_cv_libc_defines_sys_errlist="no" ]) ]) if test "x$ac_cv_libc_defines_sys_errlist" = "xyes" ; then AC_DEFINE([HAVE_SYS_ERRLIST], [1], [Define if your system defines sys_errlist[]]) fi AC_CACHE_CHECK([if libc defines sys_nerr], ac_cv_libc_defines_sys_nerr, [ AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ extern int sys_nerr; printf("%i", sys_nerr);]])], [ ac_cv_libc_defines_sys_nerr="yes" ], [ ac_cv_libc_defines_sys_nerr="no" ]) ]) if test "x$ac_cv_libc_defines_sys_nerr" = "xyes" ; then AC_DEFINE([HAVE_SYS_NERR], [1], [Define if your system defines sys_nerr]) fi # Check libraries needed by DNS fingerprint support AC_SEARCH_LIBS([getrrsetbyname], [resolv], [AC_DEFINE([HAVE_GETRRSETBYNAME], [1], [Define if getrrsetbyname() exists])], [ # Needed by our getrrsetbyname() AC_SEARCH_LIBS([res_query], [resolv]) AC_SEARCH_LIBS([dn_expand], [resolv]) AC_MSG_CHECKING([if res_query will link]) AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include #include #include #include #include ]], [[ res_query (0, 0, 0, 0, 0); ]])], AC_MSG_RESULT([yes]), [AC_MSG_RESULT([no]) saved_LIBS="$LIBS" LIBS="$LIBS -lresolv" AC_MSG_CHECKING([for res_query in -lresolv]) AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include #include #include #include #include ]], [[ res_query (0, 0, 0, 0, 0); ]])], [AC_MSG_RESULT([yes])], [LIBS="$saved_LIBS" AC_MSG_RESULT([no])]) ]) AC_CHECK_FUNCS([_getshort _getlong]) AC_CHECK_DECLS([_getshort, _getlong], , , [#include #include ]) AC_CHECK_MEMBER([HEADER.ad], [AC_DEFINE([HAVE_HEADER_AD], [1], [Define if HEADER.ad exists in arpa/nameser.h])], , [#include ]) ]) AC_MSG_CHECKING([if struct __res_state _res is an extern]) AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include #if HAVE_SYS_TYPES_H # include #endif #include #include #include extern struct __res_state _res; ]], [[ struct __res_state *volatile p = &_res; /* force resolution of _res */ return 0; ]],)], [AC_MSG_RESULT([yes]) AC_DEFINE([HAVE__RES_EXTERN], [1], [Define if you have struct __res_state _res as an extern]) ], [ AC_MSG_RESULT([no]) ] ) # Check whether user wants SELinux support SELINUX_MSG="no" LIBSELINUX="" AC_ARG_WITH([selinux], [ --with-selinux Enable SELinux support], [ if test "x$withval" != "xno" ; then save_LIBS="$LIBS" AC_DEFINE([WITH_SELINUX], [1], [Define if you want SELinux support.]) SELINUX_MSG="yes" AC_CHECK_HEADER([selinux/selinux.h], , AC_MSG_ERROR([SELinux support requires selinux.h header])) AC_CHECK_LIB([selinux], [setexeccon], [ LIBSELINUX="-lselinux" LIBS="$LIBS -lselinux" ], AC_MSG_ERROR([SELinux support requires libselinux library])) AC_CHECK_FUNCS([getseuserbyname get_default_context_with_level]) LIBS="$save_LIBS $LIBSELINUX" fi ] ) AC_SUBST([SSHDLIBS]) # Check whether user wants Kerberos 5 support KRB5_MSG="no" AC_ARG_WITH([kerberos5], [ --with-kerberos5=PATH Enable Kerberos 5 support], [ if test "x$withval" != "xno" ; then if test "x$withval" = "xyes" ; then KRB5ROOT="/usr/local" else KRB5ROOT=${withval} fi AC_DEFINE([KRB5], [1], [Define if you want Kerberos 5 support]) KRB5_MSG="yes" use_pkgconfig_for_krb5= if test "x$PKGCONFIG" != "xno"; then AC_MSG_CHECKING([if $PKGCONFIG knows about kerberos5]) if "$PKGCONFIG" krb5; then AC_MSG_RESULT([yes]) use_pkgconfig_for_krb5=yes else AC_MSG_RESULT([no]) fi fi if test "x$use_pkgconfig_for_krb5" = "xyes"; then K5CFLAGS=`$PKGCONFIG --cflags krb5` K5LIBS=`$PKGCONFIG --libs krb5` CPPFLAGS="$CPPFLAGS $K5CFLAGS" AC_MSG_CHECKING([for gssapi support]) if "$PKGCONFIG" krb5-gssapi; then AC_MSG_RESULT([yes]) AC_DEFINE([GSSAPI], [1], [Define this if you want GSSAPI support in the version 2 protocol]) GSSCFLAGS="`$PKGCONFIG --cflags krb5-gssapi`" GSSLIBS="`$PKGCONFIG --libs krb5-gssapi`" CPPFLAGS="$CPPFLAGS $GSSCFLAGS" else AC_MSG_RESULT([no]) fi AC_MSG_CHECKING([whether we are using Heimdal]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ char *tmp = heimdal_version; ]])], [ AC_MSG_RESULT([yes]) AC_DEFINE([HEIMDAL], [1], [Define this if you are using the Heimdal version of Kerberos V5]) ], [AC_MSG_RESULT([no]) ]) else AC_PATH_TOOL([KRB5CONF], [krb5-config], [$KRB5ROOT/bin/krb5-config], [$KRB5ROOT/bin:$PATH]) if test -x $KRB5CONF ; then K5CFLAGS="`$KRB5CONF --cflags`" K5LIBS="`$KRB5CONF --libs`" CPPFLAGS="$CPPFLAGS $K5CFLAGS" AC_MSG_CHECKING([for gssapi support]) if $KRB5CONF | grep gssapi >/dev/null ; then AC_MSG_RESULT([yes]) AC_DEFINE([GSSAPI], [1], [Define this if you want GSSAPI support in the version 2 protocol]) GSSCFLAGS="`$KRB5CONF --cflags gssapi`" GSSLIBS="`$KRB5CONF --libs gssapi`" CPPFLAGS="$CPPFLAGS $GSSCFLAGS" else AC_MSG_RESULT([no]) fi AC_MSG_CHECKING([whether we are using Heimdal]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ char *tmp = heimdal_version; ]])], [ AC_MSG_RESULT([yes]) AC_DEFINE([HEIMDAL], [1], [Define this if you are using the Heimdal version of Kerberos V5]) ], [AC_MSG_RESULT([no]) ]) else CPPFLAGS="$CPPFLAGS -I${KRB5ROOT}/include" LDFLAGS="$LDFLAGS -L${KRB5ROOT}/lib" AC_MSG_CHECKING([whether we are using Heimdal]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ char *tmp = heimdal_version; ]])], [ AC_MSG_RESULT([yes]) AC_DEFINE([HEIMDAL]) K5LIBS="-lkrb5" K5LIBS="$K5LIBS -lcom_err -lasn1" AC_CHECK_LIB([roken], [net_write], [K5LIBS="$K5LIBS -lroken"]) AC_CHECK_LIB([des], [des_cbc_encrypt], [K5LIBS="$K5LIBS -ldes"]) ], [ AC_MSG_RESULT([no]) K5LIBS="-lkrb5 -lk5crypto -lcom_err" ]) AC_SEARCH_LIBS([dn_expand], [resolv]) AC_CHECK_LIB([gssapi_krb5], [gss_init_sec_context], [ AC_DEFINE([GSSAPI]) GSSLIBS="-lgssapi_krb5" ], [ AC_CHECK_LIB([gssapi], [gss_init_sec_context], [ AC_DEFINE([GSSAPI]) GSSLIBS="-lgssapi" ], [ AC_CHECK_LIB([gss], [gss_init_sec_context], [ AC_DEFINE([GSSAPI]) GSSLIBS="-lgss" ], AC_MSG_WARN([Cannot find any suitable gss-api library - build may fail])) ]) ]) AC_CHECK_HEADER([gssapi.h], , [ unset ac_cv_header_gssapi_h CPPFLAGS="$CPPFLAGS -I${KRB5ROOT}/include/gssapi" AC_CHECK_HEADERS([gssapi.h], , AC_MSG_WARN([Cannot find any suitable gss-api header - build may fail]) ) ] ) oldCPP="$CPPFLAGS" CPPFLAGS="$CPPFLAGS -I${KRB5ROOT}/include/gssapi" AC_CHECK_HEADER([gssapi_krb5.h], , [ CPPFLAGS="$oldCPP" ]) fi fi if test -n "${rpath_opt}" ; then LDFLAGS="$LDFLAGS ${rpath_opt}${KRB5ROOT}/lib" fi if test ! -z "$blibpath" ; then blibpath="$blibpath:${KRB5ROOT}/lib" fi AC_CHECK_HEADERS([gssapi.h gssapi/gssapi.h]) AC_CHECK_HEADERS([gssapi_krb5.h gssapi/gssapi_krb5.h]) AC_CHECK_HEADERS([gssapi_generic.h gssapi/gssapi_generic.h]) AC_SEARCH_LIBS([k_hasafs], [kafs], [AC_DEFINE([USE_AFS], [1], [Define this if you want to use libkafs' AFS support])]) AC_CHECK_DECLS([GSS_C_NT_HOSTBASED_SERVICE], [], [], [[ #ifdef HAVE_GSSAPI_H # include #elif defined(HAVE_GSSAPI_GSSAPI_H) # include #endif #ifdef HAVE_GSSAPI_GENERIC_H # include #elif defined(HAVE_GSSAPI_GSSAPI_GENERIC_H) # include #endif ]]) saved_LIBS="$LIBS" LIBS="$LIBS $K5LIBS" AC_CHECK_FUNCS([krb5_cc_new_unique krb5_get_error_message krb5_free_error_message]) LIBS="$saved_LIBS" fi ] ) AC_SUBST([GSSLIBS]) AC_SUBST([K5LIBS]) AC_SUBST([CHANNELLIBS]) # Looking for programs, paths and files PRIVSEP_PATH=/var/empty AC_ARG_WITH([privsep-path], [ --with-privsep-path=xxx Path for privilege separation chroot (default=/var/empty)], [ if test -n "$withval" && test "x$withval" != "xno" && \ test "x${withval}" != "xyes"; then PRIVSEP_PATH=$withval fi ] ) AC_SUBST([PRIVSEP_PATH]) AC_ARG_WITH([xauth], [ --with-xauth=PATH Specify path to xauth program ], [ if test -n "$withval" && test "x$withval" != "xno" && \ test "x${withval}" != "xyes"; then xauth_path=$withval fi ], [ TestPath="$PATH" TestPath="${TestPath}${PATH_SEPARATOR}/usr/X/bin" TestPath="${TestPath}${PATH_SEPARATOR}/usr/bin/X11" TestPath="${TestPath}${PATH_SEPARATOR}/usr/X11R6/bin" TestPath="${TestPath}${PATH_SEPARATOR}/usr/openwin/bin" AC_PATH_PROG([xauth_path], [xauth], , [$TestPath]) if (test ! -z "$xauth_path" && test -x "/usr/openwin/bin/xauth") ; then xauth_path="/usr/openwin/bin/xauth" fi ] ) STRIP_OPT=-s AC_ARG_ENABLE([strip], [ --disable-strip Disable calling strip(1) on install], [ if test "x$enableval" = "xno" ; then STRIP_OPT= fi ] ) AC_SUBST([STRIP_OPT]) if test -z "$xauth_path" ; then XAUTH_PATH="undefined" AC_SUBST([XAUTH_PATH]) else AC_DEFINE_UNQUOTED([XAUTH_PATH], ["$xauth_path"], [Define if xauth is found in your path]) XAUTH_PATH=$xauth_path AC_SUBST([XAUTH_PATH]) fi dnl # --with-maildir=/path/to/mail gets top priority. dnl # if maildir is set in the platform case statement above we use that. dnl # Otherwise we run a program to get the dir from system headers. dnl # We first look for _PATH_MAILDIR then MAILDIR then _PATH_MAIL dnl # If we find _PATH_MAILDIR we do nothing because that is what dnl # session.c expects anyway. Otherwise we set to the value found dnl # stripping any trailing slash. If for some strage reason our program dnl # does not find what it needs, we default to /var/spool/mail. # Check for mail directory AC_ARG_WITH([maildir], [ --with-maildir=/path/to/mail Specify your system mail directory], [ if test "X$withval" != X && test "x$withval" != xno && \ test "x${withval}" != xyes; then AC_DEFINE_UNQUOTED([MAIL_DIRECTORY], ["$withval"], [Set this to your mail directory if you do not have _PATH_MAILDIR]) fi ],[ if test "X$maildir" != "X"; then AC_DEFINE_UNQUOTED([MAIL_DIRECTORY], ["$maildir"]) else AC_MSG_CHECKING([Discovering system mail directory]) AC_RUN_IFELSE( [AC_LANG_PROGRAM([[ #include #include #include #ifdef HAVE_PATHS_H #include #endif #ifdef HAVE_MAILLOCK_H #include #endif #define DATA "conftest.maildir" ]], [[ FILE *fd; int rc; fd = fopen(DATA,"w"); if(fd == NULL) exit(1); #if defined (_PATH_MAILDIR) if ((rc = fprintf(fd ,"_PATH_MAILDIR:%s\n", _PATH_MAILDIR)) <0) exit(1); #elif defined (MAILDIR) if ((rc = fprintf(fd ,"MAILDIR:%s\n", MAILDIR)) <0) exit(1); #elif defined (_PATH_MAIL) if ((rc = fprintf(fd ,"_PATH_MAIL:%s\n", _PATH_MAIL)) <0) exit(1); #else exit (2); #endif exit(0); ]])], [ maildir_what=`awk -F: '{print $1}' conftest.maildir` maildir=`awk -F: '{print $2}' conftest.maildir \ | sed 's|/$||'` AC_MSG_RESULT([Using: $maildir from $maildir_what]) if test "x$maildir_what" != "x_PATH_MAILDIR"; then AC_DEFINE_UNQUOTED([MAIL_DIRECTORY], ["$maildir"]) fi ], [ if test "X$ac_status" = "X2";then # our test program didn't find it. Default to /var/spool/mail AC_MSG_RESULT([Using: default value of /var/spool/mail]) AC_DEFINE_UNQUOTED([MAIL_DIRECTORY], ["/var/spool/mail"]) else AC_MSG_RESULT([*** not found ***]) fi ], [ AC_MSG_WARN([cross compiling: use --with-maildir=/path/to/mail]) ] ) fi ] ) # maildir if test ! -z "$cross_compiling" && test "x$cross_compiling" = "xyes"; then AC_MSG_WARN([cross compiling: Disabling /dev/ptmx test]) disable_ptmx_check=yes fi if test -z "$no_dev_ptmx" ; then if test "x$disable_ptmx_check" != "xyes" ; then AC_CHECK_FILE(["/dev/ptmx"], [ AC_DEFINE_UNQUOTED([HAVE_DEV_PTMX], [1], [Define if you have /dev/ptmx]) have_dev_ptmx=1 ] ) fi fi if test ! -z "$cross_compiling" && test "x$cross_compiling" != "xyes"; then AC_CHECK_FILE(["/dev/ptc"], [ AC_DEFINE_UNQUOTED([HAVE_DEV_PTS_AND_PTC], [1], [Define if you have /dev/ptc]) have_dev_ptc=1 ] ) else AC_MSG_WARN([cross compiling: Disabling /dev/ptc test]) fi # Options from here on. Some of these are preset by platform above AC_ARG_WITH([mantype], [ --with-mantype=man|cat|doc Set man page type], [ case "$withval" in man|cat|doc) MANTYPE=$withval ;; *) AC_MSG_ERROR([invalid man type: $withval]) ;; esac ] ) if test -z "$MANTYPE"; then if ${MANDOC} ${srcdir}/ssh.1 >/dev/null 2>&1; then MANTYPE=doc elif ${NROFF} -mdoc ${srcdir}/ssh.1 >/dev/null 2>&1; then MANTYPE=doc elif ${NROFF} -man ${srcdir}/ssh.1 >/dev/null 2>&1; then MANTYPE=man else MANTYPE=cat fi fi AC_SUBST([MANTYPE]) if test "$MANTYPE" = "doc"; then mansubdir=man; else mansubdir=$MANTYPE; fi AC_SUBST([mansubdir]) # Whether to disable shadow password support AC_ARG_WITH([shadow], [ --without-shadow Disable shadow password support], [ if test "x$withval" = "xno" ; then AC_DEFINE([DISABLE_SHADOW]) disable_shadow=yes fi ] ) if test -z "$disable_shadow" ; then AC_MSG_CHECKING([if the systems has expire shadow information]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include struct spwd sp; ]], [[ sp.sp_expire = sp.sp_lstchg = sp.sp_inact = 0; ]])], [ sp_expire_available=yes ], [ ]) if test "x$sp_expire_available" = "xyes" ; then AC_MSG_RESULT([yes]) AC_DEFINE([HAS_SHADOW_EXPIRE], [1], [Define if you want to use shadow password expire field]) else AC_MSG_RESULT([no]) fi fi # Use ip address instead of hostname in $DISPLAY if test ! -z "$IPADDR_IN_DISPLAY" ; then DISPLAY_HACK_MSG="yes" AC_DEFINE([IPADDR_IN_DISPLAY], [1], [Define if you need to use IP address instead of hostname in $DISPLAY]) else DISPLAY_HACK_MSG="no" AC_ARG_WITH([ipaddr-display], [ --with-ipaddr-display Use ip address instead of hostname in $DISPLAY], [ if test "x$withval" != "xno" ; then AC_DEFINE([IPADDR_IN_DISPLAY]) DISPLAY_HACK_MSG="yes" fi ] ) fi # check for /etc/default/login and use it if present. AC_ARG_ENABLE([etc-default-login], [ --disable-etc-default-login Disable using PATH from /etc/default/login [no]], [ if test "x$enableval" = "xno"; then AC_MSG_NOTICE([/etc/default/login handling disabled]) etc_default_login=no else etc_default_login=yes fi ], [ if test ! -z "$cross_compiling" && test "x$cross_compiling" = "xyes"; then AC_MSG_WARN([cross compiling: not checking /etc/default/login]) etc_default_login=no else etc_default_login=yes fi ] ) if test "x$etc_default_login" != "xno"; then AC_CHECK_FILE(["/etc/default/login"], [ external_path_file=/etc/default/login ]) if test "x$external_path_file" = "x/etc/default/login"; then AC_DEFINE([HAVE_ETC_DEFAULT_LOGIN], [1], [Define if your system has /etc/default/login]) fi fi dnl BSD systems use /etc/login.conf so --with-default-path= has no effect if test $ac_cv_func_login_getcapbool = "yes" && \ test $ac_cv_header_login_cap_h = "yes" ; then external_path_file=/etc/login.conf fi # Whether to mess with the default path SERVER_PATH_MSG="(default)" AC_ARG_WITH([default-path], [ --with-default-path= Specify default $PATH environment for server], [ if test "x$external_path_file" = "x/etc/login.conf" ; then AC_MSG_WARN([ --with-default-path=PATH has no effect on this system. Edit /etc/login.conf instead.]) elif test "x$withval" != "xno" ; then if test ! -z "$external_path_file" ; then AC_MSG_WARN([ --with-default-path=PATH will only be used if PATH is not defined in $external_path_file .]) fi user_path="$withval" SERVER_PATH_MSG="$withval" fi ], [ if test "x$external_path_file" = "x/etc/login.conf" ; then AC_MSG_WARN([Make sure the path to scp is in /etc/login.conf]) else if test ! -z "$external_path_file" ; then AC_MSG_WARN([ If PATH is defined in $external_path_file, ensure the path to scp is included, otherwise scp will not work.]) fi AC_RUN_IFELSE( [AC_LANG_PROGRAM([[ /* find out what STDPATH is */ #include #include #ifdef HAVE_PATHS_H # include #endif #ifndef _PATH_STDPATH # ifdef _PATH_USERPATH /* Irix */ # define _PATH_STDPATH _PATH_USERPATH # else # define _PATH_STDPATH "/usr/bin:/bin:/usr/sbin:/sbin" # endif #endif #include #include #include #define DATA "conftest.stdpath" ]], [[ FILE *fd; int rc; fd = fopen(DATA,"w"); if(fd == NULL) exit(1); if ((rc = fprintf(fd,"%s", _PATH_STDPATH)) < 0) exit(1); exit(0); ]])], [ user_path=`cat conftest.stdpath` ], [ user_path="/usr/bin:/bin:/usr/sbin:/sbin" ], [ user_path="/usr/bin:/bin:/usr/sbin:/sbin" ] ) # make sure $bindir is in USER_PATH so scp will work t_bindir="${bindir}" while echo "${t_bindir}" | egrep '\$\{|NONE/' >/dev/null 2>&1; do t_bindir=`eval echo ${t_bindir}` case $t_bindir in NONE/*) t_bindir=`echo $t_bindir | sed "s~NONE~$prefix~"` ;; esac case $t_bindir in NONE/*) t_bindir=`echo $t_bindir | sed "s~NONE~$ac_default_prefix~"` ;; esac done echo $user_path | grep ":$t_bindir" > /dev/null 2>&1 if test $? -ne 0 ; then echo $user_path | grep "^$t_bindir" > /dev/null 2>&1 if test $? -ne 0 ; then user_path=$user_path:$t_bindir AC_MSG_RESULT([Adding $t_bindir to USER_PATH so scp will work]) fi fi fi ] ) if test "x$external_path_file" != "x/etc/login.conf" ; then AC_DEFINE_UNQUOTED([USER_PATH], ["$user_path"], [Specify default $PATH]) AC_SUBST([user_path]) fi # Set superuser path separately to user path AC_ARG_WITH([superuser-path], [ --with-superuser-path= Specify different path for super-user], [ if test -n "$withval" && test "x$withval" != "xno" && \ test "x${withval}" != "xyes"; then AC_DEFINE_UNQUOTED([SUPERUSER_PATH], ["$withval"], [Define if you want a different $PATH for the superuser]) superuser_path=$withval fi ] ) AC_MSG_CHECKING([if we need to convert IPv4 in IPv6-mapped addresses]) IPV4_IN6_HACK_MSG="no" AC_ARG_WITH(4in6, [ --with-4in6 Check for and convert IPv4 in IPv6 mapped addresses], [ if test "x$withval" != "xno" ; then AC_MSG_RESULT([yes]) AC_DEFINE([IPV4_IN_IPV6], [1], [Detect IPv4 in IPv6 mapped addresses and treat as IPv4]) IPV4_IN6_HACK_MSG="yes" else AC_MSG_RESULT([no]) fi ], [ if test "x$inet6_default_4in6" = "xyes"; then AC_MSG_RESULT([yes (default)]) AC_DEFINE([IPV4_IN_IPV6]) IPV4_IN6_HACK_MSG="yes" else AC_MSG_RESULT([no (default)]) fi ] ) # Whether to enable BSD auth support BSD_AUTH_MSG=no AC_ARG_WITH([bsd-auth], [ --with-bsd-auth Enable BSD auth support], [ if test "x$withval" != "xno" ; then AC_DEFINE([BSD_AUTH], [1], [Define if you have BSD auth support]) BSD_AUTH_MSG=yes fi ] ) # Where to place sshd.pid piddir=/var/run # make sure the directory exists if test ! -d $piddir ; then piddir=`eval echo ${sysconfdir}` case $piddir in NONE/*) piddir=`echo $piddir | sed "s~NONE~$ac_default_prefix~"` ;; esac fi AC_ARG_WITH([pid-dir], [ --with-pid-dir=PATH Specify location of sshd.pid file], [ if test -n "$withval" && test "x$withval" != "xno" && \ test "x${withval}" != "xyes"; then piddir=$withval if test ! -d $piddir ; then AC_MSG_WARN([** no $piddir directory on this system **]) fi fi ] ) AC_DEFINE_UNQUOTED([_PATH_SSH_PIDDIR], ["$piddir"], [Specify location of ssh.pid]) AC_SUBST([piddir]) dnl allow user to disable some login recording features AC_ARG_ENABLE([lastlog], [ --disable-lastlog disable use of lastlog even if detected [no]], [ if test "x$enableval" = "xno" ; then AC_DEFINE([DISABLE_LASTLOG]) fi ] ) AC_ARG_ENABLE([utmp], [ --disable-utmp disable use of utmp even if detected [no]], [ if test "x$enableval" = "xno" ; then AC_DEFINE([DISABLE_UTMP]) fi ] ) AC_ARG_ENABLE([utmpx], [ --disable-utmpx disable use of utmpx even if detected [no]], [ if test "x$enableval" = "xno" ; then AC_DEFINE([DISABLE_UTMPX], [1], [Define if you don't want to use utmpx]) fi ] ) AC_ARG_ENABLE([wtmp], [ --disable-wtmp disable use of wtmp even if detected [no]], [ if test "x$enableval" = "xno" ; then AC_DEFINE([DISABLE_WTMP]) fi ] ) AC_ARG_ENABLE([wtmpx], [ --disable-wtmpx disable use of wtmpx even if detected [no]], [ if test "x$enableval" = "xno" ; then AC_DEFINE([DISABLE_WTMPX], [1], [Define if you don't want to use wtmpx]) fi ] ) AC_ARG_ENABLE([libutil], [ --disable-libutil disable use of libutil (login() etc.) [no]], [ if test "x$enableval" = "xno" ; then AC_DEFINE([DISABLE_LOGIN]) fi ] ) AC_ARG_ENABLE([pututline], [ --disable-pututline disable use of pututline() etc. ([uw]tmp) [no]], [ if test "x$enableval" = "xno" ; then AC_DEFINE([DISABLE_PUTUTLINE], [1], [Define if you don't want to use pututline() etc. to write [uw]tmp]) fi ] ) AC_ARG_ENABLE([pututxline], [ --disable-pututxline disable use of pututxline() etc. ([uw]tmpx) [no]], [ if test "x$enableval" = "xno" ; then AC_DEFINE([DISABLE_PUTUTXLINE], [1], [Define if you don't want to use pututxline() etc. to write [uw]tmpx]) fi ] ) AC_ARG_WITH([lastlog], [ --with-lastlog=FILE|DIR specify lastlog location [common locations]], [ if test "x$withval" = "xno" ; then AC_DEFINE([DISABLE_LASTLOG]) elif test -n "$withval" && test "x${withval}" != "xyes"; then conf_lastlog_location=$withval fi ] ) dnl lastlog, [uw]tmpx? detection dnl NOTE: set the paths in the platform section to avoid the dnl need for command-line parameters dnl lastlog and [uw]tmp are subject to a file search if all else fails dnl lastlog detection dnl NOTE: the code itself will detect if lastlog is a directory AC_MSG_CHECKING([if your system defines LASTLOG_FILE]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include #ifdef HAVE_LASTLOG_H # include #endif #ifdef HAVE_PATHS_H # include #endif #ifdef HAVE_LOGIN_H # include #endif ]], [[ char *lastlog = LASTLOG_FILE; ]])], [ AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) AC_MSG_CHECKING([if your system defines _PATH_LASTLOG]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include #ifdef HAVE_LASTLOG_H # include #endif #ifdef HAVE_PATHS_H # include #endif ]], [[ char *lastlog = _PATH_LASTLOG; ]])], [ AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) system_lastlog_path=no ]) ]) if test -z "$conf_lastlog_location"; then if test x"$system_lastlog_path" = x"no" ; then for f in /var/log/lastlog /usr/adm/lastlog /var/adm/lastlog /etc/security/lastlog ; do if (test -d "$f" || test -f "$f") ; then conf_lastlog_location=$f fi done if test -z "$conf_lastlog_location"; then AC_MSG_WARN([** Cannot find lastlog **]) dnl Don't define DISABLE_LASTLOG - that means we don't try wtmp/wtmpx fi fi fi if test -n "$conf_lastlog_location"; then AC_DEFINE_UNQUOTED([CONF_LASTLOG_FILE], ["$conf_lastlog_location"], [Define if you want to specify the path to your lastlog file]) fi dnl utmp detection AC_MSG_CHECKING([if your system defines UTMP_FILE]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include #ifdef HAVE_PATHS_H # include #endif ]], [[ char *utmp = UTMP_FILE; ]])], [ AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) system_utmp_path=no ]) if test -z "$conf_utmp_location"; then if test x"$system_utmp_path" = x"no" ; then for f in /etc/utmp /usr/adm/utmp /var/run/utmp; do if test -f $f ; then conf_utmp_location=$f fi done if test -z "$conf_utmp_location"; then AC_DEFINE([DISABLE_UTMP]) fi fi fi if test -n "$conf_utmp_location"; then AC_DEFINE_UNQUOTED([CONF_UTMP_FILE], ["$conf_utmp_location"], [Define if you want to specify the path to your utmp file]) fi dnl wtmp detection AC_MSG_CHECKING([if your system defines WTMP_FILE]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include #ifdef HAVE_PATHS_H # include #endif ]], [[ char *wtmp = WTMP_FILE; ]])], [ AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) system_wtmp_path=no ]) if test -z "$conf_wtmp_location"; then if test x"$system_wtmp_path" = x"no" ; then for f in /usr/adm/wtmp /var/log/wtmp; do if test -f $f ; then conf_wtmp_location=$f fi done if test -z "$conf_wtmp_location"; then AC_DEFINE([DISABLE_WTMP]) fi fi fi if test -n "$conf_wtmp_location"; then AC_DEFINE_UNQUOTED([CONF_WTMP_FILE], ["$conf_wtmp_location"], [Define if you want to specify the path to your wtmp file]) fi dnl wtmpx detection AC_MSG_CHECKING([if your system defines WTMPX_FILE]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include #ifdef HAVE_UTMPX_H #include #endif #ifdef HAVE_PATHS_H # include #endif ]], [[ char *wtmpx = WTMPX_FILE; ]])], [ AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) system_wtmpx_path=no ]) if test -z "$conf_wtmpx_location"; then if test x"$system_wtmpx_path" = x"no" ; then AC_DEFINE([DISABLE_WTMPX]) fi else AC_DEFINE_UNQUOTED([CONF_WTMPX_FILE], ["$conf_wtmpx_location"], [Define if you want to specify the path to your wtmpx file]) fi if test ! -z "$blibpath" ; then LDFLAGS="$LDFLAGS $blibflags$blibpath" AC_MSG_WARN([Please check and edit blibpath in LDFLAGS in Makefile]) fi AC_CHECK_MEMBER([struct lastlog.ll_line], [], [ if test x$SKIP_DISABLE_LASTLOG_DEFINE != "xyes" ; then AC_DEFINE([DISABLE_LASTLOG]) fi ], [ #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_UTMP_H #include #endif #ifdef HAVE_UTMPX_H #include #endif #ifdef HAVE_LASTLOG_H #include #endif ]) AC_CHECK_MEMBER([struct utmp.ut_line], [], [ AC_DEFINE([DISABLE_UTMP]) AC_DEFINE([DISABLE_WTMP]) ], [ #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_UTMP_H #include #endif #ifdef HAVE_UTMPX_H #include #endif #ifdef HAVE_LASTLOG_H #include #endif ]) dnl Adding -Werror to CFLAGS early prevents configure tests from running. dnl Add now. CFLAGS="$CFLAGS $werror_flags" if test "x$ac_cv_func_getaddrinfo" != "xyes" ; then TEST_SSH_IPV6=no else TEST_SSH_IPV6=yes fi AC_CHECK_DECL([BROKEN_GETADDRINFO], [TEST_SSH_IPV6=no]) AC_SUBST([TEST_SSH_IPV6], [$TEST_SSH_IPV6]) AC_SUBST([TEST_SSH_UTF8], [$TEST_SSH_UTF8]) AC_SUBST([TEST_MALLOC_OPTIONS], [$TEST_MALLOC_OPTIONS]) AC_SUBST([UNSUPPORTED_ALGORITHMS], [$unsupported_algorithms]) AC_SUBST([DEPEND], [$(cat $srcdir/.depend)]) CFLAGS="${CFLAGS} ${CFLAGS_AFTER}" LDFLAGS="${LDFLAGS} ${LDFLAGS_AFTER}" # Make a copy of CFLAGS/LDFLAGS without PIE options. LDFLAGS_NOPIE=`echo "$LDFLAGS" | sed 's/ -pie//'` CFLAGS_NOPIE=`echo "$CFLAGS" | sed 's/ -fPIE//'` AC_SUBST([LDFLAGS_NOPIE]) AC_SUBST([CFLAGS_NOPIE]) AC_EXEEXT AC_CONFIG_FILES([Makefile buildpkg.sh opensshd.init openssh.xml \ openbsd-compat/Makefile openbsd-compat/regress/Makefile \ survey.sh]) AC_OUTPUT # Print summary of options # Someone please show me a better way :) A=`eval echo ${prefix}` ; A=`eval echo ${A}` B=`eval echo ${bindir}` ; B=`eval echo ${B}` C=`eval echo ${sbindir}` ; C=`eval echo ${C}` D=`eval echo ${sysconfdir}` ; D=`eval echo ${D}` E=`eval echo ${libexecdir}/ssh-askpass` ; E=`eval echo ${E}` F=`eval echo ${mandir}/${mansubdir}X` ; F=`eval echo ${F}` G=`eval echo ${piddir}` ; G=`eval echo ${G}` H=`eval echo ${PRIVSEP_PATH}` ; H=`eval echo ${H}` I=`eval echo ${user_path}` ; I=`eval echo ${I}` J=`eval echo ${superuser_path}` ; J=`eval echo ${J}` echo "" echo "OpenSSH has been configured with the following options:" echo " User binaries: $B" echo " System binaries: $C" echo " Configuration files: $D" echo " Askpass program: $E" echo " Manual pages: $F" echo " PID file: $G" echo " Privilege separation chroot path: $H" if test "x$external_path_file" = "x/etc/login.conf" ; then echo " At runtime, sshd will use the path defined in $external_path_file" echo " Make sure the path to scp is present, otherwise scp will not work" else echo " sshd default user PATH: $I" if test ! -z "$external_path_file"; then echo " (If PATH is set in $external_path_file it will be used instead. If" echo " used, ensure the path to scp is present, otherwise scp will not work.)" fi fi if test ! -z "$superuser_path" ; then echo " sshd superuser user PATH: $J" fi echo " Manpage format: $MANTYPE" echo " PAM support: $PAM_MSG" echo " OSF SIA support: $SIA_MSG" echo " KerberosV support: $KRB5_MSG" echo " SELinux support: $SELINUX_MSG" echo " libedit support: $LIBEDIT_MSG" echo " libldns support: $LDNS_MSG" echo " Solaris process contract support: $SPC_MSG" echo " Solaris project support: $SP_MSG" echo " Solaris privilege support: $SPP_MSG" echo " IP address in \$DISPLAY hack: $DISPLAY_HACK_MSG" echo " Translate v4 in v6 hack: $IPV4_IN6_HACK_MSG" echo " BSD Auth support: $BSD_AUTH_MSG" echo " Random number source: $RAND_MSG" echo " Privsep sandbox style: $SANDBOX_STYLE" echo " PKCS#11 support: $enable_pkcs11" echo " U2F/FIDO support: $enable_sk" echo "" echo " Host: ${host}" echo " Compiler: ${CC}" echo " Compiler flags: ${CFLAGS}" echo "Preprocessor flags: ${CPPFLAGS}" echo " Linker flags: ${LDFLAGS}" echo " Libraries: ${LIBS}" if test ! -z "${CHANNELLIBS}"; then echo " +for channels: ${CHANNELLIBS}" fi if test ! -z "${LIBFIDO2}"; then echo " +for FIDO2: ${LIBFIDO2}" fi if test ! -z "${SSHDLIBS}"; then echo " +for sshd: ${SSHDLIBS}" fi echo "" if test "x$MAKE_PACKAGE_SUPPORTED" = "xyes" ; then echo "SVR4 style packages are supported with \"make package\"" echo "" fi if test "x$PAM_MSG" = "xyes" ; then echo "PAM is enabled. You may need to install a PAM control file " echo "for sshd, otherwise password authentication may fail. " echo "Example PAM control files can be found in the contrib/ " echo "subdirectory" echo "" fi if test ! -z "$NO_PEERCHECK" ; then echo "WARNING: the operating system that you are using does not" echo "appear to support getpeereid(), getpeerucred() or the" echo "SO_PEERCRED getsockopt() option. These facilities are used to" echo "enforce security checks to prevent unauthorised connections to" echo "ssh-agent. Their absence increases the risk that a malicious" echo "user can connect to your agent." echo "" fi if test "$AUDIT_MODULE" = "bsm" ; then echo "WARNING: BSM audit support is currently considered EXPERIMENTAL." echo "See the Solaris section in README.platform for details." fi diff --git a/contrib/redhat/openssh.spec b/contrib/redhat/openssh.spec index 7d6fe3cfd2fe..7a167d4456d1 100644 --- a/contrib/redhat/openssh.spec +++ b/contrib/redhat/openssh.spec @@ -1,850 +1,850 @@ -%global ver 9.4p1 +%global ver 9.5p1 %global rel 1%{?dist} # OpenSSH privilege separation requires a user & group ID %global sshd_uid 74 %global sshd_gid 74 # Version of ssh-askpass %global aversion 1.2.4.1 # Do we want to disable building of x11-askpass? (1=yes 0=no) %global no_x11_askpass 0 # Do we want to disable building of gnome-askpass? (1=yes 0=no) %global no_gnome_askpass 0 # Do we want to link against a static libcrypto? (1=yes 0=no) %global static_libcrypto 0 # Do we want smartcard support (1=yes 0=no) %global scard 0 # Use GTK2 instead of GNOME in gnome-ssh-askpass %global gtk2 1 # Use build6x options for older RHEL builds # RHEL 7 not yet supported %if 0%{?rhel} > 6 %global build6x 0 %else %global build6x 1 %endif %if 0%{?fedora} >= 26 %global compat_openssl 1 %else %global compat_openssl 0 %endif # Do we want kerberos5 support (1=yes 0=no) %global kerberos5 1 # Reserve options to override askpass settings with: # rpm -ba|--rebuild --define 'skip_xxx 1' %{?skip_x11_askpass:%global no_x11_askpass 1} %{?skip_gnome_askpass:%global no_gnome_askpass 1} # Add option to build without GTK2 for older platforms with only GTK+. # RedHat <= 7.2 and Red Hat Advanced Server 2.1 are examples. # rpm -ba|--rebuild --define 'no_gtk2 1' %{?no_gtk2:%global gtk2 0} # Is this a build for RHL 6.x or earlier? %{?build_6x:%global build6x 1} # If this is RHL 6.x, the default configuration has sysconfdir in /usr/etc. %if %{build6x} %global _sysconfdir /etc %endif # Options for static OpenSSL link: # rpm -ba|--rebuild --define "static_openssl 1" %{?static_openssl:%global static_libcrypto 1} # Options for Smartcard support: (needs libsectok and openssl-engine) # rpm -ba|--rebuild --define "smartcard 1" %{?smartcard:%global scard 1} # Is this a build for the rescue CD (without PAM)? (1=yes 0=no) %global rescue 0 %{?build_rescue:%global rescue 1} # Turn off some stuff for resuce builds %if %{rescue} %global kerberos5 0 %endif Summary: The OpenSSH implementation of SSH protocol version 2. Name: openssh Version: %{ver} %if %{rescue} Release: %{rel}rescue %else Release: %{rel} %endif URL: https://www.openssh.com/portable.html Source0: https://ftp.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-%{version}.tar.gz Source1: http://www.jmknoble.net/software/x11-ssh-askpass/x11-ssh-askpass-%{aversion}.tar.gz License: BSD Group: Applications/Internet BuildRoot: %{_tmppath}/%{name}-%{version}-buildroot Obsoletes: ssh %if %{build6x} PreReq: initscripts >= 5.00 %else Requires: initscripts >= 5.20 %endif BuildRequires: perl %if %{compat_openssl} BuildRequires: compat-openssl10-devel %else BuildRequires: openssl-devel >= 1.0.1 BuildRequires: openssl-devel < 1.1 %endif BuildRequires: /bin/login %if ! %{build6x} BuildRequires: glibc-devel, pam %else BuildRequires: /usr/include/security/pam_appl.h %endif %if ! %{no_x11_askpass} BuildRequires: /usr/include/X11/Xlib.h # Xt development tools BuildRequires: libXt-devel # Provides xmkmf BuildRequires: imake # Rely on relatively recent gtk BuildRequires: gtk2-devel %endif %if ! %{no_gnome_askpass} BuildRequires: pkgconfig %endif %if %{kerberos5} BuildRequires: krb5-devel BuildRequires: krb5-libs %endif %package clients Summary: OpenSSH clients. Requires: openssh = %{version}-%{release} Group: Applications/Internet Obsoletes: ssh-clients %package server Summary: The OpenSSH server daemon. Group: System Environment/Daemons Obsoletes: ssh-server Requires: openssh = %{version}-%{release}, chkconfig >= 0.9 %if ! %{build6x} Requires: /etc/pam.d/system-auth %endif %package askpass Summary: A passphrase dialog for OpenSSH and X. Group: Applications/Internet Requires: openssh = %{version}-%{release} Obsoletes: ssh-extras %package askpass-gnome Summary: A passphrase dialog for OpenSSH, X, and GNOME. Group: Applications/Internet Requires: openssh = %{version}-%{release} Obsoletes: ssh-extras %description SSH (Secure SHell) is a program for logging into and executing commands on a remote machine. SSH is intended to replace rlogin and rsh, and to provide secure encrypted communications between two untrusted hosts over an insecure network. X11 connections and arbitrary TCP/IP ports can also be forwarded over the secure channel. OpenSSH is OpenBSD's version of the last free version of SSH, bringing it up to date in terms of security and features, as well as removing all patented algorithms to separate libraries. This package includes the core files necessary for both the OpenSSH client and server. To make this package useful, you should also install openssh-clients, openssh-server, or both. %description clients OpenSSH is a free version of SSH (Secure SHell), a program for logging into and executing commands on a remote machine. This package includes the clients necessary to make encrypted connections to SSH servers. You'll also need to install the openssh package on OpenSSH clients. %description server OpenSSH is a free version of SSH (Secure SHell), a program for logging into and executing commands on a remote machine. This package contains the secure shell daemon (sshd). The sshd daemon allows SSH clients to securely connect to your SSH server. You also need to have the openssh package installed. %description askpass OpenSSH is a free version of SSH (Secure SHell), a program for logging into and executing commands on a remote machine. This package contains an X11 passphrase dialog for OpenSSH. %description askpass-gnome OpenSSH is a free version of SSH (Secure SHell), a program for logging into and executing commands on a remote machine. This package contains an X11 passphrase dialog for OpenSSH and the GNOME GUI desktop environment. %prep %if ! %{no_x11_askpass} %setup -q -a 1 %else %setup -q %endif %build %if %{rescue} CFLAGS="$RPM_OPT_FLAGS -Os"; export CFLAGS %endif %configure \ --sysconfdir=%{_sysconfdir}/ssh \ --libexecdir=%{_libexecdir}/openssh \ --datadir=%{_datadir}/openssh \ --with-default-path=/usr/local/bin:/bin:/usr/bin \ --with-superuser-path=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin \ --with-privsep-path=%{_var}/empty/sshd \ --mandir=%{_mandir} \ --with-mantype=man \ --disable-strip \ %if %{scard} --with-smartcard \ %endif %if %{rescue} --without-pam \ %else --with-pam \ %endif %if %{kerberos5} --with-kerberos5=$K5DIR \ %endif %if %{static_libcrypto} perl -pi -e "s|-lcrypto|%{_libdir}/libcrypto.a|g" Makefile %endif make %if ! %{no_x11_askpass} pushd x11-ssh-askpass-%{aversion} %configure --libexecdir=%{_libexecdir}/openssh xmkmf -a make popd %endif # Define a variable to toggle gnome1/gtk2 building. This is necessary # because RPM doesn't handle nested %if statements. %if %{gtk2} gtk2=yes %else gtk2=no %endif %if ! %{no_gnome_askpass} pushd contrib if [ $gtk2 = yes ] ; then make gnome-ssh-askpass2 mv gnome-ssh-askpass2 gnome-ssh-askpass else make gnome-ssh-askpass1 mv gnome-ssh-askpass1 gnome-ssh-askpass fi popd %endif %install rm -rf $RPM_BUILD_ROOT mkdir -p -m755 $RPM_BUILD_ROOT%{_sysconfdir}/ssh mkdir -p -m755 $RPM_BUILD_ROOT%{_libexecdir}/openssh mkdir -p -m755 $RPM_BUILD_ROOT%{_var}/empty/sshd make install DESTDIR=$RPM_BUILD_ROOT install -d $RPM_BUILD_ROOT/etc/pam.d/ install -d $RPM_BUILD_ROOT/etc/rc.d/init.d install -d $RPM_BUILD_ROOT%{_libexecdir}/openssh %if %{build6x} install -m644 contrib/redhat/sshd.pam.old $RPM_BUILD_ROOT/etc/pam.d/sshd %else install -m644 contrib/redhat/sshd.pam $RPM_BUILD_ROOT/etc/pam.d/sshd %endif install -m755 contrib/redhat/sshd.init $RPM_BUILD_ROOT/etc/rc.d/init.d/sshd %if ! %{no_x11_askpass} install x11-ssh-askpass-%{aversion}/x11-ssh-askpass $RPM_BUILD_ROOT%{_libexecdir}/openssh/x11-ssh-askpass ln -s x11-ssh-askpass $RPM_BUILD_ROOT%{_libexecdir}/openssh/ssh-askpass %endif %if ! %{no_gnome_askpass} install contrib/gnome-ssh-askpass $RPM_BUILD_ROOT%{_libexecdir}/openssh/gnome-ssh-askpass %endif %if ! %{scard} rm -f $RPM_BUILD_ROOT/usr/share/openssh/Ssh.bin %endif %if ! %{no_gnome_askpass} install -m 755 -d $RPM_BUILD_ROOT%{_sysconfdir}/profile.d/ install -m 755 contrib/redhat/gnome-ssh-askpass.csh $RPM_BUILD_ROOT%{_sysconfdir}/profile.d/ install -m 755 contrib/redhat/gnome-ssh-askpass.sh $RPM_BUILD_ROOT%{_sysconfdir}/profile.d/ %endif perl -pi -e "s|$RPM_BUILD_ROOT||g" $RPM_BUILD_ROOT%{_mandir}/man*/* %clean rm -rf $RPM_BUILD_ROOT %triggerun server -- ssh-server if [ "$1" != 0 -a -r /var/run/sshd.pid ] ; then touch /var/run/sshd.restart fi %triggerun server -- openssh-server < 2.5.0p1 # Count the number of HostKey and HostDsaKey statements we have. gawk 'BEGIN {IGNORECASE=1} /^hostkey/ || /^hostdsakey/ {sawhostkey = sawhostkey + 1} END {exit sawhostkey}' /etc/ssh/sshd_config # And if we only found one, we know the client was relying on the old default # behavior, which loaded the the SSH2 DSA host key when HostDsaKey wasn't # specified. Now that HostKey is used for both SSH1 and SSH2 keys, specifying # one nullifies the default, which would have loaded both. if [ $? -eq 1 ] ; then echo HostKey /etc/ssh/ssh_host_rsa_key >> /etc/ssh/sshd_config echo HostKey /etc/ssh/ssh_host_dsa_key >> /etc/ssh/sshd_config fi %triggerpostun server -- ssh-server if [ "$1" != 0 ] ; then /sbin/chkconfig --add sshd if test -f /var/run/sshd.restart ; then rm -f /var/run/sshd.restart /sbin/service sshd start > /dev/null 2>&1 || : fi fi %pre server %{_sbindir}/groupadd -r -g %{sshd_gid} sshd 2>/dev/null || : %{_sbindir}/useradd -d /var/empty/sshd -s /bin/false -u %{sshd_uid} \ -g sshd -M -r sshd 2>/dev/null || : %post server /sbin/chkconfig --add sshd %postun server /sbin/service sshd condrestart > /dev/null 2>&1 || : %preun server if [ "$1" = 0 ] then /sbin/service sshd stop > /dev/null 2>&1 || : /sbin/chkconfig --del sshd fi %files %defattr(-,root,root) %doc CREDITS ChangeLog INSTALL LICENCE OVERVIEW README* PROTOCOL* TODO %attr(0755,root,root) %{_bindir}/scp %attr(0644,root,root) %{_mandir}/man1/scp.1* %attr(0755,root,root) %dir %{_sysconfdir}/ssh %attr(0600,root,root) %config(noreplace) %{_sysconfdir}/ssh/moduli %if ! %{rescue} %attr(0755,root,root) %{_bindir}/ssh-keygen %attr(0644,root,root) %{_mandir}/man1/ssh-keygen.1* %attr(0755,root,root) %dir %{_libexecdir}/openssh %attr(4711,root,root) %{_libexecdir}/openssh/ssh-keysign %attr(0755,root,root) %{_libexecdir}/openssh/ssh-pkcs11-helper %attr(0755,root,root) %{_libexecdir}/openssh/ssh-sk-helper %attr(0644,root,root) %{_mandir}/man8/ssh-keysign.8* %attr(0644,root,root) %{_mandir}/man8/ssh-pkcs11-helper.8* %attr(0644,root,root) %{_mandir}/man8/ssh-sk-helper.8* %endif %if %{scard} %attr(0755,root,root) %dir %{_datadir}/openssh %attr(0644,root,root) %{_datadir}/openssh/Ssh.bin %endif %files clients %defattr(-,root,root) %attr(0755,root,root) %{_bindir}/ssh %attr(0644,root,root) %{_mandir}/man1/ssh.1* %attr(0644,root,root) %{_mandir}/man5/ssh_config.5* %attr(0644,root,root) %config(noreplace) %{_sysconfdir}/ssh/ssh_config %if ! %{rescue} %attr(2755,root,nobody) %{_bindir}/ssh-agent %attr(0755,root,root) %{_bindir}/ssh-add %attr(0755,root,root) %{_bindir}/ssh-keyscan %attr(0755,root,root) %{_bindir}/sftp %attr(0644,root,root) %{_mandir}/man1/ssh-agent.1* %attr(0644,root,root) %{_mandir}/man1/ssh-add.1* %attr(0644,root,root) %{_mandir}/man1/ssh-keyscan.1* %attr(0644,root,root) %{_mandir}/man1/sftp.1* %endif %if ! %{rescue} %files server %defattr(-,root,root) %dir %attr(0111,root,root) %{_var}/empty/sshd %attr(0755,root,root) %{_sbindir}/sshd %attr(0755,root,root) %{_libexecdir}/openssh/sftp-server %attr(0644,root,root) %{_mandir}/man8/sshd.8* %attr(0644,root,root) %{_mandir}/man5/moduli.5* %attr(0644,root,root) %{_mandir}/man5/sshd_config.5* %attr(0644,root,root) %{_mandir}/man8/sftp-server.8* %attr(0755,root,root) %dir %{_sysconfdir}/ssh %attr(0600,root,root) %config(noreplace) %{_sysconfdir}/ssh/sshd_config %attr(0600,root,root) %config(noreplace) /etc/pam.d/sshd %attr(0755,root,root) %config /etc/rc.d/init.d/sshd %endif %if ! %{no_x11_askpass} %files askpass %defattr(-,root,root) %doc x11-ssh-askpass-%{aversion}/README %doc x11-ssh-askpass-%{aversion}/ChangeLog %doc x11-ssh-askpass-%{aversion}/SshAskpass*.ad %{_libexecdir}/openssh/ssh-askpass %attr(0755,root,root) %{_libexecdir}/openssh/x11-ssh-askpass %endif %if ! %{no_gnome_askpass} %files askpass-gnome %defattr(-,root,root) %attr(0755,root,root) %config %{_sysconfdir}/profile.d/gnome-ssh-askpass.* %attr(0755,root,root) %{_libexecdir}/openssh/gnome-ssh-askpass %endif %changelog * Thu Oct 28 2021 Damien Miller - Remove remaining traces of --with-md5-passwords * Mon Jul 20 2020 Damien Miller - Add ssh-sk-helper and corresponding manual page. * Sat Feb 10 2018 Darren Tucker - Update openssl-devel dependency to match current requirements. - Handle Fedora >=6 openssl 1.0 compat libs. - Remove SSH1 from description. - Don't strip binaries at build time so that debuginfo package can be created. * Sun Nov 16 2014 Nico Kadel-Garcia - Add '--mandir' and '--with-mantype' for RHEL 5 compatibility - Add 'dist' option to 'ver' so package names reflect OS at build time - Always include x11-ssh-askpass tarball in SRPM - Add openssh-x11-aspass BuildRequires for libXT-devel, imake, gtk2-devel - Discard 'K5DIR' reporting, not usable inside 'mock' for RHEL 5 compatibility - Discard obsolete '--with-rsh' configure option - Update openssl-devel dependency to 0.9.8f, as found in autoconf * Wed Jul 14 2010 Tim Rice - test for skip_x11_askpass (line 77) should have been for no_x11_askpass * Mon Jun 2 2003 Damien Miller - Remove noip6 option. This may be controlled at run-time in client config file using new AddressFamily directive * Mon May 12 2003 Damien Miller - Don't install profile.d scripts when not building with GNOME/GTK askpass (patch from bet@rahul.net) * Tue Oct 01 2002 Damien Miller - Install ssh-agent setgid nobody to prevent ptrace() key theft attacks * Mon Sep 30 2002 Damien Miller - Use contrib/ Makefile for building askpass programs * Fri Jun 21 2002 Damien Miller - Merge in spec changes from seba@iq.pl (Sebastian Pachuta) - Add new {ssh,sshd}_config.5 manpages - Add new ssh-keysign program and remove setuid from ssh client * Fri May 10 2002 Damien Miller - Merge in spec changes from RedHat, reorgansie a little - Add Privsep user, group and directory * Thu Mar 7 2002 Nalin Dahyabhai 3.1p1-2 - bump and grind (through the build system) * Thu Mar 7 2002 Nalin Dahyabhai 3.1p1-1 - require sharutils for building (mindrot #137) - require db1-devel only when building for 6.x (#55105), which probably won't work anyway (3.1 requires OpenSSL 0.9.6 to build), but what the heck - require pam-devel by file (not by package name) again - add Markus's patch to compile with OpenSSL 0.9.5a (from http://bugzilla.mindrot.org/show_bug.cgi?id=141) and apply it if we're building for 6.x * Thu Mar 7 2002 Nalin Dahyabhai 3.1p1-0 - update to 3.1p1 * Tue Mar 5 2002 Nalin Dahyabhai SNAP-20020305 - update to SNAP-20020305 - drop debug patch, fixed upstream * Wed Feb 20 2002 Nalin Dahyabhai SNAP-20020220 - update to SNAP-20020220 for testing purposes (you've been warned, if there's anything to be warned about, gss patches won't apply, I don't mind) * Wed Feb 13 2002 Nalin Dahyabhai 3.0.2p1-3 - add patches from Simon Wilkinson and Nicolas Williams for GSSAPI key exchange, authentication, and named key support * Wed Jan 23 2002 Nalin Dahyabhai 3.0.2p1-2 - remove dependency on db1-devel, which has just been swallowed up whole by gnome-libs-devel * Sat Dec 29 2001 Nalin Dahyabhai - adjust build dependencies so that build6x actually works right (fix from Hugo van der Kooij) * Tue Dec 4 2001 Nalin Dahyabhai 3.0.2p1-1 - update to 3.0.2p1 * Fri Nov 16 2001 Nalin Dahyabhai 3.0.1p1-1 - update to 3.0.1p1 * Tue Nov 13 2001 Nalin Dahyabhai - update to current CVS (not for use in distribution) * Thu Nov 8 2001 Nalin Dahyabhai 3.0p1-1 - merge some of Damien Miller changes from the upstream 3.0p1 spec file and init script * Wed Nov 7 2001 Nalin Dahyabhai - update to 3.0p1 - update to x11-ssh-askpass 1.2.4.1 - change build dependency on a file from pam-devel to the pam-devel package - replace primes with moduli * Thu Sep 27 2001 Nalin Dahyabhai 2.9p2-9 - incorporate fix from Markus Friedl's advisory for IP-based authorization bugs * Thu Sep 13 2001 Bernhard Rosenkraenzer 2.9p2-8 - Merge changes to rescue build from current sysadmin survival cd * Thu Sep 6 2001 Nalin Dahyabhai 2.9p2-7 - fix scp's server's reporting of file sizes, and build with the proper preprocessor define to get large-file capable open(), stat(), etc. (sftp has been doing this correctly all along) (#51827) - configure without --with-ipv4-default on RHL 7.x and newer (#45987,#52247) - pull cvs patch to fix support for /etc/nologin for non-PAM logins (#47298) - mark profile.d scriptlets as config files (#42337) - refer to Jason Stone's mail for zsh workaround for exit-hanging quasi-bug - change a couple of log() statements to debug() statements (#50751) - pull cvs patch to add -t flag to sshd (#28611) - clear fd_sets correctly (one bit per FD, not one byte per FD) (#43221) * Mon Aug 20 2001 Nalin Dahyabhai 2.9p2-6 - add db1-devel as a BuildPrerequisite (noted by Hans Ecke) * Thu Aug 16 2001 Nalin Dahyabhai - pull cvs patch to fix remote port forwarding with protocol 2 * Thu Aug 9 2001 Nalin Dahyabhai - pull cvs patch to add session initialization to no-pty sessions - pull cvs patch to not cut off challengeresponse auth needlessly - refuse to do X11 forwarding if xauth isn't there, handy if you enable it by default on a system that doesn't have X installed (#49263) * Wed Aug 8 2001 Nalin Dahyabhai - don't apply patches to code we don't intend to build (spotted by Matt Galgoci) * Mon Aug 6 2001 Nalin Dahyabhai - pass OPTIONS correctly to initlog (#50151) * Wed Jul 25 2001 Nalin Dahyabhai - switch to x11-ssh-askpass 1.2.2 * Wed Jul 11 2001 Nalin Dahyabhai - rebuild in new environment * Mon Jun 25 2001 Nalin Dahyabhai - disable the gssapi patch * Mon Jun 18 2001 Nalin Dahyabhai - update to 2.9p2 - refresh to a new version of the gssapi patch * Thu Jun 7 2001 Nalin Dahyabhai - change Copyright: BSD to License: BSD - add Markus Friedl's unverified patch for the cookie file deletion problem so that we can verify it - drop patch to check if xauth is present (was folded into cookie patch) - don't apply gssapi patches for the errata candidate - clear supplemental groups list at startup * Fri May 25 2001 Nalin Dahyabhai - fix an error parsing the new default sshd_config - add a fix from Markus Friedl (via openssh-unix-dev) for ssh-keygen not dealing with comments right * Thu May 24 2001 Nalin Dahyabhai - add in Simon Wilkinson's GSSAPI patch to give it some testing in-house, to be removed before the next beta cycle because it's a big departure from the upstream version * Thu May 3 2001 Nalin Dahyabhai - finish marking strings in the init script for translation - modify init script to source /etc/sysconfig/sshd and pass $OPTIONS to sshd at startup (change merged from openssh.com init script, originally by Pekka Savola) - refuse to do X11 forwarding if xauth isn't there, handy if you enable it by default on a system that doesn't have X installed * Wed May 2 2001 Nalin Dahyabhai - update to 2.9 - drop various patches that came from or went upstream or to or from CVS * Wed Apr 18 2001 Nalin Dahyabhai - only require initscripts 5.00 on 6.2 (reported by Peter Bieringer) * Sun Apr 8 2001 Preston Brown - remove explicit openssl requirement, fixes builddistro issue - make initscript stop() function wait until sshd really dead to avoid races in condrestart * Mon Apr 2 2001 Nalin Dahyabhai - mention that challengereponse supports PAM, so disabling password doesn't limit users to pubkey and rsa auth (#34378) - bypass the daemon() function in the init script and call initlog directly, because daemon() won't start a daemon it detects is already running (like open connections) - require the version of openssl we had when we were built * Fri Mar 23 2001 Nalin Dahyabhai - make do_pam_setcred() smart enough to know when to establish creds and when to reinitialize them - add in a couple of other fixes from Damien for inclusion in the errata * Thu Mar 22 2001 Nalin Dahyabhai - update to 2.5.2p2 - call setcred() again after initgroups, because the "creds" could actually be group memberships * Tue Mar 20 2001 Nalin Dahyabhai - update to 2.5.2p1 (includes endianness fixes in the rijndael implementation) - don't enable challenge-response by default until we find a way to not have too many userauth requests (we may make up to six pubkey and up to three password attempts as it is) - remove build dependency on rsh to match openssh.com's packages more closely * Sat Mar 3 2001 Nalin Dahyabhai - remove dependency on openssl -- would need to be too precise * Fri Mar 2 2001 Nalin Dahyabhai - rebuild in new environment * Mon Feb 26 2001 Nalin Dahyabhai - Revert the patch to move pam_open_session. - Init script and spec file changes from Pekka Savola. (#28750) - Patch sftp to recognize '-o protocol' arguments. (#29540) * Thu Feb 22 2001 Nalin Dahyabhai - Chuck the closing patch. - Add a trigger to add host keys for protocol 2 to the config file, now that configuration file syntax requires us to specify it with HostKey if we specify any other HostKey values, which we do. * Tue Feb 20 2001 Nalin Dahyabhai - Redo patch to move pam_open_session after the server setuid()s to the user. - Rework the nopam patch to use be picked up by autoconf. * Mon Feb 19 2001 Nalin Dahyabhai - Update for 2.5.1p1. - Add init script mods from Pekka Savola. - Tweak the init script to match the CVS contrib script more closely. - Redo patch to ssh-add to try to adding both identity and id_dsa to also try adding id_rsa. * Fri Feb 16 2001 Nalin Dahyabhai - Update for 2.5.0p1. - Use $RPM_OPT_FLAGS instead of -O when building gnome-ssh-askpass - Resync with parts of Damien Miller's openssh.spec from CVS, including update of x11 askpass to 1.2.0. - Only require openssl (don't prereq) because we generate keys in the init script now. * Tue Feb 13 2001 Nalin Dahyabhai - Don't open a PAM session until we've forked and become the user (#25690). - Apply Andrew Bartlett's patch for letting pam_authenticate() know which host the user is attempting a login from. - Resync with parts of Damien Miller's openssh.spec from CVS. - Don't expose KbdInt responses in debug messages (from CVS). - Detect and handle errors in rsa_{public,private}_decrypt (from CVS). * Wed Feb 7 2001 Trond Eivind Glomsrxd - i18n-tweak to initscript. * Tue Jan 23 2001 Nalin Dahyabhai - More gettextizing. - Close all files after going into daemon mode (needs more testing). - Extract patch from CVS to handle auth banners (in the client). - Extract patch from CVS to handle compat weirdness. * Fri Jan 19 2001 Nalin Dahyabhai - Finish with the gettextizing. * Thu Jan 18 2001 Nalin Dahyabhai - Fix a bug in auth2-pam.c (#23877) - Gettextize the init script. * Wed Dec 20 2000 Nalin Dahyabhai - Incorporate a switch for using PAM configs for 6.x, just in case. * Tue Dec 5 2000 Nalin Dahyabhai - Incorporate Bero's changes for a build specifically for rescue CDs. * Wed Nov 29 2000 Nalin Dahyabhai - Don't treat pam_setcred() failure as fatal unless pam_authenticate() has succeeded, to allow public-key authentication after a failure with "none" authentication. (#21268) * Tue Nov 28 2000 Nalin Dahyabhai - Update to x11-askpass 1.1.1. (#21301) - Don't second-guess fixpaths, which causes paths to get fixed twice. (#21290) * Mon Nov 27 2000 Nalin Dahyabhai - Merge multiple PAM text messages into subsequent prompts when possible when doing keyboard-interactive authentication. * Sun Nov 26 2000 Nalin Dahyabhai - Disable the built-in MD5 password support. We're using PAM. - Take a crack at doing keyboard-interactive authentication with PAM, and enable use of it in the default client configuration so that the client will try it when the server disallows password authentication. - Build with debugging flags. Build root policies strip all binaries anyway. * Tue Nov 21 2000 Nalin Dahyabhai - Use DESTDIR instead of %%makeinstall. - Remove /usr/X11R6/bin from the path-fixing patch. * Mon Nov 20 2000 Nalin Dahyabhai - Add the primes file from the latest snapshot to the main package (#20884). - Add the dev package to the prereq list (#19984). - Remove the default path and mimic login's behavior in the server itself. * Fri Nov 17 2000 Nalin Dahyabhai - Resync with conditional options in Damien Miller's .spec file for an errata. - Change libexecdir from %%{_libexecdir}/ssh to %%{_libexecdir}/openssh. * Tue Nov 7 2000 Nalin Dahyabhai - Update to OpenSSH 2.3.0p1. - Update to x11-askpass 1.1.0. - Enable keyboard-interactive authentication. * Mon Oct 30 2000 Nalin Dahyabhai - Update to ssh-askpass-x11 1.0.3. - Change authentication related messages to be private (#19966). * Tue Oct 10 2000 Nalin Dahyabhai - Patch ssh-keygen to be able to list signatures for DSA public key files it generates. * Thu Oct 5 2000 Nalin Dahyabhai - Add BuildRequires on /usr/include/security/pam_appl.h to be sure we always build PAM authentication in. - Try setting SSH_ASKPASS if gnome-ssh-askpass is installed. - Clean out no-longer-used patches. - Patch ssh-add to try to add both identity and id_dsa, and to error only when neither exists. * Mon Oct 2 2000 Nalin Dahyabhai - Update x11-askpass to 1.0.2. (#17835) - Add BuildRequiress for /bin/login and /usr/bin/rsh so that configure will always find them in the right place. (#17909) - Set the default path to be the same as the one supplied by /bin/login, but add /usr/X11R6/bin. (#17909) - Try to handle obsoletion of ssh-server more cleanly. Package names are different, but init script name isn't. (#17865) * Wed Sep 6 2000 Nalin Dahyabhai - Update to 2.2.0p1. (#17835) - Tweak the init script to allow proper restarting. (#18023) * Wed Aug 23 2000 Nalin Dahyabhai - Update to 20000823 snapshot. - Change subpackage requirements from %%{version} to %%{version}-%%{release} - Back out the pipe patch. * Mon Jul 17 2000 Nalin Dahyabhai - Update to 2.1.1p4, which includes fixes for config file parsing problems. - Move the init script back. - Add Damien's quick fix for wackiness. * Wed Jul 12 2000 Nalin Dahyabhai - Update to 2.1.1p3, which includes fixes for X11 forwarding and strtok(). * Thu Jul 6 2000 Nalin Dahyabhai - Move condrestart to server postun. - Move key generation to init script. - Actually use the right patch for moving the key generation to the init script. - Clean up the init script a bit. * Wed Jul 5 2000 Nalin Dahyabhai - Fix X11 forwarding, from mail post by Chan Shih-Ping Richard. * Sun Jul 2 2000 Nalin Dahyabhai - Update to 2.1.1p2. - Use of strtok() considered harmful. * Sat Jul 1 2000 Nalin Dahyabhai - Get the build root out of the man pages. * Thu Jun 29 2000 Nalin Dahyabhai - Add and use condrestart support in the init script. - Add newer initscripts as a prereq. * Tue Jun 27 2000 Nalin Dahyabhai - Build in new environment (release 2) - Move -clients subpackage to Applications/Internet group * Fri Jun 9 2000 Nalin Dahyabhai - Update to 2.2.1p1 * Sat Jun 3 2000 Nalin Dahyabhai - Patch to build with neither RSA nor RSAref. - Miscellaneous FHS-compliance tweaks. - Fix for possibly-compressed man pages. * Wed Mar 15 2000 Damien Miller - Updated for new location - Updated for new gnome-ssh-askpass build * Sun Dec 26 1999 Damien Miller - Added Jim Knoble's askpass * Mon Nov 15 1999 Damien Miller - Split subpackages further based on patch from jim knoble * Sat Nov 13 1999 Damien Miller - Added 'Obsoletes' directives * Tue Nov 09 1999 Damien Miller - Use make install - Subpackages * Mon Nov 08 1999 Damien Miller - Added links for slogin - Fixed perms on manpages * Sat Oct 30 1999 Damien Miller - Renamed init script * Fri Oct 29 1999 Damien Miller - Back to old binary names * Thu Oct 28 1999 Damien Miller - Use autoconf - New binary names * Wed Oct 27 1999 Damien Miller - Initial RPMification, based on Jan "Yenya" Kasprzak's spec. diff --git a/contrib/suse/openssh.spec b/contrib/suse/openssh.spec index 77736276638c..921d0f5a4d67 100644 --- a/contrib/suse/openssh.spec +++ b/contrib/suse/openssh.spec @@ -1,245 +1,245 @@ # Default values for additional components %define build_x11_askpass 1 # Define the UID/GID to use for privilege separation %define sshd_gid 65 %define sshd_uid 71 # The version of x11-ssh-askpass to use %define xversion 1.2.4.1 # Allow the ability to override defaults with -D skip_xxx=1 %{?skip_x11_askpass:%define build_x11_askpass 0} Summary: OpenSSH, a free Secure Shell (SSH) protocol implementation Name: openssh -Version: 9.4p1 +Version: 9.5p1 URL: https://www.openssh.com/ Release: 1 Source0: openssh-%{version}.tar.gz Source1: x11-ssh-askpass-%{xversion}.tar.gz License: BSD Group: Productivity/Networking/SSH BuildRoot: %{_tmppath}/openssh-%{version}-buildroot PreReq: openssl Obsoletes: ssh Provides: ssh # # (Build[ing] Prereq[uisites] only work for RPM 2.95 and newer.) # building prerequisites -- stuff for # OpenSSL (openssl-devel), # and Gnome (glibdev, gtkdev, and gnlibsd) # BuildPrereq: openssl BuildPrereq: zlib-devel #BuildPrereq: glibdev #BuildPrereq: gtkdev #BuildPrereq: gnlibsd %package askpass Summary: A passphrase dialog for OpenSSH and the X window System. Group: Productivity/Networking/SSH Requires: openssh = %{version} Obsoletes: ssh-extras Provides: openssh:${_libdir}/ssh/ssh-askpass %if %{build_x11_askpass} BuildPrereq: XFree86-devel %endif %description Ssh (Secure Shell) is a program for logging into a remote machine and for executing commands in a remote machine. It is intended to replace rlogin and rsh, and provide secure encrypted communications between two untrusted hosts over an insecure network. X11 connections and arbitrary TCP/IP ports can also be forwarded over the secure channel. OpenSSH is OpenBSD's rework of the last free version of SSH, bringing it up to date in terms of security and features, as well as removing all patented algorithms to separate libraries (OpenSSL). This package includes all files necessary for both the OpenSSH client and server. %description askpass Ssh (Secure Shell) is a program for logging into a remote machine and for executing commands in a remote machine. It is intended to replace rlogin and rsh, and provide secure encrypted communications between two untrusted hosts over an insecure network. X11 connections and arbitrary TCP/IP ports can also be forwarded over the secure channel. OpenSSH is OpenBSD's rework of the last free version of SSH, bringing it up to date in terms of security and features, as well as removing all patented algorithms to separate libraries (OpenSSL). This package contains an X Window System passphrase dialog for OpenSSH. %changelog * Mon Jul 20 2020 Damien Miller - Add ssh-sk-helper and corresponding manual page. * Wed Oct 26 2005 Iain Morgan - Removed accidental inclusion of --without-zlib-version-check * Tue Oct 25 2005 Iain Morgan - Overhaul to deal with newer versions of SuSE and OpenSSH * Mon Jun 12 2000 Damien Miller - Glob manpages to catch compressed files * Wed Mar 15 2000 Damien Miller - Updated for new location - Updated for new gnome-ssh-askpass build * Sun Dec 26 1999 Chris Saia - Made symlink to gnome-ssh-askpass called ssh-askpass * Wed Nov 24 1999 Chris Saia - Removed patches that included /etc/pam.d/sshd, /sbin/init.d/rc.sshd, and /var/adm/fillup-templates/rc.config.sshd, since Damien merged these into his released tarfile - Changed permissions on ssh_config in the install procedure to 644 from 600 even though it was correct in the %files section and thus right in the RPMs - Postinstall script for the server now only prints "Generating SSH host key..." if we need to actually do this, in order to eliminate a confusing message if an SSH host key is already in place - Marked all manual pages as %doc(umentation) * Mon Nov 22 1999 Chris Saia - Added flag to configure daemon with TCP Wrappers support - Added building prerequisites (works in RPM 3.0 and newer) * Thu Nov 18 1999 Chris Saia - Made this package correct for SuSE. - Changed instances of pam_pwdb.so to pam_unix.so, since it works more properly with SuSE, and lib_pwdb.so isn't installed by default. * Mon Nov 15 1999 Damien Miller - Split subpackages further based on patch from jim knoble * Sat Nov 13 1999 Damien Miller - Added 'Obsoletes' directives * Tue Nov 09 1999 Damien Miller - Use make install - Subpackages * Mon Nov 08 1999 Damien Miller - Added links for slogin - Fixed perms on manpages * Sat Oct 30 1999 Damien Miller - Renamed init script * Fri Oct 29 1999 Damien Miller - Back to old binary names * Thu Oct 28 1999 Damien Miller - Use autoconf - New binary names * Wed Oct 27 1999 Damien Miller - Initial RPMification, based on Jan "Yenya" Kasprzak's spec. %prep %if %{build_x11_askpass} %setup -q -a 1 %else %setup -q %endif %build CFLAGS="$RPM_OPT_FLAGS" \ %configure --prefix=/usr \ --sysconfdir=%{_sysconfdir}/ssh \ --mandir=%{_mandir} \ --with-privsep-path=/var/lib/empty \ --with-pam \ --libexecdir=%{_libdir}/ssh make %if %{build_x11_askpass} cd x11-ssh-askpass-%{xversion} %configure --mandir=/usr/X11R6/man \ --libexecdir=%{_libdir}/ssh xmkmf -a make cd .. %endif %install rm -rf $RPM_BUILD_ROOT make install DESTDIR=$RPM_BUILD_ROOT/ install -d $RPM_BUILD_ROOT/etc/pam.d/ install -d $RPM_BUILD_ROOT/etc/init.d/ install -d $RPM_BUILD_ROOT/var/adm/fillup-templates install -m644 contrib/sshd.pam.generic $RPM_BUILD_ROOT/etc/pam.d/sshd install -m744 contrib/suse/rc.sshd $RPM_BUILD_ROOT/etc/init.d/sshd install -m744 contrib/suse/sysconfig.ssh \ $RPM_BUILD_ROOT/var/adm/fillup-templates %if %{build_x11_askpass} cd x11-ssh-askpass-%{xversion} make install install.man BINDIR=%{_libdir}/ssh DESTDIR=$RPM_BUILD_ROOT/ rm -f $RPM_BUILD_ROOT/usr/share/Ssh.bin %endif %clean rm -rf $RPM_BUILD_ROOT %pre /usr/sbin/groupadd -g %{sshd_gid} -o -r sshd 2> /dev/null || : /usr/sbin/useradd -r -o -g sshd -u %{sshd_uid} -s /bin/false -c "SSH Privilege Separation User" -d /var/lib/sshd sshd 2> /dev/null || : %post /usr/bin/ssh-keygen -A %{fillup_and_insserv -n -y ssh sshd} %run_permissions %verifyscript %verify_permissions -e /etc/ssh/sshd_config -e /etc/ssh/ssh_config -e /usr/bin/ssh %preun %stop_on_removal sshd %postun %restart_on_update sshd %{insserv_cleanup} %files %defattr(-,root,root) %doc ChangeLog OVERVIEW README* PROTOCOL* %doc TODO CREDITS LICENCE %attr(0755,root,root) %dir %{_sysconfdir}/ssh %attr(0644,root,root) %config(noreplace) %{_sysconfdir}/ssh/ssh_config %attr(0600,root,root) %config(noreplace) %{_sysconfdir}/ssh/sshd_config %attr(0600,root,root) %config(noreplace) %{_sysconfdir}/ssh/moduli %attr(0644,root,root) %config(noreplace) /etc/pam.d/sshd %attr(0755,root,root) %config /etc/init.d/sshd %attr(0755,root,root) %{_bindir}/ssh-keygen %attr(0755,root,root) %{_bindir}/scp %attr(0755,root,root) %{_bindir}/ssh %attr(0755,root,root) %{_bindir}/ssh-agent %attr(0755,root,root) %{_bindir}/ssh-add %attr(0755,root,root) %{_bindir}/ssh-keyscan %attr(0755,root,root) %{_bindir}/sftp %attr(0755,root,root) %{_sbindir}/sshd %attr(0755,root,root) %dir %{_libdir}/ssh %attr(0755,root,root) %{_libdir}/ssh/sftp-server %attr(4711,root,root) %{_libdir}/ssh/ssh-keysign %attr(0755,root,root) %{_libdir}/ssh/ssh-pkcs11-helper %attr(0755,root,root) %{_libdir}/ssh/ssh-sk-helper %attr(0644,root,root) %doc %{_mandir}/man1/scp.1* %attr(0644,root,root) %doc %{_mandir}/man1/sftp.1* %attr(0644,root,root) %doc %{_mandir}/man1/ssh.1* %attr(0644,root,root) %doc %{_mandir}/man1/ssh-add.1* %attr(0644,root,root) %doc %{_mandir}/man1/ssh-agent.1* %attr(0644,root,root) %doc %{_mandir}/man1/ssh-keygen.1* %attr(0644,root,root) %doc %{_mandir}/man1/ssh-keyscan.1* %attr(0644,root,root) %doc %{_mandir}/man5/moduli.5* %attr(0644,root,root) %doc %{_mandir}/man5/ssh_config.5* %attr(0644,root,root) %doc %{_mandir}/man5/sshd_config.5* %attr(0644,root,root) %doc %{_mandir}/man8/sftp-server.8* %attr(0644,root,root) %doc %{_mandir}/man8/ssh-keysign.8* %attr(0644,root,root) %doc %{_mandir}/man8/ssh-pkcs11-helper.8* %attr(0644,root,root) %doc %{_mandir}/man8/ssh-sk-helper.8* %attr(0644,root,root) %doc %{_mandir}/man8/sshd.8* %attr(0644,root,root) /var/adm/fillup-templates/sysconfig.ssh %if %{build_x11_askpass} %files askpass %defattr(-,root,root) %doc x11-ssh-askpass-%{xversion}/README %doc x11-ssh-askpass-%{xversion}/ChangeLog %doc x11-ssh-askpass-%{xversion}/SshAskpass*.ad %attr(0755,root,root) %{_libdir}/ssh/ssh-askpass %attr(0755,root,root) %{_libdir}/ssh/x11-ssh-askpass %attr(0644,root,root) %doc /usr/X11R6/man/man1/ssh-askpass.1x* %attr(0644,root,root) %doc /usr/X11R6/man/man1/x11-ssh-askpass.1x* %attr(0644,root,root) %config /usr/X11R6/lib/X11/app-defaults/SshAskpass %endif diff --git a/kex.c b/kex.c index b4e2ab75f541..8ff92f2a2d5e 100644 --- a/kex.c +++ b/kex.c @@ -1,1477 +1,1498 @@ -/* $OpenBSD: kex.c,v 1.178 2023/03/12 10:40:39 dtucker Exp $ */ +/* $OpenBSD: kex.c,v 1.181 2023/08/28 03:28:43 djm Exp $ */ /* * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" #include #include #include #include #include #include #include #include #ifdef HAVE_POLL_H #include #endif #ifdef WITH_OPENSSL #include #include #endif #include "ssh.h" #include "ssh2.h" #include "atomicio.h" #include "version.h" #include "packet.h" #include "compat.h" #include "cipher.h" #include "sshkey.h" #include "kex.h" #include "log.h" #include "mac.h" #include "match.h" #include "misc.h" #include "dispatch.h" #include "monitor.h" #include "myproposal.h" #include "ssherr.h" #include "sshbuf.h" #include "digest.h" #include "xmalloc.h" /* prototype */ static int kex_choose_conf(struct ssh *); static int kex_input_newkeys(int, u_int32_t, struct ssh *); static const char * const proposal_names[PROPOSAL_MAX] = { "KEX algorithms", "host key algorithms", "ciphers ctos", "ciphers stoc", "MACs ctos", "MACs stoc", "compression ctos", "compression stoc", "languages ctos", "languages stoc", }; struct kexalg { char *name; u_int type; int ec_nid; int hash_alg; }; static const struct kexalg kexalgs[] = { #ifdef WITH_OPENSSL { KEX_DH1, KEX_DH_GRP1_SHA1, 0, SSH_DIGEST_SHA1 }, { KEX_DH14_SHA1, KEX_DH_GRP14_SHA1, 0, SSH_DIGEST_SHA1 }, { KEX_DH14_SHA256, KEX_DH_GRP14_SHA256, 0, SSH_DIGEST_SHA256 }, { KEX_DH16_SHA512, KEX_DH_GRP16_SHA512, 0, SSH_DIGEST_SHA512 }, { KEX_DH18_SHA512, KEX_DH_GRP18_SHA512, 0, SSH_DIGEST_SHA512 }, { KEX_DHGEX_SHA1, KEX_DH_GEX_SHA1, 0, SSH_DIGEST_SHA1 }, #ifdef HAVE_EVP_SHA256 { KEX_DHGEX_SHA256, KEX_DH_GEX_SHA256, 0, SSH_DIGEST_SHA256 }, #endif /* HAVE_EVP_SHA256 */ #ifdef OPENSSL_HAS_ECC { KEX_ECDH_SHA2_NISTP256, KEX_ECDH_SHA2, NID_X9_62_prime256v1, SSH_DIGEST_SHA256 }, { KEX_ECDH_SHA2_NISTP384, KEX_ECDH_SHA2, NID_secp384r1, SSH_DIGEST_SHA384 }, # ifdef OPENSSL_HAS_NISTP521 { KEX_ECDH_SHA2_NISTP521, KEX_ECDH_SHA2, NID_secp521r1, SSH_DIGEST_SHA512 }, # endif /* OPENSSL_HAS_NISTP521 */ #endif /* OPENSSL_HAS_ECC */ #endif /* WITH_OPENSSL */ #if defined(HAVE_EVP_SHA256) || !defined(WITH_OPENSSL) { KEX_CURVE25519_SHA256, KEX_C25519_SHA256, 0, SSH_DIGEST_SHA256 }, { KEX_CURVE25519_SHA256_OLD, KEX_C25519_SHA256, 0, SSH_DIGEST_SHA256 }, #ifdef USE_SNTRUP761X25519 { KEX_SNTRUP761X25519_SHA512, KEX_KEM_SNTRUP761X25519_SHA512, 0, SSH_DIGEST_SHA512 }, #endif #endif /* HAVE_EVP_SHA256 || !WITH_OPENSSL */ { NULL, 0, -1, -1}, }; char * kex_alg_list(char sep) { char *ret = NULL, *tmp; size_t nlen, rlen = 0; const struct kexalg *k; for (k = kexalgs; k->name != NULL; k++) { if (ret != NULL) ret[rlen++] = sep; nlen = strlen(k->name); if ((tmp = realloc(ret, rlen + nlen + 2)) == NULL) { free(ret); return NULL; } ret = tmp; memcpy(ret + rlen, k->name, nlen + 1); rlen += nlen; } return ret; } static const struct kexalg * kex_alg_by_name(const char *name) { const struct kexalg *k; for (k = kexalgs; k->name != NULL; k++) { if (strcmp(k->name, name) == 0) return k; } return NULL; } /* Validate KEX method name list */ int kex_names_valid(const char *names) { char *s, *cp, *p; if (names == NULL || strcmp(names, "") == 0) return 0; if ((s = cp = strdup(names)) == NULL) return 0; for ((p = strsep(&cp, ",")); p && *p != '\0'; (p = strsep(&cp, ","))) { if (kex_alg_by_name(p) == NULL) { error("Unsupported KEX algorithm \"%.100s\"", p); free(s); return 0; } } debug3("kex names ok: [%s]", names); free(s); return 1; } /* * Concatenate algorithm names, avoiding duplicates in the process. * Caller must free returned string. */ char * kex_names_cat(const char *a, const char *b) { char *ret = NULL, *tmp = NULL, *cp, *p, *m; size_t len; if (a == NULL || *a == '\0') return strdup(b); if (b == NULL || *b == '\0') return strdup(a); if (strlen(b) > 1024*1024) return NULL; len = strlen(a) + strlen(b) + 2; if ((tmp = cp = strdup(b)) == NULL || (ret = calloc(1, len)) == NULL) { free(tmp); return NULL; } strlcpy(ret, a, len); for ((p = strsep(&cp, ",")); p && *p != '\0'; (p = strsep(&cp, ","))) { if ((m = match_list(ret, p, NULL)) != NULL) { free(m); continue; /* Algorithm already present */ } if (strlcat(ret, ",", len) >= len || strlcat(ret, p, len) >= len) { free(tmp); free(ret); return NULL; /* Shouldn't happen */ } } free(tmp); return ret; } /* * Assemble a list of algorithms from a default list and a string from a * configuration file. The user-provided string may begin with '+' to * indicate that it should be appended to the default, '-' that the * specified names should be removed, or '^' that they should be placed * at the head. */ int kex_assemble_names(char **listp, const char *def, const char *all) { char *cp, *tmp, *patterns; char *list = NULL, *ret = NULL, *matching = NULL, *opatterns = NULL; int r = SSH_ERR_INTERNAL_ERROR; if (listp == NULL || def == NULL || all == NULL) return SSH_ERR_INVALID_ARGUMENT; if (*listp == NULL || **listp == '\0') { if ((*listp = strdup(def)) == NULL) return SSH_ERR_ALLOC_FAIL; return 0; } list = *listp; *listp = NULL; if (*list == '+') { /* Append names to default list */ if ((tmp = kex_names_cat(def, list + 1)) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto fail; } free(list); list = tmp; } else if (*list == '-') { /* Remove names from default list */ if ((*listp = match_filter_denylist(def, list + 1)) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto fail; } free(list); /* filtering has already been done */ return 0; } else if (*list == '^') { /* Place names at head of default list */ if ((tmp = kex_names_cat(list + 1, def)) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto fail; } free(list); list = tmp; } else { /* Explicit list, overrides default - just use "list" as is */ } /* * The supplied names may be a pattern-list. For the -list case, * the patterns are applied above. For the +list and explicit list * cases we need to do it now. */ ret = NULL; if ((patterns = opatterns = strdup(list)) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto fail; } /* Apply positive (i.e. non-negated) patterns from the list */ while ((cp = strsep(&patterns, ",")) != NULL) { if (*cp == '!') { /* negated matches are not supported here */ r = SSH_ERR_INVALID_ARGUMENT; goto fail; } free(matching); if ((matching = match_filter_allowlist(all, cp)) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto fail; } if ((tmp = kex_names_cat(ret, matching)) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto fail; } free(ret); ret = tmp; } if (ret == NULL || *ret == '\0') { /* An empty name-list is an error */ /* XXX better error code? */ r = SSH_ERR_INVALID_ARGUMENT; goto fail; } /* success */ *listp = ret; ret = NULL; r = 0; fail: free(matching); free(opatterns); free(list); free(ret); return r; } /* * Fill out a proposal array with dynamically allocated values, which may * be modified as required for compatibility reasons. * Any of the options may be NULL, in which case the default is used. * Array contents must be freed by calling kex_proposal_free_entries. */ void kex_proposal_populate_entries(struct ssh *ssh, char *prop[PROPOSAL_MAX], const char *kexalgos, const char *ciphers, const char *macs, const char *comp, const char *hkalgs) { const char *defpropserver[PROPOSAL_MAX] = { KEX_SERVER }; const char *defpropclient[PROPOSAL_MAX] = { KEX_CLIENT }; const char **defprop = ssh->kex->server ? defpropserver : defpropclient; u_int i; if (prop == NULL) fatal_f("proposal missing"); for (i = 0; i < PROPOSAL_MAX; i++) { switch(i) { case PROPOSAL_KEX_ALGS: prop[i] = compat_kex_proposal(ssh, kexalgos ? kexalgos : defprop[i]); break; case PROPOSAL_ENC_ALGS_CTOS: case PROPOSAL_ENC_ALGS_STOC: prop[i] = xstrdup(ciphers ? ciphers : defprop[i]); break; case PROPOSAL_MAC_ALGS_CTOS: case PROPOSAL_MAC_ALGS_STOC: prop[i] = xstrdup(macs ? macs : defprop[i]); break; case PROPOSAL_COMP_ALGS_CTOS: case PROPOSAL_COMP_ALGS_STOC: prop[i] = xstrdup(comp ? comp : defprop[i]); break; case PROPOSAL_SERVER_HOST_KEY_ALGS: prop[i] = xstrdup(hkalgs ? hkalgs : defprop[i]); break; default: prop[i] = xstrdup(defprop[i]); } } } void kex_proposal_free_entries(char *prop[PROPOSAL_MAX]) { u_int i; for (i = 0; i < PROPOSAL_MAX; i++) free(prop[i]); } /* put algorithm proposal into buffer */ int kex_prop2buf(struct sshbuf *b, char *proposal[PROPOSAL_MAX]) { u_int i; int r; sshbuf_reset(b); /* * add a dummy cookie, the cookie will be overwritten by * kex_send_kexinit(), each time a kexinit is set */ for (i = 0; i < KEX_COOKIE_LEN; i++) { if ((r = sshbuf_put_u8(b, 0)) != 0) return r; } for (i = 0; i < PROPOSAL_MAX; i++) { if ((r = sshbuf_put_cstring(b, proposal[i])) != 0) return r; } if ((r = sshbuf_put_u8(b, 0)) != 0 || /* first_kex_packet_follows */ (r = sshbuf_put_u32(b, 0)) != 0) /* uint32 reserved */ return r; return 0; } /* parse buffer and return algorithm proposal */ int kex_buf2prop(struct sshbuf *raw, int *first_kex_follows, char ***propp) { struct sshbuf *b = NULL; u_char v; u_int i; char **proposal = NULL; int r; *propp = NULL; if ((proposal = calloc(PROPOSAL_MAX, sizeof(char *))) == NULL) return SSH_ERR_ALLOC_FAIL; if ((b = sshbuf_fromb(raw)) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } if ((r = sshbuf_consume(b, KEX_COOKIE_LEN)) != 0) { /* skip cookie */ error_fr(r, "consume cookie"); goto out; } /* extract kex init proposal strings */ for (i = 0; i < PROPOSAL_MAX; i++) { if ((r = sshbuf_get_cstring(b, &(proposal[i]), NULL)) != 0) { error_fr(r, "parse proposal %u", i); goto out; } debug2("%s: %s", proposal_names[i], proposal[i]); } /* first kex follows / reserved */ if ((r = sshbuf_get_u8(b, &v)) != 0 || /* first_kex_follows */ (r = sshbuf_get_u32(b, &i)) != 0) { /* reserved */ error_fr(r, "parse"); goto out; } if (first_kex_follows != NULL) *first_kex_follows = v; debug2("first_kex_follows %d ", v); debug2("reserved %u ", i); r = 0; *propp = proposal; out: if (r != 0 && proposal != NULL) kex_prop_free(proposal); sshbuf_free(b); return r; } void kex_prop_free(char **proposal) { u_int i; if (proposal == NULL) return; for (i = 0; i < PROPOSAL_MAX; i++) free(proposal[i]); free(proposal); } int kex_protocol_error(int type, u_int32_t seq, struct ssh *ssh) { int r; error("kex protocol error: type %d seq %u", type, seq); if ((r = sshpkt_start(ssh, SSH2_MSG_UNIMPLEMENTED)) != 0 || (r = sshpkt_put_u32(ssh, seq)) != 0 || (r = sshpkt_send(ssh)) != 0) return r; return 0; } static void kex_reset_dispatch(struct ssh *ssh) { ssh_dispatch_range(ssh, SSH2_MSG_TRANSPORT_MIN, SSH2_MSG_TRANSPORT_MAX, &kex_protocol_error); } static int kex_send_ext_info(struct ssh *ssh) { int r; char *algs; debug("Sending SSH2_MSG_EXT_INFO"); if ((algs = sshkey_alg_list(0, 1, 1, ',')) == NULL) return SSH_ERR_ALLOC_FAIL; /* XXX filter algs list by allowed pubkey/hostbased types */ if ((r = sshpkt_start(ssh, SSH2_MSG_EXT_INFO)) != 0 || - (r = sshpkt_put_u32(ssh, 2)) != 0 || + (r = sshpkt_put_u32(ssh, 3)) != 0 || (r = sshpkt_put_cstring(ssh, "server-sig-algs")) != 0 || (r = sshpkt_put_cstring(ssh, algs)) != 0 || (r = sshpkt_put_cstring(ssh, "publickey-hostbound@openssh.com")) != 0 || (r = sshpkt_put_cstring(ssh, "0")) != 0 || + (r = sshpkt_put_cstring(ssh, "ping@openssh.com")) != 0 || + (r = sshpkt_put_cstring(ssh, "0")) != 0 || (r = sshpkt_send(ssh)) != 0) { error_fr(r, "compose"); goto out; } /* success */ r = 0; out: free(algs); return r; } int kex_send_newkeys(struct ssh *ssh) { int r; kex_reset_dispatch(ssh); if ((r = sshpkt_start(ssh, SSH2_MSG_NEWKEYS)) != 0 || (r = sshpkt_send(ssh)) != 0) return r; debug("SSH2_MSG_NEWKEYS sent"); ssh_dispatch_set(ssh, SSH2_MSG_NEWKEYS, &kex_input_newkeys); if (ssh->kex->ext_info_c && (ssh->kex->flags & KEX_INITIAL) != 0) if ((r = kex_send_ext_info(ssh)) != 0) return r; debug("expecting SSH2_MSG_NEWKEYS"); return 0; } +/* Check whether an ext_info value contains the expected version string */ +static int +kex_ext_info_check_ver(struct kex *kex, const char *name, + const u_char *val, size_t len, const char *want_ver, u_int flag) +{ + if (memchr(val, '\0', len) != NULL) { + error("SSH2_MSG_EXT_INFO: %s value contains nul byte", name); + return SSH_ERR_INVALID_FORMAT; + } + debug_f("%s=<%s>", name, val); + if (strcmp(val, want_ver) == 0) + kex->flags |= flag; + else + debug_f("unsupported version of %s extension", name); + return 0; +} + int kex_input_ext_info(int type, u_int32_t seq, struct ssh *ssh) { struct kex *kex = ssh->kex; u_int32_t i, ninfo; char *name; u_char *val; size_t vlen; int r; debug("SSH2_MSG_EXT_INFO received"); ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &kex_protocol_error); if ((r = sshpkt_get_u32(ssh, &ninfo)) != 0) return r; if (ninfo >= 1024) { error("SSH2_MSG_EXT_INFO with too many entries, expected " "<=1024, received %u", ninfo); return SSH_ERR_INVALID_FORMAT; } for (i = 0; i < ninfo; i++) { if ((r = sshpkt_get_cstring(ssh, &name, NULL)) != 0) return r; if ((r = sshpkt_get_string(ssh, &val, &vlen)) != 0) { free(name); return r; } if (strcmp(name, "server-sig-algs") == 0) { /* Ensure no \0 lurking in value */ if (memchr(val, '\0', vlen) != NULL) { error_f("nul byte in %s", name); + free(name); + free(val); return SSH_ERR_INVALID_FORMAT; } debug_f("%s=<%s>", name, val); kex->server_sig_algs = val; val = NULL; } else if (strcmp(name, "publickey-hostbound@openssh.com") == 0) { - /* XXX refactor */ - /* Ensure no \0 lurking in value */ - if (memchr(val, '\0', vlen) != NULL) { - error_f("nul byte in %s", name); - return SSH_ERR_INVALID_FORMAT; + if ((r = kex_ext_info_check_ver(kex, name, val, vlen, + "0", KEX_HAS_PUBKEY_HOSTBOUND)) != 0) { + free(name); + free(val); + return r; } - debug_f("%s=<%s>", name, val); - if (strcmp(val, "0") == 0) - kex->flags |= KEX_HAS_PUBKEY_HOSTBOUND; - else { - debug_f("unsupported version of %s extension", - name); + } else if (strcmp(name, "ping@openssh.com") == 0) { + if ((r = kex_ext_info_check_ver(kex, name, val, vlen, + "0", KEX_HAS_PING)) != 0) { + free(name); + free(val); + return r; } } else debug_f("%s (unrecognised)", name); free(name); free(val); } return sshpkt_get_end(ssh); } static int kex_input_newkeys(int type, u_int32_t seq, struct ssh *ssh) { struct kex *kex = ssh->kex; int r; debug("SSH2_MSG_NEWKEYS received"); ssh_dispatch_set(ssh, SSH2_MSG_NEWKEYS, &kex_protocol_error); ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_input_kexinit); if ((r = sshpkt_get_end(ssh)) != 0) return r; if ((r = ssh_set_newkeys(ssh, MODE_IN)) != 0) return r; kex->done = 1; kex->flags &= ~KEX_INITIAL; sshbuf_reset(kex->peer); /* sshbuf_reset(kex->my); */ kex->flags &= ~KEX_INIT_SENT; free(kex->name); kex->name = NULL; return 0; } int kex_send_kexinit(struct ssh *ssh) { u_char *cookie; struct kex *kex = ssh->kex; int r; if (kex == NULL) { error_f("no kex"); return SSH_ERR_INTERNAL_ERROR; } if (kex->flags & KEX_INIT_SENT) return 0; kex->done = 0; /* generate a random cookie */ if (sshbuf_len(kex->my) < KEX_COOKIE_LEN) { error_f("bad kex length: %zu < %d", sshbuf_len(kex->my), KEX_COOKIE_LEN); return SSH_ERR_INVALID_FORMAT; } if ((cookie = sshbuf_mutable_ptr(kex->my)) == NULL) { error_f("buffer error"); return SSH_ERR_INTERNAL_ERROR; } arc4random_buf(cookie, KEX_COOKIE_LEN); if ((r = sshpkt_start(ssh, SSH2_MSG_KEXINIT)) != 0 || (r = sshpkt_putb(ssh, kex->my)) != 0 || (r = sshpkt_send(ssh)) != 0) { error_fr(r, "compose reply"); return r; } debug("SSH2_MSG_KEXINIT sent"); kex->flags |= KEX_INIT_SENT; return 0; } int kex_input_kexinit(int type, u_int32_t seq, struct ssh *ssh) { struct kex *kex = ssh->kex; const u_char *ptr; u_int i; size_t dlen; int r; debug("SSH2_MSG_KEXINIT received"); if (kex == NULL) { error_f("no kex"); return SSH_ERR_INTERNAL_ERROR; } ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, NULL); ptr = sshpkt_ptr(ssh, &dlen); if ((r = sshbuf_put(kex->peer, ptr, dlen)) != 0) return r; /* discard packet */ for (i = 0; i < KEX_COOKIE_LEN; i++) { if ((r = sshpkt_get_u8(ssh, NULL)) != 0) { error_fr(r, "discard cookie"); return r; } } for (i = 0; i < PROPOSAL_MAX; i++) { if ((r = sshpkt_get_string(ssh, NULL, NULL)) != 0) { error_fr(r, "discard proposal"); return r; } } /* * XXX RFC4253 sec 7: "each side MAY guess" - currently no supported * KEX method has the server move first, but a server might be using * a custom method or one that we otherwise don't support. We should * be prepared to remember first_kex_follows here so we can eat a * packet later. * XXX2 - RFC4253 is kind of ambiguous on what first_kex_follows means * for cases where the server *doesn't* go first. I guess we should * ignore it when it is set for these cases, which is what we do now. */ if ((r = sshpkt_get_u8(ssh, NULL)) != 0 || /* first_kex_follows */ (r = sshpkt_get_u32(ssh, NULL)) != 0 || /* reserved */ (r = sshpkt_get_end(ssh)) != 0) return r; if (!(kex->flags & KEX_INIT_SENT)) if ((r = kex_send_kexinit(ssh)) != 0) return r; if ((r = kex_choose_conf(ssh)) != 0) return r; if (kex->kex_type < KEX_MAX && kex->kex[kex->kex_type] != NULL) return (kex->kex[kex->kex_type])(ssh); error_f("unknown kex type %u", kex->kex_type); return SSH_ERR_INTERNAL_ERROR; } struct kex * kex_new(void) { struct kex *kex; if ((kex = calloc(1, sizeof(*kex))) == NULL || (kex->peer = sshbuf_new()) == NULL || (kex->my = sshbuf_new()) == NULL || (kex->client_version = sshbuf_new()) == NULL || (kex->server_version = sshbuf_new()) == NULL || (kex->session_id = sshbuf_new()) == NULL) { kex_free(kex); return NULL; } return kex; } void kex_free_newkeys(struct newkeys *newkeys) { if (newkeys == NULL) return; if (newkeys->enc.key) { explicit_bzero(newkeys->enc.key, newkeys->enc.key_len); free(newkeys->enc.key); newkeys->enc.key = NULL; } if (newkeys->enc.iv) { explicit_bzero(newkeys->enc.iv, newkeys->enc.iv_len); free(newkeys->enc.iv); newkeys->enc.iv = NULL; } free(newkeys->enc.name); explicit_bzero(&newkeys->enc, sizeof(newkeys->enc)); free(newkeys->comp.name); explicit_bzero(&newkeys->comp, sizeof(newkeys->comp)); mac_clear(&newkeys->mac); if (newkeys->mac.key) { explicit_bzero(newkeys->mac.key, newkeys->mac.key_len); free(newkeys->mac.key); newkeys->mac.key = NULL; } free(newkeys->mac.name); explicit_bzero(&newkeys->mac, sizeof(newkeys->mac)); freezero(newkeys, sizeof(*newkeys)); } void kex_free(struct kex *kex) { u_int mode; if (kex == NULL) return; #ifdef WITH_OPENSSL DH_free(kex->dh); #ifdef OPENSSL_HAS_ECC EC_KEY_free(kex->ec_client_key); #endif /* OPENSSL_HAS_ECC */ #endif /* WITH_OPENSSL */ for (mode = 0; mode < MODE_MAX; mode++) { kex_free_newkeys(kex->newkeys[mode]); kex->newkeys[mode] = NULL; } sshbuf_free(kex->peer); sshbuf_free(kex->my); sshbuf_free(kex->client_version); sshbuf_free(kex->server_version); sshbuf_free(kex->client_pub); sshbuf_free(kex->session_id); sshbuf_free(kex->initial_sig); sshkey_free(kex->initial_hostkey); free(kex->failed_choice); free(kex->hostkey_alg); free(kex->name); free(kex); } int kex_ready(struct ssh *ssh, char *proposal[PROPOSAL_MAX]) { int r; if ((r = kex_prop2buf(ssh->kex->my, proposal)) != 0) return r; ssh->kex->flags = KEX_INITIAL; kex_reset_dispatch(ssh); ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_input_kexinit); return 0; } int kex_setup(struct ssh *ssh, char *proposal[PROPOSAL_MAX]) { int r; if ((r = kex_ready(ssh, proposal)) != 0) return r; if ((r = kex_send_kexinit(ssh)) != 0) { /* we start */ kex_free(ssh->kex); ssh->kex = NULL; return r; } return 0; } /* * Request key re-exchange, returns 0 on success or a ssherr.h error * code otherwise. Must not be called if KEX is incomplete or in-progress. */ int kex_start_rekex(struct ssh *ssh) { if (ssh->kex == NULL) { error_f("no kex"); return SSH_ERR_INTERNAL_ERROR; } if (ssh->kex->done == 0) { error_f("requested twice"); return SSH_ERR_INTERNAL_ERROR; } ssh->kex->done = 0; return kex_send_kexinit(ssh); } static int choose_enc(struct sshenc *enc, char *client, char *server) { char *name = match_list(client, server, NULL); if (name == NULL) return SSH_ERR_NO_CIPHER_ALG_MATCH; if ((enc->cipher = cipher_by_name(name)) == NULL) { error_f("unsupported cipher %s", name); free(name); return SSH_ERR_INTERNAL_ERROR; } enc->name = name; enc->enabled = 0; enc->iv = NULL; enc->iv_len = cipher_ivlen(enc->cipher); enc->key = NULL; enc->key_len = cipher_keylen(enc->cipher); enc->block_size = cipher_blocksize(enc->cipher); return 0; } static int choose_mac(struct ssh *ssh, struct sshmac *mac, char *client, char *server) { char *name = match_list(client, server, NULL); if (name == NULL) return SSH_ERR_NO_MAC_ALG_MATCH; if (mac_setup(mac, name) < 0) { error_f("unsupported MAC %s", name); free(name); return SSH_ERR_INTERNAL_ERROR; } mac->name = name; mac->key = NULL; mac->enabled = 0; return 0; } static int choose_comp(struct sshcomp *comp, char *client, char *server) { char *name = match_list(client, server, NULL); if (name == NULL) return SSH_ERR_NO_COMPRESS_ALG_MATCH; #ifdef WITH_ZLIB if (strcmp(name, "zlib@openssh.com") == 0) { comp->type = COMP_DELAYED; } else if (strcmp(name, "zlib") == 0) { comp->type = COMP_ZLIB; } else #endif /* WITH_ZLIB */ if (strcmp(name, "none") == 0) { comp->type = COMP_NONE; } else { error_f("unsupported compression scheme %s", name); free(name); return SSH_ERR_INTERNAL_ERROR; } comp->name = name; return 0; } static int choose_kex(struct kex *k, char *client, char *server) { const struct kexalg *kexalg; k->name = match_list(client, server, NULL); debug("kex: algorithm: %s", k->name ? k->name : "(no match)"); if (k->name == NULL) return SSH_ERR_NO_KEX_ALG_MATCH; if ((kexalg = kex_alg_by_name(k->name)) == NULL) { error_f("unsupported KEX method %s", k->name); return SSH_ERR_INTERNAL_ERROR; } k->kex_type = kexalg->type; k->hash_alg = kexalg->hash_alg; k->ec_nid = kexalg->ec_nid; return 0; } static int choose_hostkeyalg(struct kex *k, char *client, char *server) { free(k->hostkey_alg); k->hostkey_alg = match_list(client, server, NULL); debug("kex: host key algorithm: %s", k->hostkey_alg ? k->hostkey_alg : "(no match)"); if (k->hostkey_alg == NULL) return SSH_ERR_NO_HOSTKEY_ALG_MATCH; k->hostkey_type = sshkey_type_from_name(k->hostkey_alg); if (k->hostkey_type == KEY_UNSPEC) { error_f("unsupported hostkey algorithm %s", k->hostkey_alg); return SSH_ERR_INTERNAL_ERROR; } k->hostkey_nid = sshkey_ecdsa_nid_from_name(k->hostkey_alg); return 0; } static int proposals_match(char *my[PROPOSAL_MAX], char *peer[PROPOSAL_MAX]) { static int check[] = { PROPOSAL_KEX_ALGS, PROPOSAL_SERVER_HOST_KEY_ALGS, -1 }; int *idx; char *p; for (idx = &check[0]; *idx != -1; idx++) { if ((p = strchr(my[*idx], ',')) != NULL) *p = '\0'; if ((p = strchr(peer[*idx], ',')) != NULL) *p = '\0'; if (strcmp(my[*idx], peer[*idx]) != 0) { debug2("proposal mismatch: my %s peer %s", my[*idx], peer[*idx]); return (0); } } debug2("proposals match"); return (1); } /* returns non-zero if proposal contains any algorithm from algs */ static int has_any_alg(const char *proposal, const char *algs) { char *cp; if ((cp = match_list(proposal, algs, NULL)) == NULL) return 0; free(cp); return 1; } static int kex_choose_conf(struct ssh *ssh) { struct kex *kex = ssh->kex; struct newkeys *newkeys; char **my = NULL, **peer = NULL; char **cprop, **sprop; int nenc, nmac, ncomp; u_int mode, ctos, need, dh_need, authlen; int r, first_kex_follows; debug2("local %s KEXINIT proposal", kex->server ? "server" : "client"); if ((r = kex_buf2prop(kex->my, NULL, &my)) != 0) goto out; debug2("peer %s KEXINIT proposal", kex->server ? "client" : "server"); if ((r = kex_buf2prop(kex->peer, &first_kex_follows, &peer)) != 0) goto out; if (kex->server) { cprop=peer; sprop=my; } else { cprop=my; sprop=peer; } /* Check whether client supports ext_info_c */ if (kex->server && (kex->flags & KEX_INITIAL)) { char *ext; ext = match_list("ext-info-c", peer[PROPOSAL_KEX_ALGS], NULL); kex->ext_info_c = (ext != NULL); free(ext); } /* Check whether client supports rsa-sha2 algorithms */ if (kex->server && (kex->flags & KEX_INITIAL)) { if (has_any_alg(peer[PROPOSAL_SERVER_HOST_KEY_ALGS], "rsa-sha2-256,rsa-sha2-256-cert-v01@openssh.com")) kex->flags |= KEX_RSA_SHA2_256_SUPPORTED; if (has_any_alg(peer[PROPOSAL_SERVER_HOST_KEY_ALGS], "rsa-sha2-512,rsa-sha2-512-cert-v01@openssh.com")) kex->flags |= KEX_RSA_SHA2_512_SUPPORTED; } /* Algorithm Negotiation */ if ((r = choose_kex(kex, cprop[PROPOSAL_KEX_ALGS], sprop[PROPOSAL_KEX_ALGS])) != 0) { kex->failed_choice = peer[PROPOSAL_KEX_ALGS]; peer[PROPOSAL_KEX_ALGS] = NULL; goto out; } if ((r = choose_hostkeyalg(kex, cprop[PROPOSAL_SERVER_HOST_KEY_ALGS], sprop[PROPOSAL_SERVER_HOST_KEY_ALGS])) != 0) { kex->failed_choice = peer[PROPOSAL_SERVER_HOST_KEY_ALGS]; peer[PROPOSAL_SERVER_HOST_KEY_ALGS] = NULL; goto out; } for (mode = 0; mode < MODE_MAX; mode++) { if ((newkeys = calloc(1, sizeof(*newkeys))) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } kex->newkeys[mode] = newkeys; ctos = (!kex->server && mode == MODE_OUT) || (kex->server && mode == MODE_IN); nenc = ctos ? PROPOSAL_ENC_ALGS_CTOS : PROPOSAL_ENC_ALGS_STOC; nmac = ctos ? PROPOSAL_MAC_ALGS_CTOS : PROPOSAL_MAC_ALGS_STOC; ncomp = ctos ? PROPOSAL_COMP_ALGS_CTOS : PROPOSAL_COMP_ALGS_STOC; if ((r = choose_enc(&newkeys->enc, cprop[nenc], sprop[nenc])) != 0) { kex->failed_choice = peer[nenc]; peer[nenc] = NULL; goto out; } authlen = cipher_authlen(newkeys->enc.cipher); /* ignore mac for authenticated encryption */ if (authlen == 0 && (r = choose_mac(ssh, &newkeys->mac, cprop[nmac], sprop[nmac])) != 0) { kex->failed_choice = peer[nmac]; peer[nmac] = NULL; goto out; } if ((r = choose_comp(&newkeys->comp, cprop[ncomp], sprop[ncomp])) != 0) { kex->failed_choice = peer[ncomp]; peer[ncomp] = NULL; goto out; } debug("kex: %s cipher: %s MAC: %s compression: %s", ctos ? "client->server" : "server->client", newkeys->enc.name, authlen == 0 ? newkeys->mac.name : "", newkeys->comp.name); } need = dh_need = 0; for (mode = 0; mode < MODE_MAX; mode++) { newkeys = kex->newkeys[mode]; need = MAXIMUM(need, newkeys->enc.key_len); need = MAXIMUM(need, newkeys->enc.block_size); need = MAXIMUM(need, newkeys->enc.iv_len); need = MAXIMUM(need, newkeys->mac.key_len); dh_need = MAXIMUM(dh_need, cipher_seclen(newkeys->enc.cipher)); dh_need = MAXIMUM(dh_need, newkeys->enc.block_size); dh_need = MAXIMUM(dh_need, newkeys->enc.iv_len); dh_need = MAXIMUM(dh_need, newkeys->mac.key_len); } /* XXX need runden? */ kex->we_need = need; kex->dh_need = dh_need; /* ignore the next message if the proposals do not match */ if (first_kex_follows && !proposals_match(my, peer)) ssh->dispatch_skip_packets = 1; r = 0; out: kex_prop_free(my); kex_prop_free(peer); return r; } static int derive_key(struct ssh *ssh, int id, u_int need, u_char *hash, u_int hashlen, const struct sshbuf *shared_secret, u_char **keyp) { struct kex *kex = ssh->kex; struct ssh_digest_ctx *hashctx = NULL; char c = id; u_int have; size_t mdsz; u_char *digest; int r; if ((mdsz = ssh_digest_bytes(kex->hash_alg)) == 0) return SSH_ERR_INVALID_ARGUMENT; if ((digest = calloc(1, ROUNDUP(need, mdsz))) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } /* K1 = HASH(K || H || "A" || session_id) */ if ((hashctx = ssh_digest_start(kex->hash_alg)) == NULL || ssh_digest_update_buffer(hashctx, shared_secret) != 0 || ssh_digest_update(hashctx, hash, hashlen) != 0 || ssh_digest_update(hashctx, &c, 1) != 0 || ssh_digest_update_buffer(hashctx, kex->session_id) != 0 || ssh_digest_final(hashctx, digest, mdsz) != 0) { r = SSH_ERR_LIBCRYPTO_ERROR; error_f("KEX hash failed"); goto out; } ssh_digest_free(hashctx); hashctx = NULL; /* * expand key: * Kn = HASH(K || H || K1 || K2 || ... || Kn-1) * Key = K1 || K2 || ... || Kn */ for (have = mdsz; need > have; have += mdsz) { if ((hashctx = ssh_digest_start(kex->hash_alg)) == NULL || ssh_digest_update_buffer(hashctx, shared_secret) != 0 || ssh_digest_update(hashctx, hash, hashlen) != 0 || ssh_digest_update(hashctx, digest, have) != 0 || ssh_digest_final(hashctx, digest + have, mdsz) != 0) { error_f("KDF failed"); r = SSH_ERR_LIBCRYPTO_ERROR; goto out; } ssh_digest_free(hashctx); hashctx = NULL; } #ifdef DEBUG_KEX fprintf(stderr, "key '%c'== ", c); dump_digest("key", digest, need); #endif *keyp = digest; digest = NULL; r = 0; out: free(digest); ssh_digest_free(hashctx); return r; } #define NKEYS 6 int kex_derive_keys(struct ssh *ssh, u_char *hash, u_int hashlen, const struct sshbuf *shared_secret) { struct kex *kex = ssh->kex; u_char *keys[NKEYS]; u_int i, j, mode, ctos; int r; /* save initial hash as session id */ if ((kex->flags & KEX_INITIAL) != 0) { if (sshbuf_len(kex->session_id) != 0) { error_f("already have session ID at kex"); return SSH_ERR_INTERNAL_ERROR; } if ((r = sshbuf_put(kex->session_id, hash, hashlen)) != 0) return r; } else if (sshbuf_len(kex->session_id) == 0) { error_f("no session ID in rekex"); return SSH_ERR_INTERNAL_ERROR; } for (i = 0; i < NKEYS; i++) { if ((r = derive_key(ssh, 'A'+i, kex->we_need, hash, hashlen, shared_secret, &keys[i])) != 0) { for (j = 0; j < i; j++) free(keys[j]); return r; } } for (mode = 0; mode < MODE_MAX; mode++) { ctos = (!kex->server && mode == MODE_OUT) || (kex->server && mode == MODE_IN); kex->newkeys[mode]->enc.iv = keys[ctos ? 0 : 1]; kex->newkeys[mode]->enc.key = keys[ctos ? 2 : 3]; kex->newkeys[mode]->mac.key = keys[ctos ? 4 : 5]; } return 0; } int kex_load_hostkey(struct ssh *ssh, struct sshkey **prvp, struct sshkey **pubp) { struct kex *kex = ssh->kex; *pubp = NULL; *prvp = NULL; if (kex->load_host_public_key == NULL || kex->load_host_private_key == NULL) { error_f("missing hostkey loader"); return SSH_ERR_INVALID_ARGUMENT; } *pubp = kex->load_host_public_key(kex->hostkey_type, kex->hostkey_nid, ssh); *prvp = kex->load_host_private_key(kex->hostkey_type, kex->hostkey_nid, ssh); if (*pubp == NULL) return SSH_ERR_NO_HOSTKEY_LOADED; return 0; } int kex_verify_host_key(struct ssh *ssh, struct sshkey *server_host_key) { struct kex *kex = ssh->kex; if (kex->verify_host_key == NULL) { error_f("missing hostkey verifier"); return SSH_ERR_INVALID_ARGUMENT; } if (server_host_key->type != kex->hostkey_type || (kex->hostkey_type == KEY_ECDSA && server_host_key->ecdsa_nid != kex->hostkey_nid)) return SSH_ERR_KEY_TYPE_MISMATCH; if (kex->verify_host_key(server_host_key, ssh) == -1) return SSH_ERR_SIGNATURE_INVALID; return 0; } #if defined(DEBUG_KEX) || defined(DEBUG_KEXDH) || defined(DEBUG_KEXECDH) void dump_digest(const char *msg, const u_char *digest, int len) { fprintf(stderr, "%s\n", msg); sshbuf_dump_data(digest, len, stderr); } #endif /* * Send a plaintext error message to the peer, suffixed by \r\n. * Only used during banner exchange, and there only for the server. */ static void send_error(struct ssh *ssh, char *msg) { char *crnl = "\r\n"; if (!ssh->kex->server) return; if (atomicio(vwrite, ssh_packet_get_connection_out(ssh), msg, strlen(msg)) != strlen(msg) || atomicio(vwrite, ssh_packet_get_connection_out(ssh), crnl, strlen(crnl)) != strlen(crnl)) error_f("write: %.100s", strerror(errno)); } /* * Sends our identification string and waits for the peer's. Will block for * up to timeout_ms (or indefinitely if timeout_ms <= 0). * Returns on 0 success or a ssherr.h code on failure. */ int kex_exchange_identification(struct ssh *ssh, int timeout_ms, const char *version_addendum) { int remote_major, remote_minor, mismatch, oerrno = 0; size_t len, n; int r, expect_nl; u_char c; struct sshbuf *our_version = ssh->kex->server ? ssh->kex->server_version : ssh->kex->client_version; struct sshbuf *peer_version = ssh->kex->server ? ssh->kex->client_version : ssh->kex->server_version; char *our_version_string = NULL, *peer_version_string = NULL; char *cp, *remote_version = NULL; /* Prepare and send our banner */ sshbuf_reset(our_version); if (version_addendum != NULL && *version_addendum == '\0') version_addendum = NULL; if ((r = sshbuf_putf(our_version, "SSH-%d.%d-%.100s%s%s\r\n", PROTOCOL_MAJOR_2, PROTOCOL_MINOR_2, SSH_VERSION, version_addendum == NULL ? "" : " ", version_addendum == NULL ? "" : version_addendum)) != 0) { oerrno = errno; error_fr(r, "sshbuf_putf"); goto out; } if (atomicio(vwrite, ssh_packet_get_connection_out(ssh), sshbuf_mutable_ptr(our_version), sshbuf_len(our_version)) != sshbuf_len(our_version)) { oerrno = errno; debug_f("write: %.100s", strerror(errno)); r = SSH_ERR_SYSTEM_ERROR; goto out; } if ((r = sshbuf_consume_end(our_version, 2)) != 0) { /* trim \r\n */ oerrno = errno; error_fr(r, "sshbuf_consume_end"); goto out; } our_version_string = sshbuf_dup_string(our_version); if (our_version_string == NULL) { error_f("sshbuf_dup_string failed"); r = SSH_ERR_ALLOC_FAIL; goto out; } debug("Local version string %.100s", our_version_string); /* Read other side's version identification. */ for (n = 0; ; n++) { if (n >= SSH_MAX_PRE_BANNER_LINES) { send_error(ssh, "No SSH identification string " "received."); error_f("No SSH version received in first %u lines " "from server", SSH_MAX_PRE_BANNER_LINES); r = SSH_ERR_INVALID_FORMAT; goto out; } sshbuf_reset(peer_version); expect_nl = 0; for (;;) { if (timeout_ms > 0) { r = waitrfd(ssh_packet_get_connection_in(ssh), - &timeout_ms); + &timeout_ms, NULL); if (r == -1 && errno == ETIMEDOUT) { send_error(ssh, "Timed out waiting " "for SSH identification string."); error("Connection timed out during " "banner exchange"); r = SSH_ERR_CONN_TIMEOUT; goto out; } else if (r == -1) { oerrno = errno; error_f("%s", strerror(errno)); r = SSH_ERR_SYSTEM_ERROR; goto out; } } len = atomicio(read, ssh_packet_get_connection_in(ssh), &c, 1); if (len != 1 && errno == EPIPE) { - error_f("Connection closed by remote host"); + verbose_f("Connection closed by remote host"); r = SSH_ERR_CONN_CLOSED; goto out; } else if (len != 1) { oerrno = errno; error_f("read: %.100s", strerror(errno)); r = SSH_ERR_SYSTEM_ERROR; goto out; } if (c == '\r') { expect_nl = 1; continue; } if (c == '\n') break; if (c == '\0' || expect_nl) { - error_f("banner line contains invalid " + verbose_f("banner line contains invalid " "characters"); goto invalid; } if ((r = sshbuf_put_u8(peer_version, c)) != 0) { oerrno = errno; error_fr(r, "sshbuf_put"); goto out; } if (sshbuf_len(peer_version) > SSH_MAX_BANNER_LEN) { - error_f("banner line too long"); + verbose_f("banner line too long"); goto invalid; } } /* Is this an actual protocol banner? */ if (sshbuf_len(peer_version) > 4 && memcmp(sshbuf_ptr(peer_version), "SSH-", 4) == 0) break; /* If not, then just log the line and continue */ if ((cp = sshbuf_dup_string(peer_version)) == NULL) { error_f("sshbuf_dup_string failed"); r = SSH_ERR_ALLOC_FAIL; goto out; } /* Do not accept lines before the SSH ident from a client */ if (ssh->kex->server) { - error_f("client sent invalid protocol identifier " + verbose_f("client sent invalid protocol identifier " "\"%.256s\"", cp); free(cp); goto invalid; } debug_f("banner line %zu: %s", n, cp); free(cp); } peer_version_string = sshbuf_dup_string(peer_version); if (peer_version_string == NULL) fatal_f("sshbuf_dup_string failed"); /* XXX must be same size for sscanf */ if ((remote_version = calloc(1, sshbuf_len(peer_version))) == NULL) { error_f("calloc failed"); r = SSH_ERR_ALLOC_FAIL; goto out; } /* * Check that the versions match. In future this might accept * several versions and set appropriate flags to handle them. */ if (sscanf(peer_version_string, "SSH-%d.%d-%[^\n]\n", &remote_major, &remote_minor, remote_version) != 3) { error("Bad remote protocol version identification: '%.100s'", peer_version_string); invalid: send_error(ssh, "Invalid SSH identification string."); r = SSH_ERR_INVALID_FORMAT; goto out; } debug("Remote protocol version %d.%d, remote software version %.100s", remote_major, remote_minor, remote_version); compat_banner(ssh, remote_version); mismatch = 0; switch (remote_major) { case 2: break; case 1: if (remote_minor != 99) mismatch = 1; break; default: mismatch = 1; break; } if (mismatch) { error("Protocol major versions differ: %d vs. %d", PROTOCOL_MAJOR_2, remote_major); send_error(ssh, "Protocol major versions differ."); r = SSH_ERR_NO_PROTOCOL_VERSION; goto out; } if (ssh->kex->server && (ssh->compat & SSH_BUG_PROBE) != 0) { logit("probed from %s port %d with %s. Don't panic.", ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), peer_version_string); r = SSH_ERR_CONN_CLOSED; /* XXX */ goto out; } if (ssh->kex->server && (ssh->compat & SSH_BUG_SCANNER) != 0) { logit("scanned from %s port %d with %s. Don't panic.", ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), peer_version_string); r = SSH_ERR_CONN_CLOSED; /* XXX */ goto out; } /* success */ r = 0; out: free(our_version_string); free(peer_version_string); free(remote_version); if (r == SSH_ERR_SYSTEM_ERROR) errno = oerrno; return r; } diff --git a/kex.h b/kex.h index 8b54e3f4b912..5f7ef784eec9 100644 --- a/kex.h +++ b/kex.h @@ -1,269 +1,270 @@ -/* $OpenBSD: kex.h,v 1.118 2023/03/06 12:14:48 dtucker Exp $ */ +/* $OpenBSD: kex.h,v 1.119 2023/08/28 03:28:43 djm Exp $ */ /* * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef KEX_H #define KEX_H #include "mac.h" #include "crypto_api.h" #ifdef WITH_OPENSSL # include # include # include # ifdef OPENSSL_HAS_ECC # include # else /* OPENSSL_HAS_ECC */ # define EC_KEY void # define EC_GROUP void # define EC_POINT void # endif /* OPENSSL_HAS_ECC */ #else /* WITH_OPENSSL */ # define DH void # define BIGNUM void # define EC_KEY void # define EC_GROUP void # define EC_POINT void #endif /* WITH_OPENSSL */ #define KEX_COOKIE_LEN 16 #define KEX_DH1 "diffie-hellman-group1-sha1" #define KEX_DH14_SHA1 "diffie-hellman-group14-sha1" #define KEX_DH14_SHA256 "diffie-hellman-group14-sha256" #define KEX_DH16_SHA512 "diffie-hellman-group16-sha512" #define KEX_DH18_SHA512 "diffie-hellman-group18-sha512" #define KEX_DHGEX_SHA1 "diffie-hellman-group-exchange-sha1" #define KEX_DHGEX_SHA256 "diffie-hellman-group-exchange-sha256" #define KEX_ECDH_SHA2_NISTP256 "ecdh-sha2-nistp256" #define KEX_ECDH_SHA2_NISTP384 "ecdh-sha2-nistp384" #define KEX_ECDH_SHA2_NISTP521 "ecdh-sha2-nistp521" #define KEX_CURVE25519_SHA256 "curve25519-sha256" #define KEX_CURVE25519_SHA256_OLD "curve25519-sha256@libssh.org" #define KEX_SNTRUP761X25519_SHA512 "sntrup761x25519-sha512@openssh.com" #define COMP_NONE 0 /* pre-auth compression (COMP_ZLIB) is only supported in the client */ #define COMP_ZLIB 1 #define COMP_DELAYED 2 #define CURVE25519_SIZE 32 enum kex_init_proposals { PROPOSAL_KEX_ALGS, PROPOSAL_SERVER_HOST_KEY_ALGS, PROPOSAL_ENC_ALGS_CTOS, PROPOSAL_ENC_ALGS_STOC, PROPOSAL_MAC_ALGS_CTOS, PROPOSAL_MAC_ALGS_STOC, PROPOSAL_COMP_ALGS_CTOS, PROPOSAL_COMP_ALGS_STOC, PROPOSAL_LANG_CTOS, PROPOSAL_LANG_STOC, PROPOSAL_MAX }; enum kex_modes { MODE_IN, MODE_OUT, MODE_MAX }; enum kex_exchange { KEX_DH_GRP1_SHA1, KEX_DH_GRP14_SHA1, KEX_DH_GRP14_SHA256, KEX_DH_GRP16_SHA512, KEX_DH_GRP18_SHA512, KEX_DH_GEX_SHA1, KEX_DH_GEX_SHA256, KEX_ECDH_SHA2, KEX_C25519_SHA256, KEX_KEM_SNTRUP761X25519_SHA512, KEX_MAX }; /* kex->flags */ #define KEX_INIT_SENT 0x0001 #define KEX_INITIAL 0x0002 #define KEX_HAS_PUBKEY_HOSTBOUND 0x0004 #define KEX_RSA_SHA2_256_SUPPORTED 0x0008 /* only set in server for now */ #define KEX_RSA_SHA2_512_SUPPORTED 0x0010 /* only set in server for now */ +#define KEX_HAS_PING 0x0020 struct sshenc { char *name; const struct sshcipher *cipher; int enabled; u_int key_len; u_int iv_len; u_int block_size; u_char *key; u_char *iv; }; struct sshcomp { u_int type; int enabled; char *name; }; struct newkeys { struct sshenc enc; struct sshmac mac; struct sshcomp comp; }; struct ssh; struct sshbuf; struct kex { struct newkeys *newkeys[MODE_MAX]; u_int we_need; u_int dh_need; int server; char *name; char *hostkey_alg; int hostkey_type; int hostkey_nid; u_int kex_type; char *server_sig_algs; int ext_info_c; struct sshbuf *my; struct sshbuf *peer; struct sshbuf *client_version; struct sshbuf *server_version; struct sshbuf *session_id; struct sshbuf *initial_sig; struct sshkey *initial_hostkey; sig_atomic_t done; u_int flags; int hash_alg; int ec_nid; char *failed_choice; int (*verify_host_key)(struct sshkey *, struct ssh *); struct sshkey *(*load_host_public_key)(int, int, struct ssh *); struct sshkey *(*load_host_private_key)(int, int, struct ssh *); int (*host_key_index)(struct sshkey *, int, struct ssh *); int (*sign)(struct ssh *, struct sshkey *, struct sshkey *, u_char **, size_t *, const u_char *, size_t, const char *); int (*kex[KEX_MAX])(struct ssh *); /* kex specific state */ DH *dh; /* DH */ u_int min, max, nbits; /* GEX */ EC_KEY *ec_client_key; /* ECDH */ const EC_GROUP *ec_group; /* ECDH */ u_char c25519_client_key[CURVE25519_SIZE]; /* 25519 + KEM */ u_char c25519_client_pubkey[CURVE25519_SIZE]; /* 25519 */ u_char sntrup761_client_key[crypto_kem_sntrup761_SECRETKEYBYTES]; /* KEM */ struct sshbuf *client_pub; }; int kex_names_valid(const char *); char *kex_alg_list(char); char *kex_names_cat(const char *, const char *); int kex_assemble_names(char **, const char *, const char *); void kex_proposal_populate_entries(struct ssh *, char *prop[PROPOSAL_MAX], const char *, const char *, const char *, const char *, const char *); void kex_proposal_free_entries(char *prop[PROPOSAL_MAX]); int kex_exchange_identification(struct ssh *, int, const char *); struct kex *kex_new(void); int kex_ready(struct ssh *, char *[PROPOSAL_MAX]); int kex_setup(struct ssh *, char *[PROPOSAL_MAX]); void kex_free_newkeys(struct newkeys *); void kex_free(struct kex *); int kex_buf2prop(struct sshbuf *, int *, char ***); int kex_prop2buf(struct sshbuf *, char *proposal[PROPOSAL_MAX]); void kex_prop_free(char **); int kex_load_hostkey(struct ssh *, struct sshkey **, struct sshkey **); int kex_verify_host_key(struct ssh *, struct sshkey *); int kex_send_kexinit(struct ssh *); int kex_input_kexinit(int, u_int32_t, struct ssh *); int kex_input_ext_info(int, u_int32_t, struct ssh *); int kex_protocol_error(int, u_int32_t, struct ssh *); int kex_derive_keys(struct ssh *, u_char *, u_int, const struct sshbuf *); int kex_send_newkeys(struct ssh *); int kex_start_rekex(struct ssh *); int kexgex_client(struct ssh *); int kexgex_server(struct ssh *); int kex_gen_client(struct ssh *); int kex_gen_server(struct ssh *); int kex_dh_keypair(struct kex *); int kex_dh_enc(struct kex *, const struct sshbuf *, struct sshbuf **, struct sshbuf **); int kex_dh_dec(struct kex *, const struct sshbuf *, struct sshbuf **); int kex_ecdh_keypair(struct kex *); int kex_ecdh_enc(struct kex *, const struct sshbuf *, struct sshbuf **, struct sshbuf **); int kex_ecdh_dec(struct kex *, const struct sshbuf *, struct sshbuf **); int kex_c25519_keypair(struct kex *); int kex_c25519_enc(struct kex *, const struct sshbuf *, struct sshbuf **, struct sshbuf **); int kex_c25519_dec(struct kex *, const struct sshbuf *, struct sshbuf **); int kex_kem_sntrup761x25519_keypair(struct kex *); int kex_kem_sntrup761x25519_enc(struct kex *, const struct sshbuf *, struct sshbuf **, struct sshbuf **); int kex_kem_sntrup761x25519_dec(struct kex *, const struct sshbuf *, struct sshbuf **); int kex_dh_keygen(struct kex *); int kex_dh_compute_key(struct kex *, BIGNUM *, struct sshbuf *); int kexgex_hash(int, const struct sshbuf *, const struct sshbuf *, const struct sshbuf *, const struct sshbuf *, const struct sshbuf *, int, int, int, const BIGNUM *, const BIGNUM *, const BIGNUM *, const BIGNUM *, const u_char *, size_t, u_char *, size_t *); void kexc25519_keygen(u_char key[CURVE25519_SIZE], u_char pub[CURVE25519_SIZE]) __attribute__((__bounded__(__minbytes__, 1, CURVE25519_SIZE))) __attribute__((__bounded__(__minbytes__, 2, CURVE25519_SIZE))); int kexc25519_shared_key(const u_char key[CURVE25519_SIZE], const u_char pub[CURVE25519_SIZE], struct sshbuf *out) __attribute__((__bounded__(__minbytes__, 1, CURVE25519_SIZE))) __attribute__((__bounded__(__minbytes__, 2, CURVE25519_SIZE))); int kexc25519_shared_key_ext(const u_char key[CURVE25519_SIZE], const u_char pub[CURVE25519_SIZE], struct sshbuf *out, int) __attribute__((__bounded__(__minbytes__, 1, CURVE25519_SIZE))) __attribute__((__bounded__(__minbytes__, 2, CURVE25519_SIZE))); #if defined(DEBUG_KEX) || defined(DEBUG_KEXDH) || defined(DEBUG_KEXECDH) void dump_digest(const char *, const u_char *, int); #endif #if !defined(WITH_OPENSSL) || !defined(OPENSSL_HAS_ECC) # undef EC_KEY # undef EC_GROUP # undef EC_POINT #endif #endif diff --git a/misc.c b/misc.c index 4b87c4090804..42582c61829a 100644 --- a/misc.c +++ b/misc.c @@ -1,2999 +1,3028 @@ -/* $OpenBSD: misc.c,v 1.185 2023/08/04 06:32:40 dtucker Exp $ */ +/* $OpenBSD: misc.c,v 1.187 2023/08/28 03:31:16 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * Copyright (c) 2005-2020 Damien Miller. All rights reserved. * Copyright (c) 2004 Henning Brauer * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "includes.h" #include #include #include #include #include #include #include #include #include #ifdef HAVE_LIBGEN_H # include #endif #ifdef HAVE_POLL_H #include #endif #ifdef HAVE_NLIST_H #include #endif #include #include #include #ifdef HAVE_STDINT_H # include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_PATHS_H # include #include #include #endif #ifdef SSH_TUN_OPENBSD #include #endif #include "xmalloc.h" #include "misc.h" #include "log.h" #include "ssh.h" #include "sshbuf.h" #include "ssherr.h" #include "platform.h" /* remove newline at end of string */ char * chop(char *s) { char *t = s; while (*t) { if (*t == '\n' || *t == '\r') { *t = '\0'; return s; } t++; } return s; } /* remove whitespace from end of string */ void rtrim(char *s) { size_t i; if ((i = strlen(s)) == 0) return; for (i--; i > 0; i--) { if (isspace((unsigned char)s[i])) s[i] = '\0'; } } /* set/unset filedescriptor to non-blocking */ int set_nonblock(int fd) { int val; val = fcntl(fd, F_GETFL); if (val == -1) { error("fcntl(%d, F_GETFL): %s", fd, strerror(errno)); return (-1); } if (val & O_NONBLOCK) { debug3("fd %d is O_NONBLOCK", fd); return (0); } debug2("fd %d setting O_NONBLOCK", fd); val |= O_NONBLOCK; if (fcntl(fd, F_SETFL, val) == -1) { debug("fcntl(%d, F_SETFL, O_NONBLOCK): %s", fd, strerror(errno)); return (-1); } return (0); } int unset_nonblock(int fd) { int val; val = fcntl(fd, F_GETFL); if (val == -1) { error("fcntl(%d, F_GETFL): %s", fd, strerror(errno)); return (-1); } if (!(val & O_NONBLOCK)) { debug3("fd %d is not O_NONBLOCK", fd); return (0); } debug("fd %d clearing O_NONBLOCK", fd); val &= ~O_NONBLOCK; if (fcntl(fd, F_SETFL, val) == -1) { debug("fcntl(%d, F_SETFL, ~O_NONBLOCK): %s", fd, strerror(errno)); return (-1); } return (0); } const char * ssh_gai_strerror(int gaierr) { if (gaierr == EAI_SYSTEM && errno != 0) return strerror(errno); return gai_strerror(gaierr); } /* disable nagle on socket */ void set_nodelay(int fd) { int opt; socklen_t optlen; optlen = sizeof opt; if (getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, &optlen) == -1) { debug("getsockopt TCP_NODELAY: %.100s", strerror(errno)); return; } if (opt == 1) { debug2("fd %d is TCP_NODELAY", fd); return; } opt = 1; debug2("fd %d setting TCP_NODELAY", fd); if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof opt) == -1) error("setsockopt TCP_NODELAY: %.100s", strerror(errno)); } /* Allow local port reuse in TIME_WAIT */ int set_reuseaddr(int fd) { int on = 1; if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) { error("setsockopt SO_REUSEADDR fd %d: %s", fd, strerror(errno)); return -1; } return 0; } /* Get/set routing domain */ char * get_rdomain(int fd) { #if defined(HAVE_SYS_GET_RDOMAIN) return sys_get_rdomain(fd); #elif defined(__OpenBSD__) int rtable; char *ret; socklen_t len = sizeof(rtable); if (getsockopt(fd, SOL_SOCKET, SO_RTABLE, &rtable, &len) == -1) { error("Failed to get routing domain for fd %d: %s", fd, strerror(errno)); return NULL; } xasprintf(&ret, "%d", rtable); return ret; #else /* defined(__OpenBSD__) */ return NULL; #endif } int set_rdomain(int fd, const char *name) { #if defined(HAVE_SYS_SET_RDOMAIN) return sys_set_rdomain(fd, name); #elif defined(__OpenBSD__) int rtable; const char *errstr; if (name == NULL) return 0; /* default table */ rtable = (int)strtonum(name, 0, 255, &errstr); if (errstr != NULL) { /* Shouldn't happen */ error("Invalid routing domain \"%s\": %s", name, errstr); return -1; } if (setsockopt(fd, SOL_SOCKET, SO_RTABLE, &rtable, sizeof(rtable)) == -1) { error("Failed to set routing domain %d on fd %d: %s", rtable, fd, strerror(errno)); return -1; } return 0; #else /* defined(__OpenBSD__) */ error("Setting routing domain is not supported on this platform"); return -1; #endif } int get_sock_af(int fd) { struct sockaddr_storage to; socklen_t tolen = sizeof(to); memset(&to, 0, sizeof(to)); if (getsockname(fd, (struct sockaddr *)&to, &tolen) == -1) return -1; #ifdef IPV4_IN_IPV6 if (to.ss_family == AF_INET6 && IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)&to)->sin6_addr)) return AF_INET; #endif return to.ss_family; } void set_sock_tos(int fd, int tos) { #ifndef IP_TOS_IS_BROKEN int af; switch ((af = get_sock_af(fd))) { case -1: /* assume not a socket */ break; case AF_INET: # ifdef IP_TOS debug3_f("set socket %d IP_TOS 0x%02x", fd, tos); if (setsockopt(fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) == -1) { error("setsockopt socket %d IP_TOS %d: %s", fd, tos, strerror(errno)); } # endif /* IP_TOS */ break; case AF_INET6: # ifdef IPV6_TCLASS debug3_f("set socket %d IPV6_TCLASS 0x%02x", fd, tos); if (setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof(tos)) == -1) { error("setsockopt socket %d IPV6_TCLASS %d: %s", fd, tos, strerror(errno)); } # endif /* IPV6_TCLASS */ break; default: debug2_f("unsupported socket family %d", af); break; } #endif /* IP_TOS_IS_BROKEN */ } /* * Wait up to *timeoutp milliseconds for events on fd. Updates * *timeoutp with time remaining. * Returns 0 if fd ready or -1 on timeout or error (see errno). */ static int -waitfd(int fd, int *timeoutp, short events) +waitfd(int fd, int *timeoutp, short events, volatile sig_atomic_t *stop) { struct pollfd pfd; - struct timeval t_start; - int oerrno, r, have_timeout = (*timeoutp >= 0); + struct timespec timeout; + int oerrno, r; + sigset_t nsigset, osigset; + if (timeoutp && *timeoutp == -1) + timeoutp = NULL; pfd.fd = fd; pfd.events = events; - for (; !have_timeout || *timeoutp >= 0;) { - monotime_tv(&t_start); - r = poll(&pfd, 1, *timeoutp); + ptimeout_init(&timeout); + if (timeoutp != NULL) + ptimeout_deadline_ms(&timeout, *timeoutp); + if (stop != NULL) + sigfillset(&nsigset); + for (; timeoutp == NULL || *timeoutp >= 0;) { + if (stop != NULL) { + sigprocmask(SIG_BLOCK, &nsigset, &osigset); + if (*stop) { + sigprocmask(SIG_SETMASK, &osigset, NULL); + errno = EINTR; + return -1; + } + } + r = ppoll(&pfd, 1, ptimeout_get_tsp(&timeout), + stop != NULL ? &osigset : NULL); oerrno = errno; - if (have_timeout) - ms_subtract_diff(&t_start, timeoutp); + if (stop != NULL) + sigprocmask(SIG_SETMASK, &osigset, NULL); + if (timeoutp) + *timeoutp = ptimeout_get_ms(&timeout); errno = oerrno; if (r > 0) return 0; else if (r == -1 && errno != EAGAIN && errno != EINTR) return -1; else if (r == 0) break; } /* timeout */ errno = ETIMEDOUT; return -1; } /* * Wait up to *timeoutp milliseconds for fd to be readable. Updates * *timeoutp with time remaining. * Returns 0 if fd ready or -1 on timeout or error (see errno). */ int -waitrfd(int fd, int *timeoutp) { - return waitfd(fd, timeoutp, POLLIN); +waitrfd(int fd, int *timeoutp, volatile sig_atomic_t *stop) { + return waitfd(fd, timeoutp, POLLIN, stop); } /* * Attempt a non-blocking connect(2) to the specified address, waiting up to * *timeoutp milliseconds for the connection to complete. If the timeout is * <=0, then wait indefinitely. * * Returns 0 on success or -1 on failure. */ int timeout_connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen, int *timeoutp) { int optval = 0; socklen_t optlen = sizeof(optval); /* No timeout: just do a blocking connect() */ if (timeoutp == NULL || *timeoutp <= 0) return connect(sockfd, serv_addr, addrlen); set_nonblock(sockfd); for (;;) { if (connect(sockfd, serv_addr, addrlen) == 0) { /* Succeeded already? */ unset_nonblock(sockfd); return 0; } else if (errno == EINTR) continue; else if (errno != EINPROGRESS) return -1; break; } - if (waitfd(sockfd, timeoutp, POLLIN | POLLOUT) == -1) + if (waitfd(sockfd, timeoutp, POLLIN | POLLOUT, NULL) == -1) return -1; /* Completed or failed */ if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval, &optlen) == -1) { debug("getsockopt: %s", strerror(errno)); return -1; } if (optval != 0) { errno = optval; return -1; } unset_nonblock(sockfd); return 0; } /* Characters considered whitespace in strsep calls. */ #define WHITESPACE " \t\r\n" #define QUOTE "\"" /* return next token in configuration line */ static char * strdelim_internal(char **s, int split_equals) { char *old; int wspace = 0; if (*s == NULL) return NULL; old = *s; *s = strpbrk(*s, split_equals ? WHITESPACE QUOTE "=" : WHITESPACE QUOTE); if (*s == NULL) return (old); if (*s[0] == '\"') { memmove(*s, *s + 1, strlen(*s)); /* move nul too */ /* Find matching quote */ if ((*s = strpbrk(*s, QUOTE)) == NULL) { return (NULL); /* no matching quote */ } else { *s[0] = '\0'; *s += strspn(*s + 1, WHITESPACE) + 1; return (old); } } /* Allow only one '=' to be skipped */ if (split_equals && *s[0] == '=') wspace = 1; *s[0] = '\0'; /* Skip any extra whitespace after first token */ *s += strspn(*s + 1, WHITESPACE) + 1; if (split_equals && *s[0] == '=' && !wspace) *s += strspn(*s + 1, WHITESPACE) + 1; return (old); } /* * Return next token in configuration line; splts on whitespace or a * single '=' character. */ char * strdelim(char **s) { return strdelim_internal(s, 1); } /* * Return next token in configuration line; splts on whitespace only. */ char * strdelimw(char **s) { return strdelim_internal(s, 0); } struct passwd * pwcopy(struct passwd *pw) { struct passwd *copy = xcalloc(1, sizeof(*copy)); copy->pw_name = xstrdup(pw->pw_name); copy->pw_passwd = xstrdup(pw->pw_passwd == NULL ? "*" : pw->pw_passwd); #ifdef HAVE_STRUCT_PASSWD_PW_GECOS copy->pw_gecos = xstrdup(pw->pw_gecos); #endif copy->pw_uid = pw->pw_uid; copy->pw_gid = pw->pw_gid; #ifdef HAVE_STRUCT_PASSWD_PW_EXPIRE copy->pw_expire = pw->pw_expire; #endif #ifdef HAVE_STRUCT_PASSWD_PW_CHANGE copy->pw_change = pw->pw_change; #endif #ifdef HAVE_STRUCT_PASSWD_PW_CLASS copy->pw_class = xstrdup(pw->pw_class); #endif copy->pw_dir = xstrdup(pw->pw_dir); copy->pw_shell = xstrdup(pw->pw_shell); return copy; } /* * Convert ASCII string to TCP/IP port number. * Port must be >=0 and <=65535. * Return -1 if invalid. */ int a2port(const char *s) { struct servent *se; long long port; const char *errstr; port = strtonum(s, 0, 65535, &errstr); if (errstr == NULL) return (int)port; if ((se = getservbyname(s, "tcp")) != NULL) return ntohs(se->s_port); return -1; } int a2tun(const char *s, int *remote) { const char *errstr = NULL; char *sp, *ep; int tun; if (remote != NULL) { *remote = SSH_TUNID_ANY; sp = xstrdup(s); if ((ep = strchr(sp, ':')) == NULL) { free(sp); return (a2tun(s, NULL)); } ep[0] = '\0'; ep++; *remote = a2tun(ep, NULL); tun = a2tun(sp, NULL); free(sp); return (*remote == SSH_TUNID_ERR ? *remote : tun); } if (strcasecmp(s, "any") == 0) return (SSH_TUNID_ANY); tun = strtonum(s, 0, SSH_TUNID_MAX, &errstr); if (errstr != NULL) return (SSH_TUNID_ERR); return (tun); } #define SECONDS 1 #define MINUTES (SECONDS * 60) #define HOURS (MINUTES * 60) #define DAYS (HOURS * 24) #define WEEKS (DAYS * 7) /* * Convert a time string into seconds; format is * a sequence of: * time[qualifier] * * Valid time qualifiers are: * seconds * s|S seconds * m|M minutes * h|H hours * d|D days * w|W weeks * * Examples: * 90m 90 minutes * 1h30m 90 minutes * 2d 2 days * 1w 1 week * * Return -1 if time string is invalid. */ int convtime(const char *s) { long total, secs, multiplier; const char *p; char *endp; errno = 0; total = 0; p = s; if (p == NULL || *p == '\0') return -1; while (*p) { secs = strtol(p, &endp, 10); if (p == endp || (errno == ERANGE && (secs == INT_MIN || secs == INT_MAX)) || secs < 0) return -1; multiplier = 1; switch (*endp++) { case '\0': endp--; break; case 's': case 'S': break; case 'm': case 'M': multiplier = MINUTES; break; case 'h': case 'H': multiplier = HOURS; break; case 'd': case 'D': multiplier = DAYS; break; case 'w': case 'W': multiplier = WEEKS; break; default: return -1; } if (secs > INT_MAX / multiplier) return -1; secs *= multiplier; if (total > INT_MAX - secs) return -1; total += secs; if (total < 0) return -1; p = endp; } return total; } #define TF_BUFS 8 #define TF_LEN 9 const char * fmt_timeframe(time_t t) { char *buf; static char tfbuf[TF_BUFS][TF_LEN]; /* ring buffer */ static int idx = 0; unsigned int sec, min, hrs, day; unsigned long long week; buf = tfbuf[idx++]; if (idx == TF_BUFS) idx = 0; week = t; sec = week % 60; week /= 60; min = week % 60; week /= 60; hrs = week % 24; week /= 24; day = week % 7; week /= 7; if (week > 0) snprintf(buf, TF_LEN, "%02lluw%01ud%02uh", week, day, hrs); else if (day > 0) snprintf(buf, TF_LEN, "%01ud%02uh%02um", day, hrs, min); else snprintf(buf, TF_LEN, "%02u:%02u:%02u", hrs, min, sec); return (buf); } /* * Returns a standardized host+port identifier string. * Caller must free returned string. */ char * put_host_port(const char *host, u_short port) { char *hoststr; if (port == 0 || port == SSH_DEFAULT_PORT) return(xstrdup(host)); if (asprintf(&hoststr, "[%s]:%d", host, (int)port) == -1) fatal("put_host_port: asprintf: %s", strerror(errno)); debug3("put_host_port: %s", hoststr); return hoststr; } /* * Search for next delimiter between hostnames/addresses and ports. * Argument may be modified (for termination). * Returns *cp if parsing succeeds. * *cp is set to the start of the next field, if one was found. * The delimiter char, if present, is stored in delim. * If this is the last field, *cp is set to NULL. */ char * hpdelim2(char **cp, char *delim) { char *s, *old; if (cp == NULL || *cp == NULL) return NULL; old = s = *cp; if (*s == '[') { if ((s = strchr(s, ']')) == NULL) return NULL; else s++; } else if ((s = strpbrk(s, ":/")) == NULL) s = *cp + strlen(*cp); /* skip to end (see first case below) */ switch (*s) { case '\0': *cp = NULL; /* no more fields*/ break; case ':': case '/': if (delim != NULL) *delim = *s; *s = '\0'; /* terminate */ *cp = s + 1; break; default: return NULL; } return old; } /* The common case: only accept colon as delimiter. */ char * hpdelim(char **cp) { char *r, delim = '\0'; r = hpdelim2(cp, &delim); if (delim == '/') return NULL; return r; } char * cleanhostname(char *host) { if (*host == '[' && host[strlen(host) - 1] == ']') { host[strlen(host) - 1] = '\0'; return (host + 1); } else return host; } char * colon(char *cp) { int flag = 0; if (*cp == ':') /* Leading colon is part of file name. */ return NULL; if (*cp == '[') flag = 1; for (; *cp; ++cp) { if (*cp == '@' && *(cp+1) == '[') flag = 1; if (*cp == ']' && *(cp+1) == ':' && flag) return (cp+1); if (*cp == ':' && !flag) return (cp); if (*cp == '/') return NULL; } return NULL; } /* * Parse a [user@]host:[path] string. * Caller must free returned user, host and path. * Any of the pointer return arguments may be NULL (useful for syntax checking). * If user was not specified then *userp will be set to NULL. * If host was not specified then *hostp will be set to NULL. * If path was not specified then *pathp will be set to ".". * Returns 0 on success, -1 on failure. */ int parse_user_host_path(const char *s, char **userp, char **hostp, char **pathp) { char *user = NULL, *host = NULL, *path = NULL; char *sdup, *tmp; int ret = -1; if (userp != NULL) *userp = NULL; if (hostp != NULL) *hostp = NULL; if (pathp != NULL) *pathp = NULL; sdup = xstrdup(s); /* Check for remote syntax: [user@]host:[path] */ if ((tmp = colon(sdup)) == NULL) goto out; /* Extract optional path */ *tmp++ = '\0'; if (*tmp == '\0') tmp = "."; path = xstrdup(tmp); /* Extract optional user and mandatory host */ tmp = strrchr(sdup, '@'); if (tmp != NULL) { *tmp++ = '\0'; host = xstrdup(cleanhostname(tmp)); if (*sdup != '\0') user = xstrdup(sdup); } else { host = xstrdup(cleanhostname(sdup)); user = NULL; } /* Success */ if (userp != NULL) { *userp = user; user = NULL; } if (hostp != NULL) { *hostp = host; host = NULL; } if (pathp != NULL) { *pathp = path; path = NULL; } ret = 0; out: free(sdup); free(user); free(host); free(path); return ret; } /* * Parse a [user@]host[:port] string. * Caller must free returned user and host. * Any of the pointer return arguments may be NULL (useful for syntax checking). * If user was not specified then *userp will be set to NULL. * If port was not specified then *portp will be -1. * Returns 0 on success, -1 on failure. */ int parse_user_host_port(const char *s, char **userp, char **hostp, int *portp) { char *sdup, *cp, *tmp; char *user = NULL, *host = NULL; int port = -1, ret = -1; if (userp != NULL) *userp = NULL; if (hostp != NULL) *hostp = NULL; if (portp != NULL) *portp = -1; if ((sdup = tmp = strdup(s)) == NULL) return -1; /* Extract optional username */ if ((cp = strrchr(tmp, '@')) != NULL) { *cp = '\0'; if (*tmp == '\0') goto out; if ((user = strdup(tmp)) == NULL) goto out; tmp = cp + 1; } /* Extract mandatory hostname */ if ((cp = hpdelim(&tmp)) == NULL || *cp == '\0') goto out; host = xstrdup(cleanhostname(cp)); /* Convert and verify optional port */ if (tmp != NULL && *tmp != '\0') { if ((port = a2port(tmp)) <= 0) goto out; } /* Success */ if (userp != NULL) { *userp = user; user = NULL; } if (hostp != NULL) { *hostp = host; host = NULL; } if (portp != NULL) *portp = port; ret = 0; out: free(sdup); free(user); free(host); return ret; } /* * Converts a two-byte hex string to decimal. * Returns the decimal value or -1 for invalid input. */ static int hexchar(const char *s) { unsigned char result[2]; int i; for (i = 0; i < 2; i++) { if (s[i] >= '0' && s[i] <= '9') result[i] = (unsigned char)(s[i] - '0'); else if (s[i] >= 'a' && s[i] <= 'f') result[i] = (unsigned char)(s[i] - 'a') + 10; else if (s[i] >= 'A' && s[i] <= 'F') result[i] = (unsigned char)(s[i] - 'A') + 10; else return -1; } return (result[0] << 4) | result[1]; } /* * Decode an url-encoded string. * Returns a newly allocated string on success or NULL on failure. */ static char * urldecode(const char *src) { char *ret, *dst; int ch; size_t srclen; if ((srclen = strlen(src)) >= SIZE_MAX) fatal_f("input too large"); ret = xmalloc(srclen + 1); for (dst = ret; *src != '\0'; src++) { switch (*src) { case '+': *dst++ = ' '; break; case '%': if (!isxdigit((unsigned char)src[1]) || !isxdigit((unsigned char)src[2]) || (ch = hexchar(src + 1)) == -1) { free(ret); return NULL; } *dst++ = ch; src += 2; break; default: *dst++ = *src; break; } } *dst = '\0'; return ret; } /* * Parse an (scp|ssh|sftp)://[user@]host[:port][/path] URI. * See https://tools.ietf.org/html/draft-ietf-secsh-scp-sftp-ssh-uri-04 * Either user or path may be url-encoded (but not host or port). * Caller must free returned user, host and path. * Any of the pointer return arguments may be NULL (useful for syntax checking) * but the scheme must always be specified. * If user was not specified then *userp will be set to NULL. * If port was not specified then *portp will be -1. * If path was not specified then *pathp will be set to NULL. * Returns 0 on success, 1 if non-uri/wrong scheme, -1 on error/invalid uri. */ int parse_uri(const char *scheme, const char *uri, char **userp, char **hostp, int *portp, char **pathp) { char *uridup, *cp, *tmp, ch; char *user = NULL, *host = NULL, *path = NULL; int port = -1, ret = -1; size_t len; len = strlen(scheme); if (strncmp(uri, scheme, len) != 0 || strncmp(uri + len, "://", 3) != 0) return 1; uri += len + 3; if (userp != NULL) *userp = NULL; if (hostp != NULL) *hostp = NULL; if (portp != NULL) *portp = -1; if (pathp != NULL) *pathp = NULL; uridup = tmp = xstrdup(uri); /* Extract optional ssh-info (username + connection params) */ if ((cp = strchr(tmp, '@')) != NULL) { char *delim; *cp = '\0'; /* Extract username and connection params */ if ((delim = strchr(tmp, ';')) != NULL) { /* Just ignore connection params for now */ *delim = '\0'; } if (*tmp == '\0') { /* Empty username */ goto out; } if ((user = urldecode(tmp)) == NULL) goto out; tmp = cp + 1; } /* Extract mandatory hostname */ if ((cp = hpdelim2(&tmp, &ch)) == NULL || *cp == '\0') goto out; host = xstrdup(cleanhostname(cp)); if (!valid_domain(host, 0, NULL)) goto out; if (tmp != NULL && *tmp != '\0') { if (ch == ':') { /* Convert and verify port. */ if ((cp = strchr(tmp, '/')) != NULL) *cp = '\0'; if ((port = a2port(tmp)) <= 0) goto out; tmp = cp ? cp + 1 : NULL; } if (tmp != NULL && *tmp != '\0') { /* Extract optional path */ if ((path = urldecode(tmp)) == NULL) goto out; } } /* Success */ if (userp != NULL) { *userp = user; user = NULL; } if (hostp != NULL) { *hostp = host; host = NULL; } if (portp != NULL) *portp = port; if (pathp != NULL) { *pathp = path; path = NULL; } ret = 0; out: free(uridup); free(user); free(host); free(path); return ret; } /* function to assist building execv() arguments */ void addargs(arglist *args, char *fmt, ...) { va_list ap; char *cp; u_int nalloc; int r; va_start(ap, fmt); r = vasprintf(&cp, fmt, ap); va_end(ap); if (r == -1) fatal_f("argument too long"); nalloc = args->nalloc; if (args->list == NULL) { nalloc = 32; args->num = 0; } else if (args->num > (256 * 1024)) fatal_f("too many arguments"); else if (args->num >= args->nalloc) fatal_f("arglist corrupt"); else if (args->num+2 >= nalloc) nalloc *= 2; args->list = xrecallocarray(args->list, args->nalloc, nalloc, sizeof(char *)); args->nalloc = nalloc; args->list[args->num++] = cp; args->list[args->num] = NULL; } void replacearg(arglist *args, u_int which, char *fmt, ...) { va_list ap; char *cp; int r; va_start(ap, fmt); r = vasprintf(&cp, fmt, ap); va_end(ap); if (r == -1) fatal_f("argument too long"); if (args->list == NULL || args->num >= args->nalloc) fatal_f("arglist corrupt"); if (which >= args->num) fatal_f("tried to replace invalid arg %d >= %d", which, args->num); free(args->list[which]); args->list[which] = cp; } void freeargs(arglist *args) { u_int i; if (args == NULL) return; if (args->list != NULL && args->num < args->nalloc) { for (i = 0; i < args->num; i++) free(args->list[i]); free(args->list); } args->nalloc = args->num = 0; args->list = NULL; } /* * Expands tildes in the file name. Returns data allocated by xmalloc. * Warning: this calls getpw*. */ int tilde_expand(const char *filename, uid_t uid, char **retp) { char *ocopy = NULL, *copy, *s = NULL; const char *path = NULL, *user = NULL; struct passwd *pw; size_t len; int ret = -1, r, slash; *retp = NULL; if (*filename != '~') { *retp = xstrdup(filename); return 0; } ocopy = copy = xstrdup(filename + 1); if (*copy == '\0') /* ~ */ path = NULL; else if (*copy == '/') { copy += strspn(copy, "/"); if (*copy == '\0') path = NULL; /* ~/ */ else path = copy; /* ~/path */ } else { user = copy; if ((path = strchr(copy, '/')) != NULL) { copy[path - copy] = '\0'; path++; path += strspn(path, "/"); if (*path == '\0') /* ~user/ */ path = NULL; /* else ~user/path */ } /* else ~user */ } if (user != NULL) { if ((pw = getpwnam(user)) == NULL) { error_f("No such user %s", user); goto out; } } else if ((pw = getpwuid(uid)) == NULL) { error_f("No such uid %ld", (long)uid); goto out; } /* Make sure directory has a trailing '/' */ slash = (len = strlen(pw->pw_dir)) == 0 || pw->pw_dir[len - 1] != '/'; if ((r = xasprintf(&s, "%s%s%s", pw->pw_dir, slash ? "/" : "", path != NULL ? path : "")) <= 0) { error_f("xasprintf failed"); goto out; } if (r >= PATH_MAX) { error_f("Path too long"); goto out; } /* success */ ret = 0; *retp = s; s = NULL; out: free(s); free(ocopy); return ret; } char * tilde_expand_filename(const char *filename, uid_t uid) { char *ret; if (tilde_expand(filename, uid, &ret) != 0) cleanup_exit(255); return ret; } /* * Expand a string with a set of %[char] escapes and/or ${ENVIRONMENT} * substitutions. A number of escapes may be specified as * (char *escape_chars, char *replacement) pairs. The list must be terminated * by a NULL escape_char. Returns replaced string in memory allocated by * xmalloc which the caller must free. */ static char * vdollar_percent_expand(int *parseerror, int dollar, int percent, const char *string, va_list ap) { #define EXPAND_MAX_KEYS 16 u_int num_keys = 0, i; struct { const char *key; const char *repl; } keys[EXPAND_MAX_KEYS]; struct sshbuf *buf; int r, missingvar = 0; char *ret = NULL, *var, *varend, *val; size_t len; if ((buf = sshbuf_new()) == NULL) fatal_f("sshbuf_new failed"); if (parseerror == NULL) fatal_f("null parseerror arg"); *parseerror = 1; /* Gather keys if we're doing percent expansion. */ if (percent) { for (num_keys = 0; num_keys < EXPAND_MAX_KEYS; num_keys++) { keys[num_keys].key = va_arg(ap, char *); if (keys[num_keys].key == NULL) break; keys[num_keys].repl = va_arg(ap, char *); if (keys[num_keys].repl == NULL) { fatal_f("NULL replacement for token %s", keys[num_keys].key); } } if (num_keys == EXPAND_MAX_KEYS && va_arg(ap, char *) != NULL) fatal_f("too many keys"); if (num_keys == 0) fatal_f("percent expansion without token list"); } /* Expand string */ for (i = 0; *string != '\0'; string++) { /* Optionally process ${ENVIRONMENT} expansions. */ if (dollar && string[0] == '$' && string[1] == '{') { string += 2; /* skip over '${' */ if ((varend = strchr(string, '}')) == NULL) { error_f("environment variable '%s' missing " "closing '}'", string); goto out; } len = varend - string; if (len == 0) { error_f("zero-length environment variable"); goto out; } var = xmalloc(len + 1); (void)strlcpy(var, string, len + 1); if ((val = getenv(var)) == NULL) { error_f("env var ${%s} has no value", var); missingvar = 1; } else { debug3_f("expand ${%s} -> '%s'", var, val); if ((r = sshbuf_put(buf, val, strlen(val))) !=0) fatal_fr(r, "sshbuf_put ${}"); } free(var); string += len; continue; } /* * Process percent expansions if we have a list of TOKENs. * If we're not doing percent expansion everything just gets * appended here. */ if (*string != '%' || !percent) { append: if ((r = sshbuf_put_u8(buf, *string)) != 0) fatal_fr(r, "sshbuf_put_u8 %%"); continue; } string++; /* %% case */ if (*string == '%') goto append; if (*string == '\0') { error_f("invalid format"); goto out; } for (i = 0; i < num_keys; i++) { if (strchr(keys[i].key, *string) != NULL) { if ((r = sshbuf_put(buf, keys[i].repl, strlen(keys[i].repl))) != 0) fatal_fr(r, "sshbuf_put %%-repl"); break; } } if (i >= num_keys) { error_f("unknown key %%%c", *string); goto out; } } if (!missingvar && (ret = sshbuf_dup_string(buf)) == NULL) fatal_f("sshbuf_dup_string failed"); *parseerror = 0; out: sshbuf_free(buf); return *parseerror ? NULL : ret; #undef EXPAND_MAX_KEYS } /* * Expand only environment variables. * Note that although this function is variadic like the other similar * functions, any such arguments will be unused. */ char * dollar_expand(int *parseerr, const char *string, ...) { char *ret; int err; va_list ap; va_start(ap, string); ret = vdollar_percent_expand(&err, 1, 0, string, ap); va_end(ap); if (parseerr != NULL) *parseerr = err; return ret; } /* * Returns expanded string or NULL if a specified environment variable is * not defined, or calls fatal if the string is invalid. */ char * percent_expand(const char *string, ...) { char *ret; int err; va_list ap; va_start(ap, string); ret = vdollar_percent_expand(&err, 0, 1, string, ap); va_end(ap); if (err) fatal_f("failed"); return ret; } /* * Returns expanded string or NULL if a specified environment variable is * not defined, or calls fatal if the string is invalid. */ char * percent_dollar_expand(const char *string, ...) { char *ret; int err; va_list ap; va_start(ap, string); ret = vdollar_percent_expand(&err, 1, 1, string, ap); va_end(ap); if (err) fatal_f("failed"); return ret; } int tun_open(int tun, int mode, char **ifname) { #if defined(CUSTOM_SYS_TUN_OPEN) return (sys_tun_open(tun, mode, ifname)); #elif defined(SSH_TUN_OPENBSD) struct ifreq ifr; char name[100]; int fd = -1, sock; const char *tunbase = "tun"; if (ifname != NULL) *ifname = NULL; if (mode == SSH_TUNMODE_ETHERNET) tunbase = "tap"; /* Open the tunnel device */ if (tun <= SSH_TUNID_MAX) { snprintf(name, sizeof(name), "/dev/%s%d", tunbase, tun); fd = open(name, O_RDWR); } else if (tun == SSH_TUNID_ANY) { for (tun = 100; tun >= 0; tun--) { snprintf(name, sizeof(name), "/dev/%s%d", tunbase, tun); if ((fd = open(name, O_RDWR)) >= 0) break; } } else { debug_f("invalid tunnel %u", tun); return -1; } if (fd == -1) { debug_f("%s open: %s", name, strerror(errno)); return -1; } debug_f("%s mode %d fd %d", name, mode, fd); /* Bring interface up if it is not already */ snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s%d", tunbase, tun); if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) goto failed; if (ioctl(sock, SIOCGIFFLAGS, &ifr) == -1) { debug_f("get interface %s flags: %s", ifr.ifr_name, strerror(errno)); goto failed; } if (!(ifr.ifr_flags & IFF_UP)) { ifr.ifr_flags |= IFF_UP; if (ioctl(sock, SIOCSIFFLAGS, &ifr) == -1) { debug_f("activate interface %s: %s", ifr.ifr_name, strerror(errno)); goto failed; } } if (ifname != NULL) *ifname = xstrdup(ifr.ifr_name); close(sock); return fd; failed: if (fd >= 0) close(fd); if (sock >= 0) close(sock); return -1; #else error("Tunnel interfaces are not supported on this platform"); return (-1); #endif } void sanitise_stdfd(void) { int nullfd, dupfd; if ((nullfd = dupfd = open(_PATH_DEVNULL, O_RDWR)) == -1) { fprintf(stderr, "Couldn't open /dev/null: %s\n", strerror(errno)); exit(1); } while (++dupfd <= STDERR_FILENO) { /* Only populate closed fds. */ if (fcntl(dupfd, F_GETFL) == -1 && errno == EBADF) { if (dup2(nullfd, dupfd) == -1) { fprintf(stderr, "dup2: %s\n", strerror(errno)); exit(1); } } } if (nullfd > STDERR_FILENO) close(nullfd); } char * tohex(const void *vp, size_t l) { const u_char *p = (const u_char *)vp; char b[3], *r; size_t i, hl; if (l > 65536) return xstrdup("tohex: length > 65536"); hl = l * 2 + 1; r = xcalloc(1, hl); for (i = 0; i < l; i++) { snprintf(b, sizeof(b), "%02x", p[i]); strlcat(r, b, hl); } return (r); } /* * Extend string *sp by the specified format. If *sp is not NULL (or empty), * then the separator 'sep' will be prepended before the formatted arguments. * Extended strings are heap allocated. */ void xextendf(char **sp, const char *sep, const char *fmt, ...) { va_list ap; char *tmp1, *tmp2; va_start(ap, fmt); xvasprintf(&tmp1, fmt, ap); va_end(ap); if (*sp == NULL || **sp == '\0') { free(*sp); *sp = tmp1; return; } xasprintf(&tmp2, "%s%s%s", *sp, sep == NULL ? "" : sep, tmp1); free(tmp1); free(*sp); *sp = tmp2; } u_int64_t get_u64(const void *vp) { const u_char *p = (const u_char *)vp; u_int64_t v; v = (u_int64_t)p[0] << 56; v |= (u_int64_t)p[1] << 48; v |= (u_int64_t)p[2] << 40; v |= (u_int64_t)p[3] << 32; v |= (u_int64_t)p[4] << 24; v |= (u_int64_t)p[5] << 16; v |= (u_int64_t)p[6] << 8; v |= (u_int64_t)p[7]; return (v); } u_int32_t get_u32(const void *vp) { const u_char *p = (const u_char *)vp; u_int32_t v; v = (u_int32_t)p[0] << 24; v |= (u_int32_t)p[1] << 16; v |= (u_int32_t)p[2] << 8; v |= (u_int32_t)p[3]; return (v); } u_int32_t get_u32_le(const void *vp) { const u_char *p = (const u_char *)vp; u_int32_t v; v = (u_int32_t)p[0]; v |= (u_int32_t)p[1] << 8; v |= (u_int32_t)p[2] << 16; v |= (u_int32_t)p[3] << 24; return (v); } u_int16_t get_u16(const void *vp) { const u_char *p = (const u_char *)vp; u_int16_t v; v = (u_int16_t)p[0] << 8; v |= (u_int16_t)p[1]; return (v); } void put_u64(void *vp, u_int64_t v) { u_char *p = (u_char *)vp; p[0] = (u_char)(v >> 56) & 0xff; p[1] = (u_char)(v >> 48) & 0xff; p[2] = (u_char)(v >> 40) & 0xff; p[3] = (u_char)(v >> 32) & 0xff; p[4] = (u_char)(v >> 24) & 0xff; p[5] = (u_char)(v >> 16) & 0xff; p[6] = (u_char)(v >> 8) & 0xff; p[7] = (u_char)v & 0xff; } void put_u32(void *vp, u_int32_t v) { u_char *p = (u_char *)vp; p[0] = (u_char)(v >> 24) & 0xff; p[1] = (u_char)(v >> 16) & 0xff; p[2] = (u_char)(v >> 8) & 0xff; p[3] = (u_char)v & 0xff; } void put_u32_le(void *vp, u_int32_t v) { u_char *p = (u_char *)vp; p[0] = (u_char)v & 0xff; p[1] = (u_char)(v >> 8) & 0xff; p[2] = (u_char)(v >> 16) & 0xff; p[3] = (u_char)(v >> 24) & 0xff; } void put_u16(void *vp, u_int16_t v) { u_char *p = (u_char *)vp; p[0] = (u_char)(v >> 8) & 0xff; p[1] = (u_char)v & 0xff; } void ms_subtract_diff(struct timeval *start, int *ms) { struct timeval diff, finish; monotime_tv(&finish); timersub(&finish, start, &diff); *ms -= (diff.tv_sec * 1000) + (diff.tv_usec / 1000); } void ms_to_timespec(struct timespec *ts, int ms) { if (ms < 0) ms = 0; ts->tv_sec = ms / 1000; ts->tv_nsec = (ms % 1000) * 1000 * 1000; } void monotime_ts(struct timespec *ts) { struct timeval tv; #if defined(HAVE_CLOCK_GETTIME) && (defined(CLOCK_BOOTTIME) || \ defined(CLOCK_MONOTONIC) || defined(CLOCK_REALTIME)) static int gettime_failed = 0; if (!gettime_failed) { # ifdef CLOCK_BOOTTIME if (clock_gettime(CLOCK_BOOTTIME, ts) == 0) return; # endif /* CLOCK_BOOTTIME */ # ifdef CLOCK_MONOTONIC if (clock_gettime(CLOCK_MONOTONIC, ts) == 0) return; # endif /* CLOCK_MONOTONIC */ # ifdef CLOCK_REALTIME /* Not monotonic, but we're almost out of options here. */ if (clock_gettime(CLOCK_REALTIME, ts) == 0) return; # endif /* CLOCK_REALTIME */ debug3("clock_gettime: %s", strerror(errno)); gettime_failed = 1; } #endif /* HAVE_CLOCK_GETTIME && (BOOTTIME || MONOTONIC || REALTIME) */ gettimeofday(&tv, NULL); ts->tv_sec = tv.tv_sec; ts->tv_nsec = (long)tv.tv_usec * 1000; } void monotime_tv(struct timeval *tv) { struct timespec ts; monotime_ts(&ts); tv->tv_sec = ts.tv_sec; tv->tv_usec = ts.tv_nsec / 1000; } time_t monotime(void) { struct timespec ts; monotime_ts(&ts); return ts.tv_sec; } double monotime_double(void) { struct timespec ts; monotime_ts(&ts); return ts.tv_sec + ((double)ts.tv_nsec / 1000000000); } void bandwidth_limit_init(struct bwlimit *bw, u_int64_t kbps, size_t buflen) { bw->buflen = buflen; bw->rate = kbps; bw->thresh = buflen; bw->lamt = 0; timerclear(&bw->bwstart); timerclear(&bw->bwend); } /* Callback from read/write loop to insert bandwidth-limiting delays */ void bandwidth_limit(struct bwlimit *bw, size_t read_len) { u_int64_t waitlen; struct timespec ts, rm; bw->lamt += read_len; if (!timerisset(&bw->bwstart)) { monotime_tv(&bw->bwstart); return; } if (bw->lamt < bw->thresh) return; monotime_tv(&bw->bwend); timersub(&bw->bwend, &bw->bwstart, &bw->bwend); if (!timerisset(&bw->bwend)) return; bw->lamt *= 8; waitlen = (double)1000000L * bw->lamt / bw->rate; bw->bwstart.tv_sec = waitlen / 1000000L; bw->bwstart.tv_usec = waitlen % 1000000L; if (timercmp(&bw->bwstart, &bw->bwend, >)) { timersub(&bw->bwstart, &bw->bwend, &bw->bwend); /* Adjust the wait time */ if (bw->bwend.tv_sec) { bw->thresh /= 2; if (bw->thresh < bw->buflen / 4) bw->thresh = bw->buflen / 4; } else if (bw->bwend.tv_usec < 10000) { bw->thresh *= 2; if (bw->thresh > bw->buflen * 8) bw->thresh = bw->buflen * 8; } TIMEVAL_TO_TIMESPEC(&bw->bwend, &ts); while (nanosleep(&ts, &rm) == -1) { if (errno != EINTR) break; ts = rm; } } bw->lamt = 0; monotime_tv(&bw->bwstart); } /* Make a template filename for mk[sd]temp() */ void mktemp_proto(char *s, size_t len) { const char *tmpdir; int r; if ((tmpdir = getenv("TMPDIR")) != NULL) { r = snprintf(s, len, "%s/ssh-XXXXXXXXXXXX", tmpdir); if (r > 0 && (size_t)r < len) return; } r = snprintf(s, len, "/tmp/ssh-XXXXXXXXXXXX"); if (r < 0 || (size_t)r >= len) fatal_f("template string too short"); } static const struct { const char *name; int value; } ipqos[] = { { "none", INT_MAX }, /* can't use 0 here; that's CS0 */ { "af11", IPTOS_DSCP_AF11 }, { "af12", IPTOS_DSCP_AF12 }, { "af13", IPTOS_DSCP_AF13 }, { "af21", IPTOS_DSCP_AF21 }, { "af22", IPTOS_DSCP_AF22 }, { "af23", IPTOS_DSCP_AF23 }, { "af31", IPTOS_DSCP_AF31 }, { "af32", IPTOS_DSCP_AF32 }, { "af33", IPTOS_DSCP_AF33 }, { "af41", IPTOS_DSCP_AF41 }, { "af42", IPTOS_DSCP_AF42 }, { "af43", IPTOS_DSCP_AF43 }, { "cs0", IPTOS_DSCP_CS0 }, { "cs1", IPTOS_DSCP_CS1 }, { "cs2", IPTOS_DSCP_CS2 }, { "cs3", IPTOS_DSCP_CS3 }, { "cs4", IPTOS_DSCP_CS4 }, { "cs5", IPTOS_DSCP_CS5 }, { "cs6", IPTOS_DSCP_CS6 }, { "cs7", IPTOS_DSCP_CS7 }, { "ef", IPTOS_DSCP_EF }, { "le", IPTOS_DSCP_LE }, { "lowdelay", IPTOS_LOWDELAY }, { "throughput", IPTOS_THROUGHPUT }, { "reliability", IPTOS_RELIABILITY }, { NULL, -1 } }; int parse_ipqos(const char *cp) { u_int i; char *ep; long val; if (cp == NULL) return -1; for (i = 0; ipqos[i].name != NULL; i++) { if (strcasecmp(cp, ipqos[i].name) == 0) return ipqos[i].value; } /* Try parsing as an integer */ val = strtol(cp, &ep, 0); if (*cp == '\0' || *ep != '\0' || val < 0 || val > 255) return -1; return val; } const char * iptos2str(int iptos) { int i; static char iptos_str[sizeof "0xff"]; for (i = 0; ipqos[i].name != NULL; i++) { if (ipqos[i].value == iptos) return ipqos[i].name; } snprintf(iptos_str, sizeof iptos_str, "0x%02x", iptos); return iptos_str; } void lowercase(char *s) { for (; *s; s++) *s = tolower((u_char)*s); } int unix_listener(const char *path, int backlog, int unlink_first) { struct sockaddr_un sunaddr; int saved_errno, sock; memset(&sunaddr, 0, sizeof(sunaddr)); sunaddr.sun_family = AF_UNIX; if (strlcpy(sunaddr.sun_path, path, sizeof(sunaddr.sun_path)) >= sizeof(sunaddr.sun_path)) { error_f("path \"%s\" too long for Unix domain socket", path); errno = ENAMETOOLONG; return -1; } sock = socket(PF_UNIX, SOCK_STREAM, 0); if (sock == -1) { saved_errno = errno; error_f("socket: %.100s", strerror(errno)); errno = saved_errno; return -1; } if (unlink_first == 1) { if (unlink(path) != 0 && errno != ENOENT) error("unlink(%s): %.100s", path, strerror(errno)); } if (bind(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) == -1) { saved_errno = errno; error_f("cannot bind to path %s: %s", path, strerror(errno)); close(sock); errno = saved_errno; return -1; } if (listen(sock, backlog) == -1) { saved_errno = errno; error_f("cannot listen on path %s: %s", path, strerror(errno)); close(sock); unlink(path); errno = saved_errno; return -1; } return sock; } void sock_set_v6only(int s) { #if defined(IPV6_V6ONLY) && !defined(__OpenBSD__) int on = 1; debug3("%s: set socket %d IPV6_V6ONLY", __func__, s); if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) == -1) error("setsockopt IPV6_V6ONLY: %s", strerror(errno)); #endif } /* * Compares two strings that maybe be NULL. Returns non-zero if strings * are both NULL or are identical, returns zero otherwise. */ static int strcmp_maybe_null(const char *a, const char *b) { if ((a == NULL && b != NULL) || (a != NULL && b == NULL)) return 0; if (a != NULL && strcmp(a, b) != 0) return 0; return 1; } /* * Compare two forwards, returning non-zero if they are identical or * zero otherwise. */ int forward_equals(const struct Forward *a, const struct Forward *b) { if (strcmp_maybe_null(a->listen_host, b->listen_host) == 0) return 0; if (a->listen_port != b->listen_port) return 0; if (strcmp_maybe_null(a->listen_path, b->listen_path) == 0) return 0; if (strcmp_maybe_null(a->connect_host, b->connect_host) == 0) return 0; if (a->connect_port != b->connect_port) return 0; if (strcmp_maybe_null(a->connect_path, b->connect_path) == 0) return 0; /* allocated_port and handle are not checked */ return 1; } /* returns 1 if process is already daemonized, 0 otherwise */ int daemonized(void) { int fd; if ((fd = open(_PATH_TTY, O_RDONLY | O_NOCTTY)) >= 0) { close(fd); return 0; /* have controlling terminal */ } if (getppid() != 1) return 0; /* parent is not init */ if (getsid(0) != getpid()) return 0; /* not session leader */ debug3("already daemonized"); return 1; } /* * Splits 's' into an argument vector. Handles quoted string and basic * escape characters (\\, \", \'). Caller must free the argument vector * and its members. */ int argv_split(const char *s, int *argcp, char ***argvp, int terminate_on_comment) { int r = SSH_ERR_INTERNAL_ERROR; int argc = 0, quote, i, j; char *arg, **argv = xcalloc(1, sizeof(*argv)); *argvp = NULL; *argcp = 0; for (i = 0; s[i] != '\0'; i++) { /* Skip leading whitespace */ if (s[i] == ' ' || s[i] == '\t') continue; if (terminate_on_comment && s[i] == '#') break; /* Start of a token */ quote = 0; argv = xreallocarray(argv, (argc + 2), sizeof(*argv)); arg = argv[argc++] = xcalloc(1, strlen(s + i) + 1); argv[argc] = NULL; /* Copy the token in, removing escapes */ for (j = 0; s[i] != '\0'; i++) { if (s[i] == '\\') { if (s[i + 1] == '\'' || s[i + 1] == '\"' || s[i + 1] == '\\' || (quote == 0 && s[i + 1] == ' ')) { i++; /* Skip '\' */ arg[j++] = s[i]; } else { /* Unrecognised escape */ arg[j++] = s[i]; } } else if (quote == 0 && (s[i] == ' ' || s[i] == '\t')) break; /* done */ else if (quote == 0 && (s[i] == '\"' || s[i] == '\'')) quote = s[i]; /* quote start */ else if (quote != 0 && s[i] == quote) quote = 0; /* quote end */ else arg[j++] = s[i]; } if (s[i] == '\0') { if (quote != 0) { /* Ran out of string looking for close quote */ r = SSH_ERR_INVALID_FORMAT; goto out; } break; } } /* Success */ *argcp = argc; *argvp = argv; argc = 0; argv = NULL; r = 0; out: if (argc != 0 && argv != NULL) { for (i = 0; i < argc; i++) free(argv[i]); free(argv); } return r; } /* * Reassemble an argument vector into a string, quoting and escaping as * necessary. Caller must free returned string. */ char * argv_assemble(int argc, char **argv) { int i, j, ws, r; char c, *ret; struct sshbuf *buf, *arg; if ((buf = sshbuf_new()) == NULL || (arg = sshbuf_new()) == NULL) fatal_f("sshbuf_new failed"); for (i = 0; i < argc; i++) { ws = 0; sshbuf_reset(arg); for (j = 0; argv[i][j] != '\0'; j++) { r = 0; c = argv[i][j]; switch (c) { case ' ': case '\t': ws = 1; r = sshbuf_put_u8(arg, c); break; case '\\': case '\'': case '"': if ((r = sshbuf_put_u8(arg, '\\')) != 0) break; /* FALLTHROUGH */ default: r = sshbuf_put_u8(arg, c); break; } if (r != 0) fatal_fr(r, "sshbuf_put_u8"); } if ((i != 0 && (r = sshbuf_put_u8(buf, ' ')) != 0) || (ws != 0 && (r = sshbuf_put_u8(buf, '"')) != 0) || (r = sshbuf_putb(buf, arg)) != 0 || (ws != 0 && (r = sshbuf_put_u8(buf, '"')) != 0)) fatal_fr(r, "assemble"); } if ((ret = malloc(sshbuf_len(buf) + 1)) == NULL) fatal_f("malloc failed"); memcpy(ret, sshbuf_ptr(buf), sshbuf_len(buf)); ret[sshbuf_len(buf)] = '\0'; sshbuf_free(buf); sshbuf_free(arg); return ret; } char * argv_next(int *argcp, char ***argvp) { char *ret = (*argvp)[0]; if (*argcp > 0 && ret != NULL) { (*argcp)--; (*argvp)++; } return ret; } void argv_consume(int *argcp) { *argcp = 0; } void argv_free(char **av, int ac) { int i; if (av == NULL) return; for (i = 0; i < ac; i++) free(av[i]); free(av); } /* Returns 0 if pid exited cleanly, non-zero otherwise */ int exited_cleanly(pid_t pid, const char *tag, const char *cmd, int quiet) { int status; while (waitpid(pid, &status, 0) == -1) { if (errno != EINTR) { error("%s waitpid: %s", tag, strerror(errno)); return -1; } } if (WIFSIGNALED(status)) { error("%s %s exited on signal %d", tag, cmd, WTERMSIG(status)); return -1; } else if (WEXITSTATUS(status) != 0) { do_log2(quiet ? SYSLOG_LEVEL_DEBUG1 : SYSLOG_LEVEL_INFO, "%s %s failed, status %d", tag, cmd, WEXITSTATUS(status)); return -1; } return 0; } /* * Check a given path for security. This is defined as all components * of the path to the file must be owned by either the owner of * of the file or root and no directories must be group or world writable. * * XXX Should any specific check be done for sym links ? * * Takes a file name, its stat information (preferably from fstat() to * avoid races), the uid of the expected owner, their home directory and an * error buffer plus max size as arguments. * * Returns 0 on success and -1 on failure */ int safe_path(const char *name, struct stat *stp, const char *pw_dir, uid_t uid, char *err, size_t errlen) { char buf[PATH_MAX], homedir[PATH_MAX]; char *cp; int comparehome = 0; struct stat st; if (realpath(name, buf) == NULL) { snprintf(err, errlen, "realpath %s failed: %s", name, strerror(errno)); return -1; } if (pw_dir != NULL && realpath(pw_dir, homedir) != NULL) comparehome = 1; if (!S_ISREG(stp->st_mode)) { snprintf(err, errlen, "%s is not a regular file", buf); return -1; } if ((!platform_sys_dir_uid(stp->st_uid) && stp->st_uid != uid) || (stp->st_mode & 022) != 0) { snprintf(err, errlen, "bad ownership or modes for file %s", buf); return -1; } /* for each component of the canonical path, walking upwards */ for (;;) { if ((cp = dirname(buf)) == NULL) { snprintf(err, errlen, "dirname() failed"); return -1; } strlcpy(buf, cp, sizeof(buf)); if (stat(buf, &st) == -1 || (!platform_sys_dir_uid(st.st_uid) && st.st_uid != uid) || (st.st_mode & 022) != 0) { snprintf(err, errlen, "bad ownership or modes for directory %s", buf); return -1; } /* If are past the homedir then we can stop */ if (comparehome && strcmp(homedir, buf) == 0) break; /* * dirname should always complete with a "/" path, * but we can be paranoid and check for "." too */ if ((strcmp("/", buf) == 0) || (strcmp(".", buf) == 0)) break; } return 0; } /* * Version of safe_path() that accepts an open file descriptor to * avoid races. * * Returns 0 on success and -1 on failure */ int safe_path_fd(int fd, const char *file, struct passwd *pw, char *err, size_t errlen) { struct stat st; /* check the open file to avoid races */ if (fstat(fd, &st) == -1) { snprintf(err, errlen, "cannot stat file %s: %s", file, strerror(errno)); return -1; } return safe_path(file, &st, pw->pw_dir, pw->pw_uid, err, errlen); } /* * Sets the value of the given variable in the environment. If the variable * already exists, its value is overridden. */ void child_set_env(char ***envp, u_int *envsizep, const char *name, const char *value) { char **env; u_int envsize; u_int i, namelen; if (strchr(name, '=') != NULL) { error("Invalid environment variable \"%.100s\"", name); return; } /* * If we're passed an uninitialized list, allocate a single null * entry before continuing. */ if ((*envp == NULL) != (*envsizep == 0)) fatal_f("environment size mismatch"); if (*envp == NULL && *envsizep == 0) { *envp = xmalloc(sizeof(char *)); *envp[0] = NULL; *envsizep = 1; } /* * Find the slot where the value should be stored. If the variable * already exists, we reuse the slot; otherwise we append a new slot * at the end of the array, expanding if necessary. */ env = *envp; namelen = strlen(name); for (i = 0; env[i]; i++) if (strncmp(env[i], name, namelen) == 0 && env[i][namelen] == '=') break; if (env[i]) { /* Reuse the slot. */ free(env[i]); } else { /* New variable. Expand if necessary. */ envsize = *envsizep; if (i >= envsize - 1) { if (envsize >= 1000) fatal("child_set_env: too many env vars"); envsize += 50; env = (*envp) = xreallocarray(env, envsize, sizeof(char *)); *envsizep = envsize; } /* Need to set the NULL pointer at end of array beyond the new slot. */ env[i + 1] = NULL; } /* Allocate space and format the variable in the appropriate slot. */ /* XXX xasprintf */ env[i] = xmalloc(strlen(name) + 1 + strlen(value) + 1); snprintf(env[i], strlen(name) + 1 + strlen(value) + 1, "%s=%s", name, value); } /* * Check and optionally lowercase a domain name, also removes trailing '.' * Returns 1 on success and 0 on failure, storing an error message in errstr. */ int valid_domain(char *name, int makelower, const char **errstr) { size_t i, l = strlen(name); u_char c, last = '\0'; static char errbuf[256]; if (l == 0) { strlcpy(errbuf, "empty domain name", sizeof(errbuf)); goto bad; } if (!isalpha((u_char)name[0]) && !isdigit((u_char)name[0])) { snprintf(errbuf, sizeof(errbuf), "domain name \"%.100s\" " "starts with invalid character", name); goto bad; } for (i = 0; i < l; i++) { c = tolower((u_char)name[i]); if (makelower) name[i] = (char)c; if (last == '.' && c == '.') { snprintf(errbuf, sizeof(errbuf), "domain name " "\"%.100s\" contains consecutive separators", name); goto bad; } if (c != '.' && c != '-' && !isalnum(c) && c != '_') /* technically invalid, but common */ { snprintf(errbuf, sizeof(errbuf), "domain name " "\"%.100s\" contains invalid characters", name); goto bad; } last = c; } if (name[l - 1] == '.') name[l - 1] = '\0'; if (errstr != NULL) *errstr = NULL; return 1; bad: if (errstr != NULL) *errstr = errbuf; return 0; } /* * Verify that a environment variable name (not including initial '$') is * valid; consisting of one or more alphanumeric or underscore characters only. * Returns 1 on valid, 0 otherwise. */ int valid_env_name(const char *name) { const char *cp; if (name[0] == '\0') return 0; for (cp = name; *cp != '\0'; cp++) { if (!isalnum((u_char)*cp) && *cp != '_') return 0; } return 1; } const char * atoi_err(const char *nptr, int *val) { const char *errstr = NULL; long long num; if (nptr == NULL || *nptr == '\0') return "missing"; num = strtonum(nptr, 0, INT_MAX, &errstr); if (errstr == NULL) *val = (int)num; return errstr; } int parse_absolute_time(const char *s, uint64_t *tp) { struct tm tm; time_t tt; char buf[32], *fmt; const char *cp; size_t l; int is_utc = 0; *tp = 0; l = strlen(s); if (l > 1 && strcasecmp(s + l - 1, "Z") == 0) { is_utc = 1; l--; } else if (l > 3 && strcasecmp(s + l - 3, "UTC") == 0) { is_utc = 1; l -= 3; } /* * POSIX strptime says "The application shall ensure that there * is white-space or other non-alphanumeric characters between * any two conversion specifications" so arrange things this way. */ switch (l) { case 8: /* YYYYMMDD */ fmt = "%Y-%m-%d"; snprintf(buf, sizeof(buf), "%.4s-%.2s-%.2s", s, s + 4, s + 6); break; case 12: /* YYYYMMDDHHMM */ fmt = "%Y-%m-%dT%H:%M"; snprintf(buf, sizeof(buf), "%.4s-%.2s-%.2sT%.2s:%.2s", s, s + 4, s + 6, s + 8, s + 10); break; case 14: /* YYYYMMDDHHMMSS */ fmt = "%Y-%m-%dT%H:%M:%S"; snprintf(buf, sizeof(buf), "%.4s-%.2s-%.2sT%.2s:%.2s:%.2s", s, s + 4, s + 6, s + 8, s + 10, s + 12); break; default: return SSH_ERR_INVALID_FORMAT; } memset(&tm, 0, sizeof(tm)); if ((cp = strptime(buf, fmt, &tm)) == NULL || *cp != '\0') return SSH_ERR_INVALID_FORMAT; if (is_utc) { if ((tt = timegm(&tm)) < 0) return SSH_ERR_INVALID_FORMAT; } else { if ((tt = mktime(&tm)) < 0) return SSH_ERR_INVALID_FORMAT; } /* success */ *tp = (uint64_t)tt; return 0; } void format_absolute_time(uint64_t t, char *buf, size_t len) { time_t tt = t > SSH_TIME_T_MAX ? SSH_TIME_T_MAX : t; struct tm tm; localtime_r(&tt, &tm); strftime(buf, len, "%Y-%m-%dT%H:%M:%S", &tm); } /* check if path is absolute */ int path_absolute(const char *path) { return (*path == '/') ? 1 : 0; } void skip_space(char **cpp) { char *cp; for (cp = *cpp; *cp == ' ' || *cp == '\t'; cp++) ; *cpp = cp; } /* authorized_key-style options parsing helpers */ /* * Match flag 'opt' in *optsp, and if allow_negate is set then also match * 'no-opt'. Returns -1 if option not matched, 1 if option matches or 0 * if negated option matches. * If the option or negated option matches, then *optsp is updated to * point to the first character after the option. */ int opt_flag(const char *opt, int allow_negate, const char **optsp) { size_t opt_len = strlen(opt); const char *opts = *optsp; int negate = 0; if (allow_negate && strncasecmp(opts, "no-", 3) == 0) { opts += 3; negate = 1; } if (strncasecmp(opts, opt, opt_len) == 0) { *optsp = opts + opt_len; return negate ? 0 : 1; } return -1; } char * opt_dequote(const char **sp, const char **errstrp) { const char *s = *sp; char *ret; size_t i; *errstrp = NULL; if (*s != '"') { *errstrp = "missing start quote"; return NULL; } s++; if ((ret = malloc(strlen((s)) + 1)) == NULL) { *errstrp = "memory allocation failed"; return NULL; } for (i = 0; *s != '\0' && *s != '"';) { if (s[0] == '\\' && s[1] == '"') s++; ret[i++] = *s++; } if (*s == '\0') { *errstrp = "missing end quote"; free(ret); return NULL; } ret[i] = '\0'; s++; *sp = s; return ret; } int opt_match(const char **opts, const char *term) { if (strncasecmp((*opts), term, strlen(term)) == 0 && (*opts)[strlen(term)] == '=') { *opts += strlen(term) + 1; return 1; } return 0; } void opt_array_append2(const char *file, const int line, const char *directive, char ***array, int **iarray, u_int *lp, const char *s, int i) { if (*lp >= INT_MAX) fatal("%s line %d: Too many %s entries", file, line, directive); if (iarray != NULL) { *iarray = xrecallocarray(*iarray, *lp, *lp + 1, sizeof(**iarray)); (*iarray)[*lp] = i; } *array = xrecallocarray(*array, *lp, *lp + 1, sizeof(**array)); (*array)[*lp] = xstrdup(s); (*lp)++; } void opt_array_append(const char *file, const int line, const char *directive, char ***array, u_int *lp, const char *s) { opt_array_append2(file, line, directive, array, NULL, lp, s, 0); } sshsig_t ssh_signal(int signum, sshsig_t handler) { struct sigaction sa, osa; /* mask all other signals while in handler */ memset(&sa, 0, sizeof(sa)); sa.sa_handler = handler; sigfillset(&sa.sa_mask); #if defined(SA_RESTART) && !defined(NO_SA_RESTART) if (signum != SIGALRM) sa.sa_flags = SA_RESTART; #endif if (sigaction(signum, &sa, &osa) == -1) { debug3("sigaction(%s): %s", strsignal(signum), strerror(errno)); return SIG_ERR; } return osa.sa_handler; } int stdfd_devnull(int do_stdin, int do_stdout, int do_stderr) { int devnull, ret = 0; if ((devnull = open(_PATH_DEVNULL, O_RDWR)) == -1) { error_f("open %s: %s", _PATH_DEVNULL, strerror(errno)); return -1; } if ((do_stdin && dup2(devnull, STDIN_FILENO) == -1) || (do_stdout && dup2(devnull, STDOUT_FILENO) == -1) || (do_stderr && dup2(devnull, STDERR_FILENO) == -1)) { error_f("dup2: %s", strerror(errno)); ret = -1; } if (devnull > STDERR_FILENO) close(devnull); return ret; } /* * Runs command in a subprocess with a minimal environment. * Returns pid on success, 0 on failure. * The child stdout and stderr maybe captured, left attached or sent to * /dev/null depending on the contents of flags. * "tag" is prepended to log messages. * NB. "command" is only used for logging; the actual command executed is * av[0]. */ pid_t subprocess(const char *tag, const char *command, int ac, char **av, FILE **child, u_int flags, struct passwd *pw, privdrop_fn *drop_privs, privrestore_fn *restore_privs) { FILE *f = NULL; struct stat st; int fd, devnull, p[2], i; pid_t pid; char *cp, errmsg[512]; u_int nenv = 0; char **env = NULL; /* If dropping privs, then must specify user and restore function */ if (drop_privs != NULL && (pw == NULL || restore_privs == NULL)) { error("%s: inconsistent arguments", tag); /* XXX fatal? */ return 0; } if (pw == NULL && (pw = getpwuid(getuid())) == NULL) { error("%s: no user for current uid", tag); return 0; } if (child != NULL) *child = NULL; debug3_f("%s command \"%s\" running as %s (flags 0x%x)", tag, command, pw->pw_name, flags); /* Check consistency */ if ((flags & SSH_SUBPROCESS_STDOUT_DISCARD) != 0 && (flags & SSH_SUBPROCESS_STDOUT_CAPTURE) != 0) { error_f("inconsistent flags"); return 0; } if (((flags & SSH_SUBPROCESS_STDOUT_CAPTURE) == 0) != (child == NULL)) { error_f("inconsistent flags/output"); return 0; } /* * If executing an explicit binary, then verify the it exists * and appears safe-ish to execute */ if (!path_absolute(av[0])) { error("%s path is not absolute", tag); return 0; } if (drop_privs != NULL) drop_privs(pw); if (stat(av[0], &st) == -1) { error("Could not stat %s \"%s\": %s", tag, av[0], strerror(errno)); goto restore_return; } if ((flags & SSH_SUBPROCESS_UNSAFE_PATH) == 0 && safe_path(av[0], &st, NULL, 0, errmsg, sizeof(errmsg)) != 0) { error("Unsafe %s \"%s\": %s", tag, av[0], errmsg); goto restore_return; } /* Prepare to keep the child's stdout if requested */ if (pipe(p) == -1) { error("%s: pipe: %s", tag, strerror(errno)); restore_return: if (restore_privs != NULL) restore_privs(); return 0; } if (restore_privs != NULL) restore_privs(); switch ((pid = fork())) { case -1: /* error */ error("%s: fork: %s", tag, strerror(errno)); close(p[0]); close(p[1]); return 0; case 0: /* child */ /* Prepare a minimal environment for the child. */ if ((flags & SSH_SUBPROCESS_PRESERVE_ENV) == 0) { nenv = 5; env = xcalloc(sizeof(*env), nenv); child_set_env(&env, &nenv, "PATH", _PATH_STDPATH); child_set_env(&env, &nenv, "USER", pw->pw_name); child_set_env(&env, &nenv, "LOGNAME", pw->pw_name); child_set_env(&env, &nenv, "HOME", pw->pw_dir); if ((cp = getenv("LANG")) != NULL) child_set_env(&env, &nenv, "LANG", cp); } for (i = 1; i < NSIG; i++) ssh_signal(i, SIG_DFL); if ((devnull = open(_PATH_DEVNULL, O_RDWR)) == -1) { error("%s: open %s: %s", tag, _PATH_DEVNULL, strerror(errno)); _exit(1); } if (dup2(devnull, STDIN_FILENO) == -1) { error("%s: dup2: %s", tag, strerror(errno)); _exit(1); } /* Set up stdout as requested; leave stderr in place for now. */ fd = -1; if ((flags & SSH_SUBPROCESS_STDOUT_CAPTURE) != 0) fd = p[1]; else if ((flags & SSH_SUBPROCESS_STDOUT_DISCARD) != 0) fd = devnull; if (fd != -1 && dup2(fd, STDOUT_FILENO) == -1) { error("%s: dup2: %s", tag, strerror(errno)); _exit(1); } closefrom(STDERR_FILENO + 1); if (geteuid() == 0 && initgroups(pw->pw_name, pw->pw_gid) == -1) { error("%s: initgroups(%s, %u): %s", tag, pw->pw_name, (u_int)pw->pw_gid, strerror(errno)); _exit(1); } if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1) { error("%s: setresgid %u: %s", tag, (u_int)pw->pw_gid, strerror(errno)); _exit(1); } if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1) { error("%s: setresuid %u: %s", tag, (u_int)pw->pw_uid, strerror(errno)); _exit(1); } /* stdin is pointed to /dev/null at this point */ if ((flags & SSH_SUBPROCESS_STDOUT_DISCARD) != 0 && dup2(STDIN_FILENO, STDERR_FILENO) == -1) { error("%s: dup2: %s", tag, strerror(errno)); _exit(1); } if (env != NULL) execve(av[0], av, env); else execv(av[0], av); error("%s %s \"%s\": %s", tag, env == NULL ? "execv" : "execve", command, strerror(errno)); _exit(127); default: /* parent */ break; } close(p[1]); if ((flags & SSH_SUBPROCESS_STDOUT_CAPTURE) == 0) close(p[0]); else if ((f = fdopen(p[0], "r")) == NULL) { error("%s: fdopen: %s", tag, strerror(errno)); close(p[0]); /* Don't leave zombie child */ kill(pid, SIGTERM); while (waitpid(pid, NULL, 0) == -1 && errno == EINTR) ; return 0; } /* Success */ debug3_f("%s pid %ld", tag, (long)pid); if (child != NULL) *child = f; return pid; } const char * lookup_env_in_list(const char *env, char * const *envs, size_t nenvs) { size_t i, envlen; envlen = strlen(env); for (i = 0; i < nenvs; i++) { if (strncmp(envs[i], env, envlen) == 0 && envs[i][envlen] == '=') { return envs[i] + envlen + 1; } } return NULL; } const char * lookup_setenv_in_list(const char *env, char * const *envs, size_t nenvs) { char *name, *cp; const char *ret; name = xstrdup(env); if ((cp = strchr(name, '=')) == NULL) { free(name); return NULL; /* not env=val */ } *cp = '\0'; ret = lookup_env_in_list(name, envs, nenvs); free(name); return ret; } /* * Helpers for managing poll(2)/ppoll(2) timeouts * Will remember the earliest deadline and return it for use in poll/ppoll. */ /* Initialise a poll/ppoll timeout with an indefinite deadline */ void ptimeout_init(struct timespec *pt) { /* * Deliberately invalid for ppoll(2). * Will be converted to NULL in ptimeout_get_tspec() later. */ pt->tv_sec = -1; pt->tv_nsec = 0; } /* Specify a poll/ppoll deadline of at most 'sec' seconds */ void ptimeout_deadline_sec(struct timespec *pt, long sec) { if (pt->tv_sec == -1 || pt->tv_sec >= sec) { pt->tv_sec = sec; pt->tv_nsec = 0; } } /* Specify a poll/ppoll deadline of at most 'p' (timespec) */ static void ptimeout_deadline_tsp(struct timespec *pt, struct timespec *p) { if (pt->tv_sec == -1 || timespeccmp(pt, p, >=)) *pt = *p; } /* Specify a poll/ppoll deadline of at most 'ms' milliseconds */ void ptimeout_deadline_ms(struct timespec *pt, long ms) { struct timespec p; p.tv_sec = ms / 1000; p.tv_nsec = (ms % 1000) * 1000000; ptimeout_deadline_tsp(pt, &p); } -/* Specify a poll/ppoll deadline at wall clock monotime 'when' */ +/* Specify a poll/ppoll deadline at wall clock monotime 'when' (timespec) */ void -ptimeout_deadline_monotime(struct timespec *pt, time_t when) +ptimeout_deadline_monotime_tsp(struct timespec *pt, struct timespec *when) { struct timespec now, t; - t.tv_sec = when; - t.tv_nsec = 0; monotime_ts(&now); - if (timespeccmp(&now, &t, >=)) - ptimeout_deadline_sec(pt, 0); - else { - timespecsub(&t, &now, &t); + if (timespeccmp(&now, when, >=)) { + /* 'when' is now or in the past. Timeout ASAP */ + pt->tv_sec = 0; + pt->tv_nsec = 0; + } else { + timespecsub(when, &now, &t); ptimeout_deadline_tsp(pt, &t); } } +/* Specify a poll/ppoll deadline at wall clock monotime 'when' */ +void +ptimeout_deadline_monotime(struct timespec *pt, time_t when) +{ + struct timespec t; + + t.tv_sec = when; + t.tv_nsec = 0; + ptimeout_deadline_monotime_tsp(pt, &t); +} + /* Get a poll(2) timeout value in milliseconds */ int ptimeout_get_ms(struct timespec *pt) { if (pt->tv_sec == -1) return -1; if (pt->tv_sec >= (INT_MAX - (pt->tv_nsec / 1000000)) / 1000) return INT_MAX; return (pt->tv_sec * 1000) + (pt->tv_nsec / 1000000); } /* Get a ppoll(2) timeout value as a timespec pointer */ struct timespec * ptimeout_get_tsp(struct timespec *pt) { return pt->tv_sec == -1 ? NULL : pt; } /* Returns non-zero if a timeout has been set (i.e. is not indefinite) */ int ptimeout_isset(struct timespec *pt) { return pt->tv_sec != -1; } /* * Returns zero if the library at 'path' contains symbol 's', nonzero * otherwise. */ int lib_contains_symbol(const char *path, const char *s) { #ifdef HAVE_NLIST_H struct nlist nl[2]; int ret = -1, r; memset(nl, 0, sizeof(nl)); nl[0].n_name = xstrdup(s); nl[1].n_name = NULL; if ((r = nlist(path, nl)) == -1) { error_f("nlist failed for %s", path); goto out; } if (r != 0 || nl[0].n_value == 0 || nl[0].n_type == 0) { error_f("library %s does not contain symbol %s", path, s); goto out; } /* success */ ret = 0; out: free(nl[0].n_name); return ret; #else /* HAVE_NLIST_H */ int fd, ret = -1; struct stat st; void *m = NULL; size_t sz = 0; memset(&st, 0, sizeof(st)); if ((fd = open(path, O_RDONLY)) < 0) { error_f("open %s: %s", path, strerror(errno)); return -1; } if (fstat(fd, &st) != 0) { error_f("fstat %s: %s", path, strerror(errno)); goto out; } if (!S_ISREG(st.st_mode)) { error_f("%s is not a regular file", path); goto out; } if (st.st_size < 0 || (size_t)st.st_size < strlen(s) || st.st_size >= INT_MAX/2) { error_f("%s bad size %lld", path, (long long)st.st_size); goto out; } sz = (size_t)st.st_size; if ((m = mmap(NULL, sz, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED || m == NULL) { error_f("mmap %s: %s", path, strerror(errno)); goto out; } if (memmem(m, sz, s, strlen(s)) == NULL) { error_f("%s does not contain expected string %s", path, s); goto out; } /* success */ ret = 0; out: if (m != NULL && m != MAP_FAILED) munmap(m, sz); close(fd); return ret; #endif /* HAVE_NLIST_H */ } diff --git a/misc.h b/misc.h index fd77a7fd7273..4f941597e9e3 100644 --- a/misc.h +++ b/misc.h @@ -1,247 +1,249 @@ -/* $OpenBSD: misc.h,v 1.103 2023/07/19 14:02:27 djm Exp $ */ +/* $OpenBSD: misc.h,v 1.105 2023/08/28 03:31:16 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". */ #ifndef _MISC_H #define _MISC_H #include #include #include #include +#include /* Data structure for representing a forwarding request. */ struct Forward { char *listen_host; /* Host (address) to listen on. */ int listen_port; /* Port to forward. */ char *listen_path; /* Path to bind domain socket. */ char *connect_host; /* Host to connect. */ int connect_port; /* Port to connect on connect_host. */ char *connect_path; /* Path to connect domain socket. */ int allocated_port; /* Dynamically allocated listen port */ int handle; /* Handle for dynamic listen ports */ }; int forward_equals(const struct Forward *, const struct Forward *); int daemonized(void); /* Common server and client forwarding options. */ struct ForwardOptions { int gateway_ports; /* Allow remote connects to forwarded ports. */ mode_t streamlocal_bind_mask; /* umask for streamlocal binds */ int streamlocal_bind_unlink; /* unlink socket before bind */ }; /* misc.c */ char *chop(char *); void rtrim(char *); void skip_space(char **); char *strdelim(char **); char *strdelimw(char **); int set_nonblock(int); int unset_nonblock(int); void set_nodelay(int); int set_reuseaddr(int); char *get_rdomain(int); int set_rdomain(int, const char *); int get_sock_af(int); void set_sock_tos(int, int); -int waitrfd(int, int *); +int waitrfd(int, int *, volatile sig_atomic_t *); int timeout_connect(int, const struct sockaddr *, socklen_t, int *); int a2port(const char *); int a2tun(const char *, int *); char *put_host_port(const char *, u_short); char *hpdelim2(char **, char *); char *hpdelim(char **); char *cleanhostname(char *); char *colon(char *); int parse_user_host_path(const char *, char **, char **, char **); int parse_user_host_port(const char *, char **, char **, int *); int parse_uri(const char *, const char *, char **, char **, int *, char **); int convtime(const char *); const char *fmt_timeframe(time_t t); int tilde_expand(const char *, uid_t, char **); char *tilde_expand_filename(const char *, uid_t); char *dollar_expand(int *, const char *string, ...); char *percent_expand(const char *, ...) __attribute__((__sentinel__)); char *percent_dollar_expand(const char *, ...) __attribute__((__sentinel__)); char *tohex(const void *, size_t); void xextendf(char **s, const char *sep, const char *fmt, ...) __attribute__((__format__ (printf, 3, 4))) __attribute__((__nonnull__ (3))); void sanitise_stdfd(void); void ms_subtract_diff(struct timeval *, int *); void ms_to_timespec(struct timespec *, int); void monotime_ts(struct timespec *); void monotime_tv(struct timeval *); time_t monotime(void); double monotime_double(void); void lowercase(char *s); int unix_listener(const char *, int, int); int valid_domain(char *, int, const char **); int valid_env_name(const char *); const char *atoi_err(const char *, int *); int parse_absolute_time(const char *, uint64_t *); void format_absolute_time(uint64_t, char *, size_t); int path_absolute(const char *); int stdfd_devnull(int, int, int); int lib_contains_symbol(const char *, const char *); void sock_set_v6only(int); struct passwd *pwcopy(struct passwd *); const char *ssh_gai_strerror(int); typedef void privdrop_fn(struct passwd *); typedef void privrestore_fn(void); #define SSH_SUBPROCESS_STDOUT_DISCARD (1) /* Discard stdout */ #define SSH_SUBPROCESS_STDOUT_CAPTURE (1<<1) /* Redirect stdout */ #define SSH_SUBPROCESS_STDERR_DISCARD (1<<2) /* Discard stderr */ #define SSH_SUBPROCESS_UNSAFE_PATH (1<<3) /* Don't check for safe cmd */ #define SSH_SUBPROCESS_PRESERVE_ENV (1<<4) /* Keep parent environment */ pid_t subprocess(const char *, const char *, int, char **, FILE **, u_int, struct passwd *, privdrop_fn *, privrestore_fn *); typedef struct arglist arglist; struct arglist { char **list; u_int num; u_int nalloc; }; void addargs(arglist *, char *, ...) __attribute__((format(printf, 2, 3))); void replacearg(arglist *, u_int, char *, ...) __attribute__((format(printf, 3, 4))); void freeargs(arglist *); int tun_open(int, int, char **); /* Common definitions for ssh tunnel device forwarding */ #define SSH_TUNMODE_NO 0x00 #define SSH_TUNMODE_POINTOPOINT 0x01 #define SSH_TUNMODE_ETHERNET 0x02 #define SSH_TUNMODE_DEFAULT SSH_TUNMODE_POINTOPOINT #define SSH_TUNMODE_YES (SSH_TUNMODE_POINTOPOINT|SSH_TUNMODE_ETHERNET) #define SSH_TUNID_ANY 0x7fffffff #define SSH_TUNID_ERR (SSH_TUNID_ANY - 1) #define SSH_TUNID_MAX (SSH_TUNID_ANY - 2) /* Fake port to indicate that host field is really a path. */ #define PORT_STREAMLOCAL -2 /* Functions to extract or store big-endian words of various sizes */ u_int64_t get_u64(const void *) __attribute__((__bounded__( __minbytes__, 1, 8))); u_int32_t get_u32(const void *) __attribute__((__bounded__( __minbytes__, 1, 4))); u_int16_t get_u16(const void *) __attribute__((__bounded__( __minbytes__, 1, 2))); void put_u64(void *, u_int64_t) __attribute__((__bounded__( __minbytes__, 1, 8))); void put_u32(void *, u_int32_t) __attribute__((__bounded__( __minbytes__, 1, 4))); void put_u16(void *, u_int16_t) __attribute__((__bounded__( __minbytes__, 1, 2))); /* Little-endian store/load, used by umac.c */ u_int32_t get_u32_le(const void *) __attribute__((__bounded__(__minbytes__, 1, 4))); void put_u32_le(void *, u_int32_t) __attribute__((__bounded__(__minbytes__, 1, 4))); struct bwlimit { size_t buflen; u_int64_t rate; /* desired rate in kbit/s */ u_int64_t thresh; /* threshold after which we'll check timers */ u_int64_t lamt; /* amount written in last timer interval */ struct timeval bwstart, bwend; }; void bandwidth_limit_init(struct bwlimit *, u_int64_t, size_t); void bandwidth_limit(struct bwlimit *, size_t); int parse_ipqos(const char *); const char *iptos2str(int); void mktemp_proto(char *, size_t); void child_set_env(char ***envp, u_int *envsizep, const char *name, const char *value); const char *lookup_env_in_list(const char *env, char * const *envs, size_t nenvs); const char *lookup_setenv_in_list(const char *env, char * const *envs, size_t nenvs); int argv_split(const char *, int *, char ***, int); char *argv_assemble(int, char **argv); char *argv_next(int *, char ***); void argv_consume(int *); void argv_free(char **, int); int exited_cleanly(pid_t, const char *, const char *, int); struct stat; int safe_path(const char *, struct stat *, const char *, uid_t, char *, size_t); int safe_path_fd(int, const char *, struct passwd *, char *err, size_t errlen); /* authorized_key-style options parsing helpers */ int opt_flag(const char *opt, int allow_negate, const char **optsp); char *opt_dequote(const char **sp, const char **errstrp); int opt_match(const char **opts, const char *term); /* readconf/servconf option lists */ void opt_array_append(const char *file, const int line, const char *directive, char ***array, u_int *lp, const char *s); void opt_array_append2(const char *file, const int line, const char *directive, char ***array, int **iarray, u_int *lp, const char *s, int i); struct timespec; void ptimeout_init(struct timespec *pt); void ptimeout_deadline_sec(struct timespec *pt, long sec); void ptimeout_deadline_ms(struct timespec *pt, long ms); +void ptimeout_deadline_monotime_tsp(struct timespec *pt, struct timespec *when); void ptimeout_deadline_monotime(struct timespec *pt, time_t when); int ptimeout_get_ms(struct timespec *pt); struct timespec *ptimeout_get_tsp(struct timespec *pt); int ptimeout_isset(struct timespec *pt); /* readpass.c */ #define RP_ECHO 0x0001 #define RP_ALLOW_STDIN 0x0002 #define RP_ALLOW_EOF 0x0004 #define RP_USE_ASKPASS 0x0008 struct notifier_ctx; char *read_passphrase(const char *, int); int ask_permission(const char *, ...) __attribute__((format(printf, 1, 2))); struct notifier_ctx *notify_start(int, const char *, ...) __attribute__((format(printf, 2, 3))); void notify_complete(struct notifier_ctx *, const char *, ...) __attribute__((format(printf, 2, 3))); #define MINIMUM(a, b) (((a) < (b)) ? (a) : (b)) #define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b)) #define ROUNDUP(x, y) ((((x)+((y)-1))/(y))*(y)) typedef void (*sshsig_t)(int); sshsig_t ssh_signal(int, sshsig_t); /* On OpenBSD time_t is int64_t which is long long. */ /* #define SSH_TIME_T_MAX LLONG_MAX */ #endif /* _MISC_H */ diff --git a/monitor.c b/monitor.c index 1489c78d81ec..b3ed515ed0ba 100644 --- a/monitor.c +++ b/monitor.c @@ -1,1951 +1,1956 @@ -/* $OpenBSD: monitor.c,v 1.236 2023/05/10 10:04:20 dtucker Exp $ */ +/* $OpenBSD: monitor.c,v 1.237 2023/08/16 16:14:11 djm Exp $ */ /* * Copyright 2002 Niels Provos * Copyright 2002 Markus Friedl * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" #include #include #include #include #include #include #ifdef HAVE_PATHS_H #include #endif #include #include #ifdef HAVE_STDINT_H # include #endif #include #include #include #include #include #ifdef HAVE_POLL_H #include #else # ifdef HAVE_SYS_POLL_H # include # endif #endif #ifdef WITH_OPENSSL #include #endif #include "openbsd-compat/sys-tree.h" #include "openbsd-compat/sys-queue.h" #include "openbsd-compat/openssl-compat.h" #include "atomicio.h" #include "xmalloc.h" #include "ssh.h" #include "sshkey.h" #include "sshbuf.h" #include "hostfile.h" #include "auth.h" #include "cipher.h" #include "kex.h" #include "dh.h" #include "auth-pam.h" #include "packet.h" #include "auth-options.h" #include "sshpty.h" #include "channels.h" #include "session.h" #include "sshlogin.h" #include "canohost.h" #include "log.h" #include "misc.h" #include "servconf.h" #include "monitor.h" #ifdef GSSAPI #include "ssh-gss.h" #endif #include "monitor_wrap.h" #include "monitor_fdpass.h" #include "compat.h" #include "ssh2.h" #include "authfd.h" #include "match.h" #include "ssherr.h" #include "sk-api.h" #ifdef GSSAPI static Gssctxt *gsscontext = NULL; #endif /* Imports */ extern ServerOptions options; extern u_int utmp_len; extern struct sshbuf *loginmsg; extern struct sshauthopt *auth_opts; /* XXX move to permanent ssh->authctxt? */ /* State exported from the child */ static struct sshbuf *child_state; /* Functions on the monitor that answer unprivileged requests */ int mm_answer_moduli(struct ssh *, int, struct sshbuf *); int mm_answer_sign(struct ssh *, int, struct sshbuf *); int mm_answer_pwnamallow(struct ssh *, int, struct sshbuf *); int mm_answer_auth2_read_banner(struct ssh *, int, struct sshbuf *); int mm_answer_authserv(struct ssh *, int, struct sshbuf *); int mm_answer_authpassword(struct ssh *, int, struct sshbuf *); int mm_answer_bsdauthquery(struct ssh *, int, struct sshbuf *); int mm_answer_bsdauthrespond(struct ssh *, int, struct sshbuf *); int mm_answer_keyallowed(struct ssh *, int, struct sshbuf *); int mm_answer_keyverify(struct ssh *, int, struct sshbuf *); int mm_answer_pty(struct ssh *, int, struct sshbuf *); int mm_answer_pty_cleanup(struct ssh *, int, struct sshbuf *); int mm_answer_term(struct ssh *, int, struct sshbuf *); int mm_answer_sesskey(struct ssh *, int, struct sshbuf *); int mm_answer_sessid(struct ssh *, int, struct sshbuf *); #ifdef USE_PAM int mm_answer_pam_start(struct ssh *, int, struct sshbuf *); int mm_answer_pam_account(struct ssh *, int, struct sshbuf *); int mm_answer_pam_init_ctx(struct ssh *, int, struct sshbuf *); int mm_answer_pam_query(struct ssh *, int, struct sshbuf *); int mm_answer_pam_respond(struct ssh *, int, struct sshbuf *); int mm_answer_pam_free_ctx(struct ssh *, int, struct sshbuf *); #endif #ifdef GSSAPI int mm_answer_gss_setup_ctx(struct ssh *, int, struct sshbuf *); int mm_answer_gss_accept_ctx(struct ssh *, int, struct sshbuf *); int mm_answer_gss_userok(struct ssh *, int, struct sshbuf *); int mm_answer_gss_checkmic(struct ssh *, int, struct sshbuf *); #endif #ifdef SSH_AUDIT_EVENTS int mm_answer_audit_event(struct ssh *, int, struct sshbuf *); int mm_answer_audit_command(struct ssh *, int, struct sshbuf *); #endif static Authctxt *authctxt; /* local state for key verify */ static u_char *key_blob = NULL; static size_t key_bloblen = 0; static u_int key_blobtype = MM_NOKEY; static struct sshauthopt *key_opts = NULL; static char *hostbased_cuser = NULL; static char *hostbased_chost = NULL; static char *auth_method = "unknown"; static char *auth_submethod = NULL; static u_int session_id2_len = 0; static u_char *session_id2 = NULL; static pid_t monitor_child_pid; struct mon_table { enum monitor_reqtype type; int flags; int (*f)(struct ssh *, int, struct sshbuf *); }; #define MON_ISAUTH 0x0004 /* Required for Authentication */ #define MON_AUTHDECIDE 0x0008 /* Decides Authentication */ #define MON_ONCE 0x0010 /* Disable after calling */ #define MON_ALOG 0x0020 /* Log auth attempt without authenticating */ #define MON_AUTH (MON_ISAUTH|MON_AUTHDECIDE) #define MON_PERMIT 0x1000 /* Request is permitted */ static int monitor_read(struct ssh *, struct monitor *, struct mon_table *, struct mon_table **); static int monitor_read_log(struct monitor *); struct mon_table mon_dispatch_proto20[] = { #ifdef WITH_OPENSSL {MONITOR_REQ_MODULI, MON_ONCE, mm_answer_moduli}, #endif {MONITOR_REQ_SIGN, MON_ONCE, mm_answer_sign}, {MONITOR_REQ_PWNAM, MON_ONCE, mm_answer_pwnamallow}, {MONITOR_REQ_AUTHSERV, MON_ONCE, mm_answer_authserv}, {MONITOR_REQ_AUTH2_READ_BANNER, MON_ONCE, mm_answer_auth2_read_banner}, {MONITOR_REQ_AUTHPASSWORD, MON_AUTH, mm_answer_authpassword}, #ifdef USE_PAM {MONITOR_REQ_PAM_START, MON_ONCE, mm_answer_pam_start}, {MONITOR_REQ_PAM_ACCOUNT, 0, mm_answer_pam_account}, {MONITOR_REQ_PAM_INIT_CTX, MON_ONCE, mm_answer_pam_init_ctx}, {MONITOR_REQ_PAM_QUERY, 0, mm_answer_pam_query}, {MONITOR_REQ_PAM_RESPOND, MON_ONCE, mm_answer_pam_respond}, {MONITOR_REQ_PAM_FREE_CTX, MON_ONCE|MON_AUTHDECIDE, mm_answer_pam_free_ctx}, #endif #ifdef SSH_AUDIT_EVENTS {MONITOR_REQ_AUDIT_EVENT, MON_PERMIT, mm_answer_audit_event}, #endif #ifdef BSD_AUTH {MONITOR_REQ_BSDAUTHQUERY, MON_ISAUTH, mm_answer_bsdauthquery}, {MONITOR_REQ_BSDAUTHRESPOND, MON_AUTH, mm_answer_bsdauthrespond}, #endif {MONITOR_REQ_KEYALLOWED, MON_ISAUTH, mm_answer_keyallowed}, {MONITOR_REQ_KEYVERIFY, MON_AUTH, mm_answer_keyverify}, #ifdef GSSAPI {MONITOR_REQ_GSSSETUP, MON_ISAUTH, mm_answer_gss_setup_ctx}, {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx}, {MONITOR_REQ_GSSUSEROK, MON_ONCE|MON_AUTHDECIDE, mm_answer_gss_userok}, {MONITOR_REQ_GSSCHECKMIC, MON_ONCE, mm_answer_gss_checkmic}, #endif {0, 0, NULL} }; struct mon_table mon_dispatch_postauth20[] = { #ifdef WITH_OPENSSL {MONITOR_REQ_MODULI, 0, mm_answer_moduli}, #endif {MONITOR_REQ_SIGN, 0, mm_answer_sign}, {MONITOR_REQ_PTY, 0, mm_answer_pty}, {MONITOR_REQ_PTYCLEANUP, 0, mm_answer_pty_cleanup}, {MONITOR_REQ_TERM, 0, mm_answer_term}, #ifdef SSH_AUDIT_EVENTS {MONITOR_REQ_AUDIT_EVENT, MON_PERMIT, mm_answer_audit_event}, {MONITOR_REQ_AUDIT_COMMAND, MON_PERMIT, mm_answer_audit_command}, #endif {0, 0, NULL} }; struct mon_table *mon_dispatch; /* Specifies if a certain message is allowed at the moment */ static void monitor_permit(struct mon_table *ent, enum monitor_reqtype type, int permit) { while (ent->f != NULL) { if (ent->type == type) { ent->flags &= ~MON_PERMIT; ent->flags |= permit ? MON_PERMIT : 0; return; } ent++; } } static void monitor_permit_authentications(int permit) { struct mon_table *ent = mon_dispatch; while (ent->f != NULL) { if (ent->flags & MON_AUTH) { ent->flags &= ~MON_PERMIT; ent->flags |= permit ? MON_PERMIT : 0; } ent++; } } void monitor_child_preauth(struct ssh *ssh, struct monitor *pmonitor) { struct mon_table *ent; int authenticated = 0, partial = 0; debug3("preauth child monitor started"); if (pmonitor->m_recvfd >= 0) close(pmonitor->m_recvfd); if (pmonitor->m_log_sendfd >= 0) close(pmonitor->m_log_sendfd); pmonitor->m_log_sendfd = pmonitor->m_recvfd = -1; authctxt = (Authctxt *)ssh->authctxt; memset(authctxt, 0, sizeof(*authctxt)); ssh->authctxt = authctxt; authctxt->loginmsg = loginmsg; mon_dispatch = mon_dispatch_proto20; /* Permit requests for moduli and signatures */ monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1); monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); /* The first few requests do not require asynchronous access */ while (!authenticated) { partial = 0; auth_method = "unknown"; auth_submethod = NULL; auth2_authctxt_reset_info(authctxt); authenticated = (monitor_read(ssh, pmonitor, mon_dispatch, &ent) == 1); /* Special handling for multiple required authentications */ if (options.num_auth_methods != 0) { if (authenticated && !auth2_update_methods_lists(authctxt, auth_method, auth_submethod)) { debug3_f("method %s: partial", auth_method); authenticated = 0; partial = 1; } } if (authenticated) { if (!(ent->flags & MON_AUTHDECIDE)) fatal_f("unexpected authentication from %d", ent->type); if (authctxt->pw->pw_uid == 0 && !auth_root_allowed(ssh, auth_method)) authenticated = 0; #ifdef USE_PAM /* PAM needs to perform account checks after auth */ if (options.use_pam && authenticated) { struct sshbuf *m; if ((m = sshbuf_new()) == NULL) fatal("%s: sshbuf_new failed", __func__); mm_request_receive_expect(pmonitor->m_sendfd, MONITOR_REQ_PAM_ACCOUNT, m); authenticated = mm_answer_pam_account( ssh, pmonitor->m_sendfd, m); sshbuf_free(m); } #endif } if (ent->flags & (MON_AUTHDECIDE|MON_ALOG)) { auth_log(ssh, authenticated, partial, auth_method, auth_submethod); if (!partial && !authenticated) authctxt->failures++; if (authenticated || partial) { auth2_update_session_info(authctxt, auth_method, auth_submethod); } } + if (authctxt->failures > options.max_authtries) { + /* Shouldn't happen */ + fatal_f("privsep child made too many authentication " + "attempts"); + } } if (!authctxt->valid) fatal_f("authenticated invalid user"); if (strcmp(auth_method, "unknown") == 0) fatal_f("authentication method name unknown"); debug_f("user %s authenticated by privileged process", authctxt->user); ssh->authctxt = NULL; ssh_packet_set_log_preamble(ssh, "user %s", authctxt->user); mm_get_keystate(ssh, pmonitor); /* Drain any buffered messages from the child */ while (pmonitor->m_log_recvfd != -1 && monitor_read_log(pmonitor) == 0) ; if (pmonitor->m_recvfd >= 0) close(pmonitor->m_recvfd); if (pmonitor->m_log_sendfd >= 0) close(pmonitor->m_log_sendfd); pmonitor->m_sendfd = pmonitor->m_log_recvfd = -1; } static void monitor_set_child_handler(pid_t pid) { monitor_child_pid = pid; } static void monitor_child_handler(int sig) { kill(monitor_child_pid, sig); } void monitor_child_postauth(struct ssh *ssh, struct monitor *pmonitor) { close(pmonitor->m_recvfd); pmonitor->m_recvfd = -1; monitor_set_child_handler(pmonitor->m_pid); ssh_signal(SIGHUP, &monitor_child_handler); ssh_signal(SIGTERM, &monitor_child_handler); ssh_signal(SIGINT, &monitor_child_handler); #ifdef SIGXFSZ ssh_signal(SIGXFSZ, SIG_IGN); #endif mon_dispatch = mon_dispatch_postauth20; /* Permit requests for moduli and signatures */ monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1); monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1); if (auth_opts->permit_pty_flag) { monitor_permit(mon_dispatch, MONITOR_REQ_PTY, 1); monitor_permit(mon_dispatch, MONITOR_REQ_PTYCLEANUP, 1); } for (;;) monitor_read(ssh, pmonitor, mon_dispatch, NULL); } static int monitor_read_log(struct monitor *pmonitor) { struct sshbuf *logmsg; u_int len, level, forced; char *msg; u_char *p; int r; if ((logmsg = sshbuf_new()) == NULL) fatal_f("sshbuf_new"); /* Read length */ if ((r = sshbuf_reserve(logmsg, 4, &p)) != 0) fatal_fr(r, "reserve len"); if (atomicio(read, pmonitor->m_log_recvfd, p, 4) != 4) { if (errno == EPIPE) { sshbuf_free(logmsg); debug_f("child log fd closed"); close(pmonitor->m_log_recvfd); pmonitor->m_log_recvfd = -1; return -1; } fatal_f("log fd read: %s", strerror(errno)); } if ((r = sshbuf_get_u32(logmsg, &len)) != 0) fatal_fr(r, "parse len"); if (len <= 4 || len > 8192) fatal_f("invalid log message length %u", len); /* Read severity, message */ sshbuf_reset(logmsg); if ((r = sshbuf_reserve(logmsg, len, &p)) != 0) fatal_fr(r, "reserve msg"); if (atomicio(read, pmonitor->m_log_recvfd, p, len) != len) fatal_f("log fd read: %s", strerror(errno)); if ((r = sshbuf_get_u32(logmsg, &level)) != 0 || (r = sshbuf_get_u32(logmsg, &forced)) != 0 || (r = sshbuf_get_cstring(logmsg, &msg, NULL)) != 0) fatal_fr(r, "parse"); /* Log it */ if (log_level_name(level) == NULL) fatal_f("invalid log level %u (corrupted message?)", level); sshlogdirect(level, forced, "%s [preauth]", msg); sshbuf_free(logmsg); free(msg); return 0; } static int monitor_read(struct ssh *ssh, struct monitor *pmonitor, struct mon_table *ent, struct mon_table **pent) { struct sshbuf *m; int r, ret; u_char type; struct pollfd pfd[2]; for (;;) { memset(&pfd, 0, sizeof(pfd)); pfd[0].fd = pmonitor->m_sendfd; pfd[0].events = POLLIN; pfd[1].fd = pmonitor->m_log_recvfd; pfd[1].events = pfd[1].fd == -1 ? 0 : POLLIN; if (poll(pfd, pfd[1].fd == -1 ? 1 : 2, -1) == -1) { if (errno == EINTR || errno == EAGAIN) continue; fatal_f("poll: %s", strerror(errno)); } if (pfd[1].revents) { /* * Drain all log messages before processing next * monitor request. */ monitor_read_log(pmonitor); continue; } if (pfd[0].revents) break; /* Continues below */ } if ((m = sshbuf_new()) == NULL) fatal_f("sshbuf_new"); mm_request_receive(pmonitor->m_sendfd, m); if ((r = sshbuf_get_u8(m, &type)) != 0) fatal_fr(r, "parse type"); debug3_f("checking request %d", type); while (ent->f != NULL) { if (ent->type == type) break; ent++; } if (ent->f != NULL) { if (!(ent->flags & MON_PERMIT)) fatal_f("unpermitted request %d", type); ret = (*ent->f)(ssh, pmonitor->m_sendfd, m); sshbuf_free(m); /* The child may use this request only once, disable it */ if (ent->flags & MON_ONCE) { debug2_f("%d used once, disabling now", type); ent->flags &= ~MON_PERMIT; } if (pent != NULL) *pent = ent; return ret; } fatal_f("unsupported request: %d", type); /* NOTREACHED */ return (-1); } /* allowed key state */ static int monitor_allowed_key(const u_char *blob, u_int bloblen) { /* make sure key is allowed */ if (key_blob == NULL || key_bloblen != bloblen || timingsafe_bcmp(key_blob, blob, key_bloblen)) return (0); return (1); } static void monitor_reset_key_state(void) { /* reset state */ free(key_blob); free(hostbased_cuser); free(hostbased_chost); sshauthopt_free(key_opts); key_blob = NULL; key_bloblen = 0; key_blobtype = MM_NOKEY; key_opts = NULL; hostbased_cuser = NULL; hostbased_chost = NULL; } #ifdef WITH_OPENSSL int mm_answer_moduli(struct ssh *ssh, int sock, struct sshbuf *m) { DH *dh; const BIGNUM *dh_p, *dh_g; int r; u_int min, want, max; if ((r = sshbuf_get_u32(m, &min)) != 0 || (r = sshbuf_get_u32(m, &want)) != 0 || (r = sshbuf_get_u32(m, &max)) != 0) fatal_fr(r, "parse"); debug3_f("got parameters: %d %d %d", min, want, max); /* We need to check here, too, in case the child got corrupted */ if (max < min || want < min || max < want) fatal_f("bad parameters: %d %d %d", min, want, max); sshbuf_reset(m); dh = choose_dh(min, want, max); if (dh == NULL) { if ((r = sshbuf_put_u8(m, 0)) != 0) fatal_fr(r, "assemble empty"); return (0); } else { /* Send first bignum */ DH_get0_pqg(dh, &dh_p, NULL, &dh_g); if ((r = sshbuf_put_u8(m, 1)) != 0 || (r = sshbuf_put_bignum2(m, dh_p)) != 0 || (r = sshbuf_put_bignum2(m, dh_g)) != 0) fatal_fr(r, "assemble"); DH_free(dh); } mm_request_send(sock, MONITOR_ANS_MODULI, m); return (0); } #endif int mm_answer_sign(struct ssh *ssh, int sock, struct sshbuf *m) { extern int auth_sock; /* XXX move to state struct? */ struct sshkey *key; struct sshbuf *sigbuf = NULL; u_char *p = NULL, *signature = NULL; char *alg = NULL; size_t datlen, siglen, alglen; int r, is_proof = 0; u_int keyid, compat; const char proof_req[] = "hostkeys-prove-00@openssh.com"; debug3_f("entering"); if ((r = sshbuf_get_u32(m, &keyid)) != 0 || (r = sshbuf_get_string(m, &p, &datlen)) != 0 || (r = sshbuf_get_cstring(m, &alg, &alglen)) != 0 || (r = sshbuf_get_u32(m, &compat)) != 0) fatal_fr(r, "parse"); if (keyid > INT_MAX) fatal_f("invalid key ID"); /* * Supported KEX types use SHA1 (20 bytes), SHA256 (32 bytes), * SHA384 (48 bytes) and SHA512 (64 bytes). * * Otherwise, verify the signature request is for a hostkey * proof. * * XXX perform similar check for KEX signature requests too? * it's not trivial, since what is signed is the hash, rather * than the full kex structure... */ if (datlen != 20 && datlen != 32 && datlen != 48 && datlen != 64) { /* * Construct expected hostkey proof and compare it to what * the client sent us. */ if (session_id2_len == 0) /* hostkeys is never first */ fatal_f("bad data length: %zu", datlen); if ((key = get_hostkey_public_by_index(keyid, ssh)) == NULL) fatal_f("no hostkey for index %d", keyid); if ((sigbuf = sshbuf_new()) == NULL) fatal_f("sshbuf_new"); if ((r = sshbuf_put_cstring(sigbuf, proof_req)) != 0 || (r = sshbuf_put_string(sigbuf, session_id2, session_id2_len)) != 0 || (r = sshkey_puts(key, sigbuf)) != 0) fatal_fr(r, "assemble private key proof"); if (datlen != sshbuf_len(sigbuf) || memcmp(p, sshbuf_ptr(sigbuf), sshbuf_len(sigbuf)) != 0) fatal_f("bad data length: %zu, hostkey proof len %zu", datlen, sshbuf_len(sigbuf)); sshbuf_free(sigbuf); is_proof = 1; } /* save session id, it will be passed on the first call */ if (session_id2_len == 0) { session_id2_len = datlen; session_id2 = xmalloc(session_id2_len); memcpy(session_id2, p, session_id2_len); } if ((key = get_hostkey_by_index(keyid)) != NULL) { if ((r = sshkey_sign(key, &signature, &siglen, p, datlen, alg, options.sk_provider, NULL, compat)) != 0) fatal_fr(r, "sign"); } else if ((key = get_hostkey_public_by_index(keyid, ssh)) != NULL && auth_sock > 0) { if ((r = ssh_agent_sign(auth_sock, key, &signature, &siglen, p, datlen, alg, compat)) != 0) fatal_fr(r, "agent sign"); } else fatal_f("no hostkey from index %d", keyid); debug3_f("%s %s signature len=%zu", alg, is_proof ? "hostkey proof" : "KEX", siglen); sshbuf_reset(m); if ((r = sshbuf_put_string(m, signature, siglen)) != 0) fatal_fr(r, "assemble"); free(alg); free(p); free(signature); mm_request_send(sock, MONITOR_ANS_SIGN, m); /* Turn on permissions for getpwnam */ monitor_permit(mon_dispatch, MONITOR_REQ_PWNAM, 1); return (0); } #define PUTPW(b, id) \ do { \ if ((r = sshbuf_put_string(b, \ &pwent->id, sizeof(pwent->id))) != 0) \ fatal_fr(r, "assemble %s", #id); \ } while (0) /* Retrieves the password entry and also checks if the user is permitted */ int mm_answer_pwnamallow(struct ssh *ssh, int sock, struct sshbuf *m) { struct passwd *pwent; int r, allowed = 0; u_int i; debug3_f("entering"); if (authctxt->attempt++ != 0) fatal_f("multiple attempts for getpwnam"); if ((r = sshbuf_get_cstring(m, &authctxt->user, NULL)) != 0) fatal_fr(r, "parse"); pwent = getpwnamallow(ssh, authctxt->user); setproctitle("%s [priv]", pwent ? authctxt->user : "unknown"); sshbuf_reset(m); if (pwent == NULL) { if ((r = sshbuf_put_u8(m, 0)) != 0) fatal_fr(r, "assemble fakepw"); authctxt->pw = fakepw(); goto out; } allowed = 1; authctxt->pw = pwent; authctxt->valid = 1; /* XXX send fake class/dir/shell, etc. */ if ((r = sshbuf_put_u8(m, 1)) != 0) fatal_fr(r, "assemble ok"); PUTPW(m, pw_uid); PUTPW(m, pw_gid); #ifdef HAVE_STRUCT_PASSWD_PW_CHANGE PUTPW(m, pw_change); #endif #ifdef HAVE_STRUCT_PASSWD_PW_EXPIRE PUTPW(m, pw_expire); #endif if ((r = sshbuf_put_cstring(m, pwent->pw_name)) != 0 || (r = sshbuf_put_cstring(m, "*")) != 0 || #ifdef HAVE_STRUCT_PASSWD_PW_GECOS (r = sshbuf_put_cstring(m, pwent->pw_gecos)) != 0 || #endif #ifdef HAVE_STRUCT_PASSWD_PW_CLASS (r = sshbuf_put_cstring(m, pwent->pw_class)) != 0 || #endif (r = sshbuf_put_cstring(m, pwent->pw_dir)) != 0 || (r = sshbuf_put_cstring(m, pwent->pw_shell)) != 0) fatal_fr(r, "assemble pw"); out: ssh_packet_set_log_preamble(ssh, "%suser %s", authctxt->valid ? "authenticating" : "invalid ", authctxt->user); if ((r = sshbuf_put_string(m, &options, sizeof(options))) != 0) fatal_fr(r, "assemble options"); #define M_CP_STROPT(x) do { \ if (options.x != NULL && \ (r = sshbuf_put_cstring(m, options.x)) != 0) \ fatal_fr(r, "assemble %s", #x); \ } while (0) #define M_CP_STRARRAYOPT(x, nx) do { \ for (i = 0; i < options.nx; i++) { \ if ((r = sshbuf_put_cstring(m, options.x[i])) != 0) \ fatal_fr(r, "assemble %s", #x); \ } \ } while (0) /* See comment in servconf.h */ COPY_MATCH_STRING_OPTS(); #undef M_CP_STROPT #undef M_CP_STRARRAYOPT /* Create valid auth method lists */ if (auth2_setup_methods_lists(authctxt) != 0) { /* * The monitor will continue long enough to let the child * run to its packet_disconnect(), but it must not allow any * authentication to succeed. */ debug_f("no valid authentication method lists"); } debug3_f("sending MONITOR_ANS_PWNAM: %d", allowed); mm_request_send(sock, MONITOR_ANS_PWNAM, m); /* Allow service/style information on the auth context */ monitor_permit(mon_dispatch, MONITOR_REQ_AUTHSERV, 1); monitor_permit(mon_dispatch, MONITOR_REQ_AUTH2_READ_BANNER, 1); #ifdef USE_PAM if (options.use_pam) monitor_permit(mon_dispatch, MONITOR_REQ_PAM_START, 1); #endif return (0); } int mm_answer_auth2_read_banner(struct ssh *ssh, int sock, struct sshbuf *m) { char *banner; int r; sshbuf_reset(m); banner = auth2_read_banner(); if ((r = sshbuf_put_cstring(m, banner != NULL ? banner : "")) != 0) fatal_fr(r, "assemble"); mm_request_send(sock, MONITOR_ANS_AUTH2_READ_BANNER, m); free(banner); return (0); } int mm_answer_authserv(struct ssh *ssh, int sock, struct sshbuf *m) { int r; monitor_permit_authentications(1); if ((r = sshbuf_get_cstring(m, &authctxt->service, NULL)) != 0 || (r = sshbuf_get_cstring(m, &authctxt->style, NULL)) != 0) fatal_fr(r, "parse"); debug3_f("service=%s, style=%s", authctxt->service, authctxt->style); if (strlen(authctxt->style) == 0) { free(authctxt->style); authctxt->style = NULL; } return (0); } /* * Check that the key type appears in the supplied pattern list, ignoring * mismatches in the signature algorithm. (Signature algorithm checks are * performed in the unprivileged authentication code). * Returns 1 on success, 0 otherwise. */ static int key_base_type_match(const char *method, const struct sshkey *key, const char *list) { char *s, *l, *ol = xstrdup(list); int found = 0; l = ol; for ((s = strsep(&l, ",")); s && *s != '\0'; (s = strsep(&l, ","))) { if (sshkey_type_from_name(s) == key->type) { found = 1; break; } } if (!found) { error("%s key type %s is not in permitted list %s", method, sshkey_ssh_name(key), list); } free(ol); return found; } int mm_answer_authpassword(struct ssh *ssh, int sock, struct sshbuf *m) { static int call_count; char *passwd; int r, authenticated; size_t plen; if (!options.password_authentication) fatal_f("password authentication not enabled"); if ((r = sshbuf_get_cstring(m, &passwd, &plen)) != 0) fatal_fr(r, "parse"); /* Only authenticate if the context is valid */ authenticated = options.password_authentication && auth_password(ssh, passwd); freezero(passwd, plen); sshbuf_reset(m); if ((r = sshbuf_put_u32(m, authenticated)) != 0) fatal_fr(r, "assemble"); #ifdef USE_PAM if ((r = sshbuf_put_u32(m, sshpam_get_maxtries_reached())) != 0) fatal_fr(r, "assemble PAM"); #endif debug3("%s: sending result %d", __func__, authenticated); debug3_f("sending result %d", authenticated); mm_request_send(sock, MONITOR_ANS_AUTHPASSWORD, m); call_count++; if (plen == 0 && call_count == 1) auth_method = "none"; else auth_method = "password"; /* Causes monitor loop to terminate if authenticated */ return (authenticated); } #ifdef BSD_AUTH int mm_answer_bsdauthquery(struct ssh *ssh, int sock, struct sshbuf *m) { char *name, *infotxt; u_int numprompts, *echo_on, success; char **prompts; int r; if (!options.kbd_interactive_authentication) fatal_f("kbd-int authentication not enabled"); success = bsdauth_query(authctxt, &name, &infotxt, &numprompts, &prompts, &echo_on) < 0 ? 0 : 1; sshbuf_reset(m); if ((r = sshbuf_put_u32(m, success)) != 0) fatal_fr(r, "assemble"); if (success) { if ((r = sshbuf_put_cstring(m, prompts[0])) != 0) fatal_fr(r, "assemble prompt"); } debug3_f("sending challenge success: %u", success); mm_request_send(sock, MONITOR_ANS_BSDAUTHQUERY, m); if (success) { free(name); free(infotxt); free(prompts); free(echo_on); } return (0); } int mm_answer_bsdauthrespond(struct ssh *ssh, int sock, struct sshbuf *m) { char *response; int r, authok; if (!options.kbd_interactive_authentication) fatal_f("kbd-int authentication not enabled"); if (authctxt->as == NULL) fatal_f("no bsd auth session"); if ((r = sshbuf_get_cstring(m, &response, NULL)) != 0) fatal_fr(r, "parse"); authok = options.kbd_interactive_authentication && auth_userresponse(authctxt->as, response, 0); authctxt->as = NULL; debug3_f("<%s> = <%d>", response, authok); free(response); sshbuf_reset(m); if ((r = sshbuf_put_u32(m, authok)) != 0) fatal_fr(r, "assemble"); debug3_f("sending authenticated: %d", authok); mm_request_send(sock, MONITOR_ANS_BSDAUTHRESPOND, m); auth_method = "keyboard-interactive"; auth_submethod = "bsdauth"; return (authok != 0); } #endif #ifdef USE_PAM int mm_answer_pam_start(struct ssh *ssh, int sock, struct sshbuf *m) { if (!options.use_pam) fatal("UsePAM not set, but ended up in %s anyway", __func__); start_pam(ssh); monitor_permit(mon_dispatch, MONITOR_REQ_PAM_ACCOUNT, 1); if (options.kbd_interactive_authentication) monitor_permit(mon_dispatch, MONITOR_REQ_PAM_INIT_CTX, 1); return (0); } int mm_answer_pam_account(struct ssh *ssh, int sock, struct sshbuf *m) { u_int ret; int r; if (!options.use_pam) fatal("%s: PAM not enabled", __func__); ret = do_pam_account(); if ((r = sshbuf_put_u32(m, ret)) != 0 || (r = sshbuf_put_stringb(m, loginmsg)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); mm_request_send(sock, MONITOR_ANS_PAM_ACCOUNT, m); return (ret); } static void *sshpam_ctxt, *sshpam_authok; extern KbdintDevice sshpam_device; int mm_answer_pam_init_ctx(struct ssh *ssh, int sock, struct sshbuf *m) { u_int ok = 0; int r; debug3("%s", __func__); if (!options.kbd_interactive_authentication) fatal("%s: kbd-int authentication not enabled", __func__); if (sshpam_ctxt != NULL) fatal("%s: already called", __func__); sshpam_ctxt = (sshpam_device.init_ctx)(authctxt); sshpam_authok = NULL; sshbuf_reset(m); if (sshpam_ctxt != NULL) { monitor_permit(mon_dispatch, MONITOR_REQ_PAM_FREE_CTX, 1); monitor_permit(mon_dispatch, MONITOR_REQ_PAM_QUERY, 1); ok = 1; } if ((r = sshbuf_put_u32(m, ok)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); mm_request_send(sock, MONITOR_ANS_PAM_INIT_CTX, m); return (0); } int mm_answer_pam_query(struct ssh *ssh, int sock, struct sshbuf *m) { char *name = NULL, *info = NULL, **prompts = NULL; u_int i, num = 0, *echo_on = 0; int r, ret; debug3("%s", __func__); sshpam_authok = NULL; if (sshpam_ctxt == NULL) fatal("%s: no context", __func__); ret = (sshpam_device.query)(sshpam_ctxt, &name, &info, &num, &prompts, &echo_on); if (ret == 0 && num == 0) sshpam_authok = sshpam_ctxt; if (num > 1 || name == NULL || info == NULL) fatal("sshpam_device.query failed"); monitor_permit(mon_dispatch, MONITOR_REQ_PAM_RESPOND, 1); sshbuf_reset(m); if ((r = sshbuf_put_u32(m, ret)) != 0 || (r = sshbuf_put_cstring(m, name)) != 0 || (r = sshbuf_put_cstring(m, info)) != 0 || (r = sshbuf_put_u32(m, sshpam_get_maxtries_reached())) != 0 || (r = sshbuf_put_u32(m, num)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); free(name); free(info); for (i = 0; i < num; ++i) { if ((r = sshbuf_put_cstring(m, prompts[i])) != 0 || (r = sshbuf_put_u32(m, echo_on[i])) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); free(prompts[i]); } free(prompts); free(echo_on); auth_method = "keyboard-interactive"; auth_submethod = "pam"; mm_request_send(sock, MONITOR_ANS_PAM_QUERY, m); return (0); } int mm_answer_pam_respond(struct ssh *ssh, int sock, struct sshbuf *m) { char **resp; u_int i, num; int r, ret; debug3("%s", __func__); if (sshpam_ctxt == NULL) fatal("%s: no context", __func__); sshpam_authok = NULL; if ((r = sshbuf_get_u32(m, &num)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); if (num > PAM_MAX_NUM_MSG) { fatal_f("Too many PAM messages, got %u, expected <= %u", num, (unsigned)PAM_MAX_NUM_MSG); } if (num > 0) { resp = xcalloc(num, sizeof(char *)); for (i = 0; i < num; ++i) { if ((r = sshbuf_get_cstring(m, &(resp[i]), NULL)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); } ret = (sshpam_device.respond)(sshpam_ctxt, num, resp); for (i = 0; i < num; ++i) free(resp[i]); free(resp); } else { ret = (sshpam_device.respond)(sshpam_ctxt, num, NULL); } sshbuf_reset(m); if ((r = sshbuf_put_u32(m, ret)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); mm_request_send(sock, MONITOR_ANS_PAM_RESPOND, m); auth_method = "keyboard-interactive"; auth_submethod = "pam"; if (ret == 0) sshpam_authok = sshpam_ctxt; return (0); } int mm_answer_pam_free_ctx(struct ssh *ssh, int sock, struct sshbuf *m) { int r = sshpam_authok != NULL && sshpam_authok == sshpam_ctxt; debug3("%s", __func__); if (sshpam_ctxt == NULL) fatal("%s: no context", __func__); (sshpam_device.free_ctx)(sshpam_ctxt); sshpam_ctxt = sshpam_authok = NULL; sshbuf_reset(m); mm_request_send(sock, MONITOR_ANS_PAM_FREE_CTX, m); /* Allow another attempt */ monitor_permit(mon_dispatch, MONITOR_REQ_PAM_INIT_CTX, 1); auth_method = "keyboard-interactive"; auth_submethod = "pam"; return r; } #endif int mm_answer_keyallowed(struct ssh *ssh, int sock, struct sshbuf *m) { struct sshkey *key = NULL; char *cuser, *chost; u_int pubkey_auth_attempt; u_int type = 0; int r, allowed = 0; struct sshauthopt *opts = NULL; debug3_f("entering"); if ((r = sshbuf_get_u32(m, &type)) != 0 || (r = sshbuf_get_cstring(m, &cuser, NULL)) != 0 || (r = sshbuf_get_cstring(m, &chost, NULL)) != 0 || (r = sshkey_froms(m, &key)) != 0 || (r = sshbuf_get_u32(m, &pubkey_auth_attempt)) != 0) fatal_fr(r, "parse"); if (key != NULL && authctxt->valid) { switch (type) { case MM_USERKEY: auth_method = "publickey"; if (!options.pubkey_authentication) break; if (auth2_key_already_used(authctxt, key)) break; if (!key_base_type_match(auth_method, key, options.pubkey_accepted_algos)) break; allowed = user_key_allowed(ssh, authctxt->pw, key, pubkey_auth_attempt, &opts); break; case MM_HOSTKEY: auth_method = "hostbased"; if (!options.hostbased_authentication) break; if (auth2_key_already_used(authctxt, key)) break; if (!key_base_type_match(auth_method, key, options.hostbased_accepted_algos)) break; allowed = hostbased_key_allowed(ssh, authctxt->pw, cuser, chost, key); auth2_record_info(authctxt, "client user \"%.100s\", client host \"%.100s\"", cuser, chost); break; default: fatal_f("unknown key type %u", type); break; } } debug3_f("%s authentication%s: %s key is %s", auth_method, pubkey_auth_attempt ? "" : " test", (key == NULL || !authctxt->valid) ? "invalid" : sshkey_type(key), allowed ? "allowed" : "not allowed"); auth2_record_key(authctxt, 0, key); /* clear temporarily storage (used by verify) */ monitor_reset_key_state(); if (allowed) { /* Save temporarily for comparison in verify */ if ((r = sshkey_to_blob(key, &key_blob, &key_bloblen)) != 0) fatal_fr(r, "sshkey_to_blob"); key_blobtype = type; key_opts = opts; hostbased_cuser = cuser; hostbased_chost = chost; } else { /* Log failed attempt */ auth_log(ssh, 0, 0, auth_method, NULL); free(cuser); free(chost); } sshkey_free(key); sshbuf_reset(m); if ((r = sshbuf_put_u32(m, allowed)) != 0) fatal_fr(r, "assemble"); if (opts != NULL && (r = sshauthopt_serialise(opts, m, 1)) != 0) fatal_fr(r, "sshauthopt_serialise"); mm_request_send(sock, MONITOR_ANS_KEYALLOWED, m); if (!allowed) sshauthopt_free(opts); return (0); } static int monitor_valid_userblob(struct ssh *ssh, const u_char *data, u_int datalen) { struct sshbuf *b; struct sshkey *hostkey = NULL; const u_char *p; char *userstyle, *cp; size_t len; u_char type; int hostbound = 0, r, fail = 0; if ((b = sshbuf_from(data, datalen)) == NULL) fatal_f("sshbuf_from"); if (ssh->compat & SSH_OLD_SESSIONID) { p = sshbuf_ptr(b); len = sshbuf_len(b); if ((session_id2 == NULL) || (len < session_id2_len) || (timingsafe_bcmp(p, session_id2, session_id2_len) != 0)) fail++; if ((r = sshbuf_consume(b, session_id2_len)) != 0) fatal_fr(r, "consume"); } else { if ((r = sshbuf_get_string_direct(b, &p, &len)) != 0) fatal_fr(r, "parse sessionid"); if ((session_id2 == NULL) || (len != session_id2_len) || (timingsafe_bcmp(p, session_id2, session_id2_len) != 0)) fail++; } if ((r = sshbuf_get_u8(b, &type)) != 0) fatal_fr(r, "parse type"); if (type != SSH2_MSG_USERAUTH_REQUEST) fail++; if ((r = sshbuf_get_cstring(b, &cp, NULL)) != 0) fatal_fr(r, "parse userstyle"); xasprintf(&userstyle, "%s%s%s", authctxt->user, authctxt->style ? ":" : "", authctxt->style ? authctxt->style : ""); if (strcmp(userstyle, cp) != 0) { logit("wrong user name passed to monitor: " "expected %s != %.100s", userstyle, cp); fail++; } free(userstyle); free(cp); if ((r = sshbuf_skip_string(b)) != 0 || /* service */ (r = sshbuf_get_cstring(b, &cp, NULL)) != 0) fatal_fr(r, "parse method"); if (strcmp("publickey", cp) != 0) { if (strcmp("publickey-hostbound-v00@openssh.com", cp) == 0) hostbound = 1; else fail++; } free(cp); if ((r = sshbuf_get_u8(b, &type)) != 0) fatal_fr(r, "parse pktype"); if (type == 0) fail++; if ((r = sshbuf_skip_string(b)) != 0 || /* pkalg */ (r = sshbuf_skip_string(b)) != 0 || /* pkblob */ (hostbound && (r = sshkey_froms(b, &hostkey)) != 0)) fatal_fr(r, "parse pk"); if (sshbuf_len(b) != 0) fail++; sshbuf_free(b); if (hostkey != NULL) { /* * Ensure this is actually one of our hostkeys; unfortunately * can't check ssh->kex->initial_hostkey directly at this point * as packet state has not yet been exported to monitor. */ if (get_hostkey_index(hostkey, 1, ssh) == -1) fatal_f("hostbound hostkey does not match"); sshkey_free(hostkey); } return (fail == 0); } static int monitor_valid_hostbasedblob(const u_char *data, u_int datalen, const char *cuser, const char *chost) { struct sshbuf *b; const u_char *p; char *cp, *userstyle; size_t len; int r, fail = 0; u_char type; if ((b = sshbuf_from(data, datalen)) == NULL) fatal_f("sshbuf_new"); if ((r = sshbuf_get_string_direct(b, &p, &len)) != 0) fatal_fr(r, "parse sessionid"); if ((session_id2 == NULL) || (len != session_id2_len) || (timingsafe_bcmp(p, session_id2, session_id2_len) != 0)) fail++; if ((r = sshbuf_get_u8(b, &type)) != 0) fatal_fr(r, "parse type"); if (type != SSH2_MSG_USERAUTH_REQUEST) fail++; if ((r = sshbuf_get_cstring(b, &cp, NULL)) != 0) fatal_fr(r, "parse userstyle"); xasprintf(&userstyle, "%s%s%s", authctxt->user, authctxt->style ? ":" : "", authctxt->style ? authctxt->style : ""); if (strcmp(userstyle, cp) != 0) { logit("wrong user name passed to monitor: " "expected %s != %.100s", userstyle, cp); fail++; } free(userstyle); free(cp); if ((r = sshbuf_skip_string(b)) != 0 || /* service */ (r = sshbuf_get_cstring(b, &cp, NULL)) != 0) fatal_fr(r, "parse method"); if (strcmp(cp, "hostbased") != 0) fail++; free(cp); if ((r = sshbuf_skip_string(b)) != 0 || /* pkalg */ (r = sshbuf_skip_string(b)) != 0) /* pkblob */ fatal_fr(r, "parse pk"); /* verify client host, strip trailing dot if necessary */ if ((r = sshbuf_get_cstring(b, &cp, NULL)) != 0) fatal_fr(r, "parse host"); if (((len = strlen(cp)) > 0) && cp[len - 1] == '.') cp[len - 1] = '\0'; if (strcmp(cp, chost) != 0) fail++; free(cp); /* verify client user */ if ((r = sshbuf_get_cstring(b, &cp, NULL)) != 0) fatal_fr(r, "parse ruser"); if (strcmp(cp, cuser) != 0) fail++; free(cp); if (sshbuf_len(b) != 0) fail++; sshbuf_free(b); return (fail == 0); } int mm_answer_keyverify(struct ssh *ssh, int sock, struct sshbuf *m) { struct sshkey *key; const u_char *signature, *data, *blob; char *sigalg = NULL, *fp = NULL; size_t signaturelen, datalen, bloblen; int r, ret, req_presence = 0, req_verify = 0, valid_data = 0; int encoded_ret; struct sshkey_sig_details *sig_details = NULL; if ((r = sshbuf_get_string_direct(m, &blob, &bloblen)) != 0 || (r = sshbuf_get_string_direct(m, &signature, &signaturelen)) != 0 || (r = sshbuf_get_string_direct(m, &data, &datalen)) != 0 || (r = sshbuf_get_cstring(m, &sigalg, NULL)) != 0) fatal_fr(r, "parse"); if (hostbased_cuser == NULL || hostbased_chost == NULL || !monitor_allowed_key(blob, bloblen)) fatal_f("bad key, not previously allowed"); /* Empty signature algorithm means NULL. */ if (*sigalg == '\0') { free(sigalg); sigalg = NULL; } /* XXX use sshkey_froms here; need to change key_blob, etc. */ if ((r = sshkey_from_blob(blob, bloblen, &key)) != 0) fatal_fr(r, "parse key"); switch (key_blobtype) { case MM_USERKEY: valid_data = monitor_valid_userblob(ssh, data, datalen); auth_method = "publickey"; break; case MM_HOSTKEY: valid_data = monitor_valid_hostbasedblob(data, datalen, hostbased_cuser, hostbased_chost); auth_method = "hostbased"; break; default: valid_data = 0; break; } if (!valid_data) fatal_f("bad %s signature data blob", key_blobtype == MM_USERKEY ? "userkey" : (key_blobtype == MM_HOSTKEY ? "hostkey" : "unknown")); if ((fp = sshkey_fingerprint(key, options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) fatal_f("sshkey_fingerprint failed"); ret = sshkey_verify(key, signature, signaturelen, data, datalen, sigalg, ssh->compat, &sig_details); debug3_f("%s %s signature using %s %s%s%s", auth_method, sshkey_type(key), sigalg == NULL ? "default" : sigalg, (ret == 0) ? "verified" : "unverified", (ret != 0) ? ": " : "", (ret != 0) ? ssh_err(ret) : ""); if (ret == 0 && key_blobtype == MM_USERKEY && sig_details != NULL) { req_presence = (options.pubkey_auth_options & PUBKEYAUTH_TOUCH_REQUIRED) || !key_opts->no_require_user_presence; if (req_presence && (sig_details->sk_flags & SSH_SK_USER_PRESENCE_REQD) == 0) { error("public key %s %s signature for %s%s from %.128s " "port %d rejected: user presence " "(authenticator touch) requirement not met ", sshkey_type(key), fp, authctxt->valid ? "" : "invalid user ", authctxt->user, ssh_remote_ipaddr(ssh), ssh_remote_port(ssh)); ret = SSH_ERR_SIGNATURE_INVALID; } req_verify = (options.pubkey_auth_options & PUBKEYAUTH_VERIFY_REQUIRED) || key_opts->require_verify; if (req_verify && (sig_details->sk_flags & SSH_SK_USER_VERIFICATION_REQD) == 0) { error("public key %s %s signature for %s%s from %.128s " "port %d rejected: user verification requirement " "not met ", sshkey_type(key), fp, authctxt->valid ? "" : "invalid user ", authctxt->user, ssh_remote_ipaddr(ssh), ssh_remote_port(ssh)); ret = SSH_ERR_SIGNATURE_INVALID; } } auth2_record_key(authctxt, ret == 0, key); if (key_blobtype == MM_USERKEY) auth_activate_options(ssh, key_opts); monitor_reset_key_state(); sshbuf_reset(m); /* encode ret != 0 as positive integer, since we're sending u32 */ encoded_ret = (ret != 0); if ((r = sshbuf_put_u32(m, encoded_ret)) != 0 || (r = sshbuf_put_u8(m, sig_details != NULL)) != 0) fatal_fr(r, "assemble"); if (sig_details != NULL) { if ((r = sshbuf_put_u32(m, sig_details->sk_counter)) != 0 || (r = sshbuf_put_u8(m, sig_details->sk_flags)) != 0) fatal_fr(r, "assemble sk"); } sshkey_sig_details_free(sig_details); mm_request_send(sock, MONITOR_ANS_KEYVERIFY, m); free(sigalg); free(fp); sshkey_free(key); return ret == 0; } static void mm_record_login(struct ssh *ssh, Session *s, struct passwd *pw) { socklen_t fromlen; struct sockaddr_storage from; /* * Get IP address of client. If the connection is not a socket, let * the address be 0.0.0.0. */ memset(&from, 0, sizeof(from)); fromlen = sizeof(from); if (ssh_packet_connection_is_on_socket(ssh)) { if (getpeername(ssh_packet_get_connection_in(ssh), (struct sockaddr *)&from, &fromlen) == -1) { debug("getpeername: %.100s", strerror(errno)); cleanup_exit(255); } } /* Record that there was a login on that tty from the remote host. */ record_login(s->pid, s->tty, pw->pw_name, pw->pw_uid, session_get_remote_name_or_ip(ssh, utmp_len, options.use_dns), (struct sockaddr *)&from, fromlen); } static void mm_session_close(Session *s) { debug3_f("session %d pid %ld", s->self, (long)s->pid); if (s->ttyfd != -1) { debug3_f("tty %s ptyfd %d", s->tty, s->ptyfd); session_pty_cleanup2(s); } session_unused(s->self); } int mm_answer_pty(struct ssh *ssh, int sock, struct sshbuf *m) { extern struct monitor *pmonitor; Session *s; int r, res, fd0; debug3_f("entering"); sshbuf_reset(m); s = session_new(); if (s == NULL) goto error; s->authctxt = authctxt; s->pw = authctxt->pw; s->pid = pmonitor->m_pid; res = pty_allocate(&s->ptyfd, &s->ttyfd, s->tty, sizeof(s->tty)); if (res == 0) goto error; pty_setowner(authctxt->pw, s->tty); if ((r = sshbuf_put_u32(m, 1)) != 0 || (r = sshbuf_put_cstring(m, s->tty)) != 0) fatal_fr(r, "assemble"); /* We need to trick ttyslot */ if (dup2(s->ttyfd, 0) == -1) fatal_f("dup2"); mm_record_login(ssh, s, authctxt->pw); /* Now we can close the file descriptor again */ close(0); /* send messages generated by record_login */ if ((r = sshbuf_put_stringb(m, loginmsg)) != 0) fatal_fr(r, "assemble loginmsg"); sshbuf_reset(loginmsg); mm_request_send(sock, MONITOR_ANS_PTY, m); if (mm_send_fd(sock, s->ptyfd) == -1 || mm_send_fd(sock, s->ttyfd) == -1) fatal_f("send fds failed"); /* make sure nothing uses fd 0 */ if ((fd0 = open(_PATH_DEVNULL, O_RDONLY)) == -1) fatal_f("open(/dev/null): %s", strerror(errno)); if (fd0 != 0) error_f("fd0 %d != 0", fd0); /* slave side of pty is not needed */ close(s->ttyfd); s->ttyfd = s->ptyfd; /* no need to dup() because nobody closes ptyfd */ s->ptymaster = s->ptyfd; debug3_f("tty %s ptyfd %d", s->tty, s->ttyfd); return (0); error: if (s != NULL) mm_session_close(s); if ((r = sshbuf_put_u32(m, 0)) != 0) fatal_fr(r, "assemble 0"); mm_request_send(sock, MONITOR_ANS_PTY, m); return (0); } int mm_answer_pty_cleanup(struct ssh *ssh, int sock, struct sshbuf *m) { Session *s; char *tty; int r; debug3_f("entering"); if ((r = sshbuf_get_cstring(m, &tty, NULL)) != 0) fatal_fr(r, "parse tty"); if ((s = session_by_tty(tty)) != NULL) mm_session_close(s); sshbuf_reset(m); free(tty); return (0); } int mm_answer_term(struct ssh *ssh, int sock, struct sshbuf *req) { extern struct monitor *pmonitor; int res, status; debug3_f("tearing down sessions"); /* The child is terminating */ session_destroy_all(ssh, &mm_session_close); #ifdef USE_PAM if (options.use_pam) sshpam_cleanup(); #endif while (waitpid(pmonitor->m_pid, &status, 0) == -1) if (errno != EINTR) exit(1); res = WIFEXITED(status) ? WEXITSTATUS(status) : 1; /* Terminate process */ exit(res); } #ifdef SSH_AUDIT_EVENTS /* Report that an audit event occurred */ int mm_answer_audit_event(struct ssh *ssh, int socket, struct sshbuf *m) { u_int n; ssh_audit_event_t event; int r; debug3("%s entering", __func__); if ((r = sshbuf_get_u32(m, &n)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); event = (ssh_audit_event_t)n; switch (event) { case SSH_AUTH_FAIL_PUBKEY: case SSH_AUTH_FAIL_HOSTBASED: case SSH_AUTH_FAIL_GSSAPI: case SSH_LOGIN_EXCEED_MAXTRIES: case SSH_LOGIN_ROOT_DENIED: case SSH_CONNECTION_CLOSE: case SSH_INVALID_USER: audit_event(ssh, event); break; default: fatal("Audit event type %d not permitted", event); } return (0); } int mm_answer_audit_command(struct ssh *ssh, int socket, struct sshbuf *m) { char *cmd; int r; debug3("%s entering", __func__); if ((r = sshbuf_get_cstring(m, &cmd, NULL)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); /* sanity check command, if so how? */ audit_run_command(cmd); free(cmd); return (0); } #endif /* SSH_AUDIT_EVENTS */ void monitor_clear_keystate(struct ssh *ssh, struct monitor *pmonitor) { ssh_clear_newkeys(ssh, MODE_IN); ssh_clear_newkeys(ssh, MODE_OUT); sshbuf_free(child_state); child_state = NULL; } void monitor_apply_keystate(struct ssh *ssh, struct monitor *pmonitor) { struct kex *kex; int r; debug3_f("packet_set_state"); if ((r = ssh_packet_set_state(ssh, child_state)) != 0) fatal_fr(r, "packet_set_state"); sshbuf_free(child_state); child_state = NULL; if ((kex = ssh->kex) == NULL) fatal_f("internal error: ssh->kex == NULL"); if (session_id2_len != sshbuf_len(ssh->kex->session_id)) { fatal_f("incorrect session id length %zu (expected %u)", sshbuf_len(ssh->kex->session_id), session_id2_len); } if (memcmp(sshbuf_ptr(ssh->kex->session_id), session_id2, session_id2_len) != 0) fatal_f("session ID mismatch"); /* XXX set callbacks */ #ifdef WITH_OPENSSL kex->kex[KEX_DH_GRP1_SHA1] = kex_gen_server; kex->kex[KEX_DH_GRP14_SHA1] = kex_gen_server; kex->kex[KEX_DH_GRP14_SHA256] = kex_gen_server; kex->kex[KEX_DH_GRP16_SHA512] = kex_gen_server; kex->kex[KEX_DH_GRP18_SHA512] = kex_gen_server; kex->kex[KEX_DH_GEX_SHA1] = kexgex_server; kex->kex[KEX_DH_GEX_SHA256] = kexgex_server; # ifdef OPENSSL_HAS_ECC kex->kex[KEX_ECDH_SHA2] = kex_gen_server; # endif #endif /* WITH_OPENSSL */ kex->kex[KEX_C25519_SHA256] = kex_gen_server; kex->kex[KEX_KEM_SNTRUP761X25519_SHA512] = kex_gen_server; kex->load_host_public_key=&get_hostkey_public_by_type; kex->load_host_private_key=&get_hostkey_private_by_type; kex->host_key_index=&get_hostkey_index; kex->sign = sshd_hostkey_sign; } /* This function requires careful sanity checking */ void mm_get_keystate(struct ssh *ssh, struct monitor *pmonitor) { debug3_f("Waiting for new keys"); if ((child_state = sshbuf_new()) == NULL) fatal_f("sshbuf_new failed"); mm_request_receive_expect(pmonitor->m_sendfd, MONITOR_REQ_KEYEXPORT, child_state); debug3_f("GOT new keys"); } /* XXX */ #define FD_CLOSEONEXEC(x) do { \ if (fcntl(x, F_SETFD, FD_CLOEXEC) == -1) \ fatal("fcntl(%d, F_SETFD)", x); \ } while (0) static void monitor_openfds(struct monitor *mon, int do_logfds) { int pair[2]; #ifdef SO_ZEROIZE int on = 1; #endif if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) fatal_f("socketpair: %s", strerror(errno)); #ifdef SO_ZEROIZE if (setsockopt(pair[0], SOL_SOCKET, SO_ZEROIZE, &on, sizeof(on)) == -1) error("setsockopt SO_ZEROIZE(0): %.100s", strerror(errno)); if (setsockopt(pair[1], SOL_SOCKET, SO_ZEROIZE, &on, sizeof(on)) == -1) error("setsockopt SO_ZEROIZE(1): %.100s", strerror(errno)); #endif FD_CLOSEONEXEC(pair[0]); FD_CLOSEONEXEC(pair[1]); mon->m_recvfd = pair[0]; mon->m_sendfd = pair[1]; if (do_logfds) { if (pipe(pair) == -1) fatal_f("pipe: %s", strerror(errno)); FD_CLOSEONEXEC(pair[0]); FD_CLOSEONEXEC(pair[1]); mon->m_log_recvfd = pair[0]; mon->m_log_sendfd = pair[1]; } else mon->m_log_recvfd = mon->m_log_sendfd = -1; } #define MM_MEMSIZE 65536 struct monitor * monitor_init(void) { struct monitor *mon; mon = xcalloc(1, sizeof(*mon)); monitor_openfds(mon, 1); return mon; } void monitor_reinit(struct monitor *mon) { monitor_openfds(mon, 0); } #ifdef GSSAPI int mm_answer_gss_setup_ctx(struct ssh *ssh, int sock, struct sshbuf *m) { gss_OID_desc goid; OM_uint32 major; size_t len; u_char *p; int r; if (!options.gss_authentication) fatal_f("GSSAPI authentication not enabled"); if ((r = sshbuf_get_string(m, &p, &len)) != 0) fatal_fr(r, "parse"); goid.elements = p; goid.length = len; major = ssh_gssapi_server_ctx(&gsscontext, &goid); free(goid.elements); sshbuf_reset(m); if ((r = sshbuf_put_u32(m, major)) != 0) fatal_fr(r, "assemble"); mm_request_send(sock, MONITOR_ANS_GSSSETUP, m); /* Now we have a context, enable the step */ monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP, 1); return (0); } int mm_answer_gss_accept_ctx(struct ssh *ssh, int sock, struct sshbuf *m) { gss_buffer_desc in; gss_buffer_desc out = GSS_C_EMPTY_BUFFER; OM_uint32 major, minor; OM_uint32 flags = 0; /* GSI needs this */ int r; if (!options.gss_authentication) fatal_f("GSSAPI authentication not enabled"); if ((r = ssh_gssapi_get_buffer_desc(m, &in)) != 0) fatal_fr(r, "ssh_gssapi_get_buffer_desc"); major = ssh_gssapi_accept_ctx(gsscontext, &in, &out, &flags); free(in.value); sshbuf_reset(m); if ((r = sshbuf_put_u32(m, major)) != 0 || (r = sshbuf_put_string(m, out.value, out.length)) != 0 || (r = sshbuf_put_u32(m, flags)) != 0) fatal_fr(r, "assemble"); mm_request_send(sock, MONITOR_ANS_GSSSTEP, m); gss_release_buffer(&minor, &out); if (major == GSS_S_COMPLETE) { monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP, 0); monitor_permit(mon_dispatch, MONITOR_REQ_GSSUSEROK, 1); monitor_permit(mon_dispatch, MONITOR_REQ_GSSCHECKMIC, 1); } return (0); } int mm_answer_gss_checkmic(struct ssh *ssh, int sock, struct sshbuf *m) { gss_buffer_desc gssbuf, mic; OM_uint32 ret; int r; if (!options.gss_authentication) fatal_f("GSSAPI authentication not enabled"); if ((r = ssh_gssapi_get_buffer_desc(m, &gssbuf)) != 0 || (r = ssh_gssapi_get_buffer_desc(m, &mic)) != 0) fatal_fr(r, "ssh_gssapi_get_buffer_desc"); ret = ssh_gssapi_checkmic(gsscontext, &gssbuf, &mic); free(gssbuf.value); free(mic.value); sshbuf_reset(m); if ((r = sshbuf_put_u32(m, ret)) != 0) fatal_fr(r, "assemble"); mm_request_send(sock, MONITOR_ANS_GSSCHECKMIC, m); if (!GSS_ERROR(ret)) monitor_permit(mon_dispatch, MONITOR_REQ_GSSUSEROK, 1); return (0); } int mm_answer_gss_userok(struct ssh *ssh, int sock, struct sshbuf *m) { int r, authenticated; const char *displayname; if (!options.gss_authentication) fatal_f("GSSAPI authentication not enabled"); authenticated = authctxt->valid && ssh_gssapi_userok(authctxt->user); sshbuf_reset(m); if ((r = sshbuf_put_u32(m, authenticated)) != 0) fatal_fr(r, "assemble"); debug3_f("sending result %d", authenticated); mm_request_send(sock, MONITOR_ANS_GSSUSEROK, m); auth_method = "gssapi-with-mic"; if ((displayname = ssh_gssapi_displayname()) != NULL) auth2_record_info(authctxt, "%s", displayname); /* Monitor loop will terminate if authenticated */ return (authenticated); } #endif /* GSSAPI */ diff --git a/mux.c b/mux.c index 3a0f87674b95..d9d5e7d994ca 100644 --- a/mux.c +++ b/mux.c @@ -1,2372 +1,2374 @@ -/* $OpenBSD: mux.c,v 1.99 2023/08/04 06:32:40 dtucker Exp $ */ +/* $OpenBSD: mux.c,v 1.100 2023/08/18 01:37:41 djm Exp $ */ /* * Copyright (c) 2002-2008 Damien Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* ssh session multiplexing support */ #include "includes.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_PATHS_H #include #endif #ifdef HAVE_POLL_H #include #else # ifdef HAVE_SYS_POLL_H # include # endif #endif #ifdef HAVE_UTIL_H # include #endif #include "openbsd-compat/sys-queue.h" #include "xmalloc.h" #include "log.h" #include "ssh.h" #include "ssh2.h" #include "pathnames.h" #include "misc.h" #include "match.h" #include "sshbuf.h" #include "channels.h" #include "msg.h" #include "packet.h" #include "monitor_fdpass.h" #include "sshpty.h" #include "sshkey.h" #include "readconf.h" #include "clientloop.h" #include "ssherr.h" #include "misc.h" /* from ssh.c */ extern int tty_flag; extern Options options; extern char *host; extern struct sshbuf *command; extern volatile sig_atomic_t quit_pending; /* Context for session open confirmation callback */ struct mux_session_confirm_ctx { u_int want_tty; u_int want_subsys; u_int want_x_fwd; u_int want_agent_fwd; struct sshbuf *cmd; char *term; struct termios tio; char **env; u_int rid; }; /* Context for stdio fwd open confirmation callback */ struct mux_stdio_confirm_ctx { u_int rid; }; /* Context for global channel callback */ struct mux_channel_confirm_ctx { u_int cid; /* channel id */ u_int rid; /* request id */ int fid; /* forward id */ }; /* fd to control socket */ int muxserver_sock = -1; /* client request id */ u_int muxclient_request_id = 0; /* Multiplexing control command */ u_int muxclient_command = 0; /* Set when signalled. */ static volatile sig_atomic_t muxclient_terminate = 0; /* PID of multiplex server */ static u_int muxserver_pid = 0; static Channel *mux_listener_channel = NULL; struct mux_master_state { int hello_rcvd; }; /* mux protocol messages */ #define MUX_MSG_HELLO 0x00000001 #define MUX_C_NEW_SESSION 0x10000002 #define MUX_C_ALIVE_CHECK 0x10000004 #define MUX_C_TERMINATE 0x10000005 #define MUX_C_OPEN_FWD 0x10000006 #define MUX_C_CLOSE_FWD 0x10000007 #define MUX_C_NEW_STDIO_FWD 0x10000008 #define MUX_C_STOP_LISTENING 0x10000009 #define MUX_C_PROXY 0x1000000f #define MUX_S_OK 0x80000001 #define MUX_S_PERMISSION_DENIED 0x80000002 #define MUX_S_FAILURE 0x80000003 #define MUX_S_EXIT_MESSAGE 0x80000004 #define MUX_S_ALIVE 0x80000005 #define MUX_S_SESSION_OPENED 0x80000006 #define MUX_S_REMOTE_PORT 0x80000007 #define MUX_S_TTY_ALLOC_FAIL 0x80000008 #define MUX_S_PROXY 0x8000000f /* type codes for MUX_C_OPEN_FWD and MUX_C_CLOSE_FWD */ #define MUX_FWD_LOCAL 1 #define MUX_FWD_REMOTE 2 #define MUX_FWD_DYNAMIC 3 static void mux_session_confirm(struct ssh *, int, int, void *); static void mux_stdio_confirm(struct ssh *, int, int, void *); static int mux_master_process_hello(struct ssh *, u_int, Channel *, struct sshbuf *, struct sshbuf *); static int mux_master_process_new_session(struct ssh *, u_int, Channel *, struct sshbuf *, struct sshbuf *); static int mux_master_process_alive_check(struct ssh *, u_int, Channel *, struct sshbuf *, struct sshbuf *); static int mux_master_process_terminate(struct ssh *, u_int, Channel *, struct sshbuf *, struct sshbuf *); static int mux_master_process_open_fwd(struct ssh *, u_int, Channel *, struct sshbuf *, struct sshbuf *); static int mux_master_process_close_fwd(struct ssh *, u_int, Channel *, struct sshbuf *, struct sshbuf *); static int mux_master_process_stdio_fwd(struct ssh *, u_int, Channel *, struct sshbuf *, struct sshbuf *); static int mux_master_process_stop_listening(struct ssh *, u_int, Channel *, struct sshbuf *, struct sshbuf *); static int mux_master_process_proxy(struct ssh *, u_int, Channel *, struct sshbuf *, struct sshbuf *); static const struct { u_int type; int (*handler)(struct ssh *, u_int, Channel *, struct sshbuf *, struct sshbuf *); } mux_master_handlers[] = { { MUX_MSG_HELLO, mux_master_process_hello }, { MUX_C_NEW_SESSION, mux_master_process_new_session }, { MUX_C_ALIVE_CHECK, mux_master_process_alive_check }, { MUX_C_TERMINATE, mux_master_process_terminate }, { MUX_C_OPEN_FWD, mux_master_process_open_fwd }, { MUX_C_CLOSE_FWD, mux_master_process_close_fwd }, { MUX_C_NEW_STDIO_FWD, mux_master_process_stdio_fwd }, { MUX_C_STOP_LISTENING, mux_master_process_stop_listening }, { MUX_C_PROXY, mux_master_process_proxy }, { 0, NULL } }; /* Cleanup callback fired on closure of mux client _session_ channel */ static void mux_master_session_cleanup_cb(struct ssh *ssh, int cid, int force, void *unused) { Channel *cc, *c = channel_by_id(ssh, cid); debug3_f("entering for channel %d", cid); if (c == NULL) fatal_f("channel_by_id(%i) == NULL", cid); if (c->ctl_chan != -1) { if ((cc = channel_by_id(ssh, c->ctl_chan)) == NULL) fatal_f("channel %d missing control channel %d", c->self, c->ctl_chan); c->ctl_chan = -1; cc->remote_id = 0; cc->have_remote_id = 0; chan_rcvd_oclose(ssh, cc); } channel_cancel_cleanup(ssh, c->self); } /* Cleanup callback fired on closure of mux client _control_ channel */ static void mux_master_control_cleanup_cb(struct ssh *ssh, int cid, int force, void *unused) { Channel *sc, *c = channel_by_id(ssh, cid); debug3_f("entering for channel %d", cid); if (c == NULL) fatal_f("channel_by_id(%i) == NULL", cid); if (c->have_remote_id) { if ((sc = channel_by_id(ssh, c->remote_id)) == NULL) fatal_f("channel %d missing session channel %u", c->self, c->remote_id); c->remote_id = 0; c->have_remote_id = 0; sc->ctl_chan = -1; if (sc->type != SSH_CHANNEL_OPEN && sc->type != SSH_CHANNEL_OPENING) { debug2_f("channel %d: not open", sc->self); chan_mark_dead(ssh, sc); } else { if (sc->istate == CHAN_INPUT_OPEN) chan_read_failed(ssh, sc); if (sc->ostate == CHAN_OUTPUT_OPEN) chan_write_failed(ssh, sc); } } channel_cancel_cleanup(ssh, c->self); } /* Check mux client environment variables before passing them to mux master. */ static int env_permitted(const char *env) { u_int i; int ret; char name[1024], *cp; if ((cp = strchr(env, '=')) == NULL || cp == env) return 0; ret = snprintf(name, sizeof(name), "%.*s", (int)(cp - env), env); if (ret <= 0 || (size_t)ret >= sizeof(name)) { error_f("name '%.100s...' too long", env); return 0; } for (i = 0; i < options.num_send_env; i++) if (match_pattern(name, options.send_env[i])) return 1; return 0; } /* Mux master protocol message handlers */ static int mux_master_process_hello(struct ssh *ssh, u_int rid, Channel *c, struct sshbuf *m, struct sshbuf *reply) { u_int ver; struct mux_master_state *state = (struct mux_master_state *)c->mux_ctx; int r; if (state == NULL) fatal_f("channel %d: c->mux_ctx == NULL", c->self); if (state->hello_rcvd) { error_f("HELLO received twice"); return -1; } if ((r = sshbuf_get_u32(m, &ver)) != 0) { error_fr(r, "parse"); return -1; } if (ver != SSHMUX_VER) { error_f("unsupported multiplexing protocol version %u " "(expected %u)", ver, SSHMUX_VER); return -1; } debug2_f("channel %d client version %u", c->self, ver); /* No extensions are presently defined */ while (sshbuf_len(m) > 0) { char *name = NULL; size_t value_len = 0; if ((r = sshbuf_get_cstring(m, &name, NULL)) != 0 || (r = sshbuf_get_string_direct(m, NULL, &value_len)) != 0) { error_fr(r, "parse extension"); return -1; } debug2_f("Unrecognised extension \"%s\" length %zu", name, value_len); free(name); } state->hello_rcvd = 1; return 0; } /* Enqueue a "ok" response to the reply buffer */ static void reply_ok(struct sshbuf *reply, u_int rid) { int r; if ((r = sshbuf_put_u32(reply, MUX_S_OK)) != 0 || (r = sshbuf_put_u32(reply, rid)) != 0) fatal_fr(r, "reply"); } /* Enqueue an error response to the reply buffer */ static void reply_error(struct sshbuf *reply, u_int type, u_int rid, const char *msg) { int r; if ((r = sshbuf_put_u32(reply, type)) != 0 || (r = sshbuf_put_u32(reply, rid)) != 0 || (r = sshbuf_put_cstring(reply, msg)) != 0) fatal_fr(r, "reply"); } static int mux_master_process_new_session(struct ssh *ssh, u_int rid, Channel *c, struct sshbuf *m, struct sshbuf *reply) { Channel *nc; struct mux_session_confirm_ctx *cctx; char *cmd, *cp; u_int i, j, env_len, escape_char, window, packetmax; int r, new_fd[3]; /* Reply for SSHMUX_COMMAND_OPEN */ cctx = xcalloc(1, sizeof(*cctx)); cctx->term = NULL; cctx->rid = rid; cmd = NULL; cctx->env = NULL; env_len = 0; if ((r = sshbuf_skip_string(m)) != 0 || /* reserved */ (r = sshbuf_get_u32(m, &cctx->want_tty)) != 0 || (r = sshbuf_get_u32(m, &cctx->want_x_fwd)) != 0 || (r = sshbuf_get_u32(m, &cctx->want_agent_fwd)) != 0 || (r = sshbuf_get_u32(m, &cctx->want_subsys)) != 0 || (r = sshbuf_get_u32(m, &escape_char)) != 0 || (r = sshbuf_get_cstring(m, &cctx->term, NULL)) != 0 || (r = sshbuf_get_cstring(m, &cmd, NULL)) != 0) { malf: free(cmd); for (j = 0; j < env_len; j++) free(cctx->env[j]); free(cctx->env); free(cctx->term); free(cctx); error_f("malformed message"); return -1; } #define MUX_MAX_ENV_VARS 4096 while (sshbuf_len(m) > 0) { if ((r = sshbuf_get_cstring(m, &cp, NULL)) != 0) goto malf; if (!env_permitted(cp)) { free(cp); continue; } cctx->env = xreallocarray(cctx->env, env_len + 2, sizeof(*cctx->env)); cctx->env[env_len++] = cp; cctx->env[env_len] = NULL; if (env_len > MUX_MAX_ENV_VARS) { error_f(">%d environment variables received, " "ignoring additional", MUX_MAX_ENV_VARS); break; } } debug2_f("channel %d: request tty %d, X %d, agent %d, subsys %d, " "term \"%s\", cmd \"%s\", env %u", c->self, cctx->want_tty, cctx->want_x_fwd, cctx->want_agent_fwd, cctx->want_subsys, cctx->term, cmd, env_len); if ((cctx->cmd = sshbuf_new()) == NULL) fatal_f("sshbuf_new"); if ((r = sshbuf_put(cctx->cmd, cmd, strlen(cmd))) != 0) fatal_fr(r, "sshbuf_put"); free(cmd); cmd = NULL; /* Gather fds from client */ for(i = 0; i < 3; i++) { if ((new_fd[i] = mm_receive_fd(c->sock)) == -1) { error_f("failed to receive fd %d from client", i); for (j = 0; j < i; j++) close(new_fd[j]); for (j = 0; j < env_len; j++) free(cctx->env[j]); free(cctx->env); free(cctx->term); sshbuf_free(cctx->cmd); free(cctx); reply_error(reply, MUX_S_FAILURE, rid, "did not receive file descriptors"); return -1; } } debug3_f("got fds stdin %d, stdout %d, stderr %d", new_fd[0], new_fd[1], new_fd[2]); /* XXX support multiple child sessions in future */ if (c->have_remote_id) { debug2_f("session already open"); reply_error(reply, MUX_S_FAILURE, rid, "Multiple sessions not supported"); cleanup: close(new_fd[0]); close(new_fd[1]); close(new_fd[2]); free(cctx->term); if (env_len != 0) { for (i = 0; i < env_len; i++) free(cctx->env[i]); free(cctx->env); } sshbuf_free(cctx->cmd); free(cctx); return 0; } if (options.control_master == SSHCTL_MASTER_ASK || options.control_master == SSHCTL_MASTER_AUTO_ASK) { if (!ask_permission("Allow shared connection to %s? ", host)) { debug2_f("session refused by user"); reply_error(reply, MUX_S_PERMISSION_DENIED, rid, "Permission denied"); goto cleanup; } } /* Try to pick up ttymodes from client before it goes raw */ if (cctx->want_tty && tcgetattr(new_fd[0], &cctx->tio) == -1) error_f("tcgetattr: %s", strerror(errno)); window = CHAN_SES_WINDOW_DEFAULT; packetmax = CHAN_SES_PACKET_DEFAULT; if (cctx->want_tty) { window >>= 1; packetmax >>= 1; } nc = channel_new(ssh, "session", SSH_CHANNEL_OPENING, new_fd[0], new_fd[1], new_fd[2], window, packetmax, CHAN_EXTENDED_WRITE, "client-session", CHANNEL_NONBLOCK_STDIO); nc->ctl_chan = c->self; /* link session -> control channel */ c->remote_id = nc->self; /* link control -> session channel */ c->have_remote_id = 1; if (cctx->want_tty && escape_char != 0xffffffff) { channel_register_filter(ssh, nc->self, client_simple_escape_filter, NULL, client_filter_cleanup, client_new_escape_filter_ctx((int)escape_char)); } debug2_f("channel_new: %d linked to control channel %d", nc->self, nc->ctl_chan); channel_send_open(ssh, nc->self); channel_register_open_confirm(ssh, nc->self, mux_session_confirm, cctx); c->mux_pause = 1; /* stop handling messages until open_confirm done */ channel_register_cleanup(ssh, nc->self, mux_master_session_cleanup_cb, 1); /* reply is deferred, sent by mux_session_confirm */ return 0; } static int mux_master_process_alive_check(struct ssh *ssh, u_int rid, Channel *c, struct sshbuf *m, struct sshbuf *reply) { int r; debug2_f("channel %d: alive check", c->self); /* prepare reply */ if ((r = sshbuf_put_u32(reply, MUX_S_ALIVE)) != 0 || (r = sshbuf_put_u32(reply, rid)) != 0 || (r = sshbuf_put_u32(reply, (u_int)getpid())) != 0) fatal_fr(r, "reply"); return 0; } static int mux_master_process_terminate(struct ssh *ssh, u_int rid, Channel *c, struct sshbuf *m, struct sshbuf *reply) { debug2_f("channel %d: terminate request", c->self); if (options.control_master == SSHCTL_MASTER_ASK || options.control_master == SSHCTL_MASTER_AUTO_ASK) { if (!ask_permission("Terminate shared connection to %s? ", host)) { debug2_f("termination refused by user"); reply_error(reply, MUX_S_PERMISSION_DENIED, rid, "Permission denied"); return 0; } } quit_pending = 1; reply_ok(reply, rid); /* XXX exit happens too soon - message never makes it to client */ return 0; } static char * format_forward(u_int ftype, struct Forward *fwd) { char *ret; switch (ftype) { case MUX_FWD_LOCAL: xasprintf(&ret, "local forward %.200s:%d -> %.200s:%d", (fwd->listen_path != NULL) ? fwd->listen_path : (fwd->listen_host == NULL) ? (options.fwd_opts.gateway_ports ? "*" : "LOCALHOST") : fwd->listen_host, fwd->listen_port, (fwd->connect_path != NULL) ? fwd->connect_path : fwd->connect_host, fwd->connect_port); break; case MUX_FWD_DYNAMIC: xasprintf(&ret, "dynamic forward %.200s:%d -> *", (fwd->listen_host == NULL) ? (options.fwd_opts.gateway_ports ? "*" : "LOCALHOST") : fwd->listen_host, fwd->listen_port); break; case MUX_FWD_REMOTE: xasprintf(&ret, "remote forward %.200s:%d -> %.200s:%d", (fwd->listen_path != NULL) ? fwd->listen_path : (fwd->listen_host == NULL) ? "LOCALHOST" : fwd->listen_host, fwd->listen_port, (fwd->connect_path != NULL) ? fwd->connect_path : fwd->connect_host, fwd->connect_port); break; default: fatal_f("unknown forward type %u", ftype); } return ret; } static int compare_host(const char *a, const char *b) { if (a == NULL && b == NULL) return 1; if (a == NULL || b == NULL) return 0; return strcmp(a, b) == 0; } static int compare_forward(struct Forward *a, struct Forward *b) { if (!compare_host(a->listen_host, b->listen_host)) return 0; if (!compare_host(a->listen_path, b->listen_path)) return 0; if (a->listen_port != b->listen_port) return 0; if (!compare_host(a->connect_host, b->connect_host)) return 0; if (!compare_host(a->connect_path, b->connect_path)) return 0; if (a->connect_port != b->connect_port) return 0; return 1; } static void mux_confirm_remote_forward(struct ssh *ssh, int type, u_int32_t seq, void *ctxt) { struct mux_channel_confirm_ctx *fctx = ctxt; char *failmsg = NULL; struct Forward *rfwd; Channel *c; struct sshbuf *out; u_int port; int r; if ((c = channel_by_id(ssh, fctx->cid)) == NULL) { /* no channel for reply */ error_f("unknown channel"); return; } if ((out = sshbuf_new()) == NULL) fatal_f("sshbuf_new"); if (fctx->fid >= options.num_remote_forwards || (options.remote_forwards[fctx->fid].connect_path == NULL && options.remote_forwards[fctx->fid].connect_host == NULL)) { xasprintf(&failmsg, "unknown forwarding id %d", fctx->fid); goto fail; } rfwd = &options.remote_forwards[fctx->fid]; debug_f("%s for: listen %d, connect %s:%d", type == SSH2_MSG_REQUEST_SUCCESS ? "success" : "failure", rfwd->listen_port, rfwd->connect_path ? rfwd->connect_path : rfwd->connect_host, rfwd->connect_port); if (type == SSH2_MSG_REQUEST_SUCCESS) { if (rfwd->listen_port == 0) { if ((r = sshpkt_get_u32(ssh, &port)) != 0) fatal_fr(r, "parse port"); if (port > 65535) { fatal("Invalid allocated port %u for " "mux remote forward to %s:%d", port, rfwd->connect_host, rfwd->connect_port); } rfwd->allocated_port = (int)port; debug("Allocated port %u for mux remote forward" " to %s:%d", rfwd->allocated_port, rfwd->connect_host, rfwd->connect_port); if ((r = sshbuf_put_u32(out, MUX_S_REMOTE_PORT)) != 0 || (r = sshbuf_put_u32(out, fctx->rid)) != 0 || (r = sshbuf_put_u32(out, rfwd->allocated_port)) != 0) fatal_fr(r, "reply"); channel_update_permission(ssh, rfwd->handle, rfwd->allocated_port); } else { reply_ok(out, fctx->rid); } goto out; } else { if (rfwd->listen_port == 0) channel_update_permission(ssh, rfwd->handle, -1); if (rfwd->listen_path != NULL) xasprintf(&failmsg, "remote port forwarding failed for " "listen path %s", rfwd->listen_path); else xasprintf(&failmsg, "remote port forwarding failed for " "listen port %d", rfwd->listen_port); debug2_f("clearing registered forwarding for listen %d, " "connect %s:%d", rfwd->listen_port, rfwd->connect_path ? rfwd->connect_path : rfwd->connect_host, rfwd->connect_port); free(rfwd->listen_host); free(rfwd->listen_path); free(rfwd->connect_host); free(rfwd->connect_path); memset(rfwd, 0, sizeof(*rfwd)); } fail: error_f("%s", failmsg); reply_error(out, MUX_S_FAILURE, fctx->rid, failmsg); free(failmsg); out: if ((r = sshbuf_put_stringb(c->output, out)) != 0) fatal_fr(r, "enqueue"); sshbuf_free(out); if (c->mux_pause <= 0) fatal_f("mux_pause %d", c->mux_pause); c->mux_pause = 0; /* start processing messages again */ } static int mux_master_process_open_fwd(struct ssh *ssh, u_int rid, Channel *c, struct sshbuf *m, struct sshbuf *reply) { struct Forward fwd; char *fwd_desc = NULL; char *listen_addr, *connect_addr; u_int ftype; u_int lport, cport; int r, i, ret = 0, freefwd = 1; memset(&fwd, 0, sizeof(fwd)); /* XXX - lport/cport check redundant */ if ((r = sshbuf_get_u32(m, &ftype)) != 0 || (r = sshbuf_get_cstring(m, &listen_addr, NULL)) != 0 || (r = sshbuf_get_u32(m, &lport)) != 0 || (r = sshbuf_get_cstring(m, &connect_addr, NULL)) != 0 || (r = sshbuf_get_u32(m, &cport)) != 0 || (lport != (u_int)PORT_STREAMLOCAL && lport > 65535) || (cport != (u_int)PORT_STREAMLOCAL && cport > 65535)) { error_f("malformed message"); ret = -1; goto out; } if (*listen_addr == '\0') { free(listen_addr); listen_addr = NULL; } if (*connect_addr == '\0') { free(connect_addr); connect_addr = NULL; } memset(&fwd, 0, sizeof(fwd)); fwd.listen_port = lport; if (fwd.listen_port == PORT_STREAMLOCAL) fwd.listen_path = listen_addr; else fwd.listen_host = listen_addr; fwd.connect_port = cport; if (fwd.connect_port == PORT_STREAMLOCAL) fwd.connect_path = connect_addr; else fwd.connect_host = connect_addr; debug2_f("channel %d: request %s", c->self, (fwd_desc = format_forward(ftype, &fwd))); if (ftype != MUX_FWD_LOCAL && ftype != MUX_FWD_REMOTE && ftype != MUX_FWD_DYNAMIC) { logit_f("invalid forwarding type %u", ftype); invalid: free(listen_addr); free(connect_addr); reply_error(reply, MUX_S_FAILURE, rid, "Invalid forwarding request"); return 0; } if (ftype == MUX_FWD_DYNAMIC && fwd.listen_path) { logit_f("streamlocal and dynamic forwards " "are mutually exclusive"); goto invalid; } if (fwd.listen_port != PORT_STREAMLOCAL && fwd.listen_port >= 65536) { logit_f("invalid listen port %u", fwd.listen_port); goto invalid; } if ((fwd.connect_port != PORT_STREAMLOCAL && fwd.connect_port >= 65536) || (ftype != MUX_FWD_DYNAMIC && ftype != MUX_FWD_REMOTE && fwd.connect_port == 0)) { logit_f("invalid connect port %u", fwd.connect_port); goto invalid; } if (ftype != MUX_FWD_DYNAMIC && fwd.connect_host == NULL && fwd.connect_path == NULL) { logit_f("missing connect host"); goto invalid; } /* Skip forwards that have already been requested */ switch (ftype) { case MUX_FWD_LOCAL: case MUX_FWD_DYNAMIC: for (i = 0; i < options.num_local_forwards; i++) { if (compare_forward(&fwd, options.local_forwards + i)) { exists: debug2_f("found existing forwarding"); reply_ok(reply, rid); goto out; } } break; case MUX_FWD_REMOTE: for (i = 0; i < options.num_remote_forwards; i++) { if (!compare_forward(&fwd, options.remote_forwards + i)) continue; if (fwd.listen_port != 0) goto exists; debug2_f("found allocated port"); if ((r = sshbuf_put_u32(reply, MUX_S_REMOTE_PORT)) != 0 || (r = sshbuf_put_u32(reply, rid)) != 0 || (r = sshbuf_put_u32(reply, options.remote_forwards[i].allocated_port)) != 0) fatal_fr(r, "reply FWD_REMOTE"); goto out; } break; } if (options.control_master == SSHCTL_MASTER_ASK || options.control_master == SSHCTL_MASTER_AUTO_ASK) { if (!ask_permission("Open %s on %s?", fwd_desc, host)) { debug2_f("forwarding refused by user"); reply_error(reply, MUX_S_PERMISSION_DENIED, rid, "Permission denied"); goto out; } } if (ftype == MUX_FWD_LOCAL || ftype == MUX_FWD_DYNAMIC) { if (!channel_setup_local_fwd_listener(ssh, &fwd, &options.fwd_opts)) { fail: logit_f("requested %s failed", fwd_desc); reply_error(reply, MUX_S_FAILURE, rid, "Port forwarding failed"); goto out; } add_local_forward(&options, &fwd); freefwd = 0; } else { struct mux_channel_confirm_ctx *fctx; fwd.handle = channel_request_remote_forwarding(ssh, &fwd); if (fwd.handle < 0) goto fail; add_remote_forward(&options, &fwd); fctx = xcalloc(1, sizeof(*fctx)); fctx->cid = c->self; fctx->rid = rid; fctx->fid = options.num_remote_forwards - 1; client_register_global_confirm(mux_confirm_remote_forward, fctx); freefwd = 0; c->mux_pause = 1; /* wait for mux_confirm_remote_forward */ /* delayed reply in mux_confirm_remote_forward */ goto out; } reply_ok(reply, rid); out: free(fwd_desc); if (freefwd) { free(fwd.listen_host); free(fwd.listen_path); free(fwd.connect_host); free(fwd.connect_path); } return ret; } static int mux_master_process_close_fwd(struct ssh *ssh, u_int rid, Channel *c, struct sshbuf *m, struct sshbuf *reply) { struct Forward fwd, *found_fwd; char *fwd_desc = NULL; const char *error_reason = NULL; char *listen_addr = NULL, *connect_addr = NULL; u_int ftype; int r, i, ret = 0; u_int lport, cport; memset(&fwd, 0, sizeof(fwd)); if ((r = sshbuf_get_u32(m, &ftype)) != 0 || (r = sshbuf_get_cstring(m, &listen_addr, NULL)) != 0 || (r = sshbuf_get_u32(m, &lport)) != 0 || (r = sshbuf_get_cstring(m, &connect_addr, NULL)) != 0 || (r = sshbuf_get_u32(m, &cport)) != 0 || (lport != (u_int)PORT_STREAMLOCAL && lport > 65535) || (cport != (u_int)PORT_STREAMLOCAL && cport > 65535)) { error_f("malformed message"); ret = -1; goto out; } if (*listen_addr == '\0') { free(listen_addr); listen_addr = NULL; } if (*connect_addr == '\0') { free(connect_addr); connect_addr = NULL; } memset(&fwd, 0, sizeof(fwd)); fwd.listen_port = lport; if (fwd.listen_port == PORT_STREAMLOCAL) fwd.listen_path = listen_addr; else fwd.listen_host = listen_addr; fwd.connect_port = cport; if (fwd.connect_port == PORT_STREAMLOCAL) fwd.connect_path = connect_addr; else fwd.connect_host = connect_addr; debug2_f("channel %d: request cancel %s", c->self, (fwd_desc = format_forward(ftype, &fwd))); /* make sure this has been requested */ found_fwd = NULL; switch (ftype) { case MUX_FWD_LOCAL: case MUX_FWD_DYNAMIC: for (i = 0; i < options.num_local_forwards; i++) { if (compare_forward(&fwd, options.local_forwards + i)) { found_fwd = options.local_forwards + i; break; } } break; case MUX_FWD_REMOTE: for (i = 0; i < options.num_remote_forwards; i++) { if (compare_forward(&fwd, options.remote_forwards + i)) { found_fwd = options.remote_forwards + i; break; } } break; } if (found_fwd == NULL) error_reason = "port not forwarded"; else if (ftype == MUX_FWD_REMOTE) { /* * This shouldn't fail unless we confused the host/port * between options.remote_forwards and permitted_opens. * However, for dynamic allocated listen ports we need * to use the actual listen port. */ if (channel_request_rforward_cancel(ssh, found_fwd) == -1) error_reason = "port not in permitted opens"; } else { /* local and dynamic forwards */ /* Ditto */ if (channel_cancel_lport_listener(ssh, &fwd, fwd.connect_port, &options.fwd_opts) == -1) error_reason = "port not found"; } if (error_reason != NULL) reply_error(reply, MUX_S_FAILURE, rid, error_reason); else { reply_ok(reply, rid); free(found_fwd->listen_host); free(found_fwd->listen_path); free(found_fwd->connect_host); free(found_fwd->connect_path); found_fwd->listen_host = found_fwd->connect_host = NULL; found_fwd->listen_path = found_fwd->connect_path = NULL; found_fwd->listen_port = found_fwd->connect_port = 0; } out: free(fwd_desc); free(listen_addr); free(connect_addr); return ret; } static int mux_master_process_stdio_fwd(struct ssh *ssh, u_int rid, Channel *c, struct sshbuf *m, struct sshbuf *reply) { Channel *nc; char *chost = NULL; u_int _cport, i, j; int ok = 0, cport, r, new_fd[2]; struct mux_stdio_confirm_ctx *cctx; if ((r = sshbuf_skip_string(m)) != 0 || /* reserved */ (r = sshbuf_get_cstring(m, &chost, NULL)) != 0 || (r = sshbuf_get_u32(m, &_cport)) != 0) { free(chost); error_f("malformed message"); return -1; } if (_cport == (u_int)PORT_STREAMLOCAL) cport = PORT_STREAMLOCAL; else if (_cport <= INT_MAX) cport = (int)_cport; else { free(chost); error_f("invalid port 0x%x", _cport); return -1; } debug2_f("channel %d: stdio fwd to %s:%d", c->self, chost, cport); /* Gather fds from client */ for(i = 0; i < 2; i++) { if ((new_fd[i] = mm_receive_fd(c->sock)) == -1) { error_f("failed to receive fd %d from client", i); for (j = 0; j < i; j++) close(new_fd[j]); free(chost); /* prepare reply */ reply_error(reply, MUX_S_FAILURE, rid, "did not receive file descriptors"); return -1; } } debug3_f("got fds stdin %d, stdout %d", new_fd[0], new_fd[1]); /* XXX support multiple child sessions in future */ if (c->have_remote_id) { debug2_f("session already open"); reply_error(reply, MUX_S_FAILURE, rid, "Multiple sessions not supported"); cleanup: close(new_fd[0]); close(new_fd[1]); free(chost); return 0; } if (options.control_master == SSHCTL_MASTER_ASK || options.control_master == SSHCTL_MASTER_AUTO_ASK) { if (cport == PORT_STREAMLOCAL) { ok = ask_permission("Allow forward to path %s", chost); } else { ok = ask_permission("Allow forward to [%s]:%d? ", chost, cport); } if (!ok) { debug2_f("stdio fwd refused by user"); reply_error(reply, MUX_S_PERMISSION_DENIED, rid, "Permission denied"); goto cleanup; } } nc = channel_connect_stdio_fwd(ssh, chost, cport, new_fd[0], new_fd[1], CHANNEL_NONBLOCK_STDIO); free(chost); nc->ctl_chan = c->self; /* link session -> control channel */ c->remote_id = nc->self; /* link control -> session channel */ c->have_remote_id = 1; debug2_f("channel_new: %d control %d", nc->self, nc->ctl_chan); channel_register_cleanup(ssh, nc->self, mux_master_session_cleanup_cb, 1); cctx = xcalloc(1, sizeof(*cctx)); cctx->rid = rid; channel_register_open_confirm(ssh, nc->self, mux_stdio_confirm, cctx); c->mux_pause = 1; /* stop handling messages until open_confirm done */ /* reply is deferred, sent by mux_session_confirm */ return 0; } /* Callback on open confirmation in mux master for a mux stdio fwd session. */ static void mux_stdio_confirm(struct ssh *ssh, int id, int success, void *arg) { struct mux_stdio_confirm_ctx *cctx = arg; Channel *c, *cc; struct sshbuf *reply; int r; if (cctx == NULL) fatal_f("cctx == NULL"); if ((c = channel_by_id(ssh, id)) == NULL) fatal_f("no channel for id %d", id); if ((cc = channel_by_id(ssh, c->ctl_chan)) == NULL) fatal_f("channel %d lacks control channel %d", id, c->ctl_chan); if ((reply = sshbuf_new()) == NULL) fatal_f("sshbuf_new"); if (!success) { debug3_f("sending failure reply"); reply_error(reply, MUX_S_FAILURE, cctx->rid, "Session open refused by peer"); /* prepare reply */ goto done; } debug3_f("sending success reply"); /* prepare reply */ if ((r = sshbuf_put_u32(reply, MUX_S_SESSION_OPENED)) != 0 || (r = sshbuf_put_u32(reply, cctx->rid)) != 0 || (r = sshbuf_put_u32(reply, c->self)) != 0) fatal_fr(r, "reply"); done: /* Send reply */ if ((r = sshbuf_put_stringb(cc->output, reply)) != 0) fatal_fr(r, "enqueue"); sshbuf_free(reply); if (cc->mux_pause <= 0) fatal_f("mux_pause %d", cc->mux_pause); cc->mux_pause = 0; /* start processing messages again */ c->open_confirm_ctx = NULL; free(cctx); } static int mux_master_process_stop_listening(struct ssh *ssh, u_int rid, Channel *c, struct sshbuf *m, struct sshbuf *reply) { debug_f("channel %d: stop listening", c->self); if (options.control_master == SSHCTL_MASTER_ASK || options.control_master == SSHCTL_MASTER_AUTO_ASK) { if (!ask_permission("Disable further multiplexing on shared " "connection to %s? ", host)) { debug2_f("stop listen refused by user"); reply_error(reply, MUX_S_PERMISSION_DENIED, rid, "Permission denied"); return 0; } } if (mux_listener_channel != NULL) { channel_free(ssh, mux_listener_channel); client_stop_mux(); free(options.control_path); options.control_path = NULL; mux_listener_channel = NULL; muxserver_sock = -1; } reply_ok(reply, rid); return 0; } static int mux_master_process_proxy(struct ssh *ssh, u_int rid, Channel *c, struct sshbuf *m, struct sshbuf *reply) { int r; debug_f("channel %d: proxy request", c->self); c->mux_rcb = channel_proxy_downstream; if ((r = sshbuf_put_u32(reply, MUX_S_PROXY)) != 0 || (r = sshbuf_put_u32(reply, rid)) != 0) fatal_fr(r, "reply"); return 0; } /* Channel callbacks fired on read/write from mux client fd */ static int mux_master_read_cb(struct ssh *ssh, Channel *c) { struct mux_master_state *state = (struct mux_master_state *)c->mux_ctx; struct sshbuf *in = NULL, *out = NULL; u_int type, rid, i; int r, ret = -1; if ((out = sshbuf_new()) == NULL) fatal_f("sshbuf_new"); /* Setup ctx and */ if (c->mux_ctx == NULL) { state = xcalloc(1, sizeof(*state)); c->mux_ctx = state; channel_register_cleanup(ssh, c->self, mux_master_control_cleanup_cb, 0); /* Send hello */ if ((r = sshbuf_put_u32(out, MUX_MSG_HELLO)) != 0 || (r = sshbuf_put_u32(out, SSHMUX_VER)) != 0) fatal_fr(r, "reply"); /* no extensions */ if ((r = sshbuf_put_stringb(c->output, out)) != 0) fatal_fr(r, "enqueue"); debug3_f("channel %d: hello sent", c->self); ret = 0; goto out; } /* Channel code ensures that we receive whole packets */ if ((r = sshbuf_froms(c->input, &in)) != 0) { malf: error_f("malformed message"); goto out; } if ((r = sshbuf_get_u32(in, &type)) != 0) goto malf; debug3_f("channel %d packet type 0x%08x len %zu", c->self, type, sshbuf_len(in)); if (type == MUX_MSG_HELLO) rid = 0; else { if (!state->hello_rcvd) { error_f("expected MUX_MSG_HELLO(0x%08x), " "received 0x%08x", MUX_MSG_HELLO, type); goto out; } if ((r = sshbuf_get_u32(in, &rid)) != 0) goto malf; } for (i = 0; mux_master_handlers[i].handler != NULL; i++) { if (type == mux_master_handlers[i].type) { ret = mux_master_handlers[i].handler(ssh, rid, c, in, out); break; } } if (mux_master_handlers[i].handler == NULL) { error_f("unsupported mux message 0x%08x", type); reply_error(out, MUX_S_FAILURE, rid, "unsupported request"); ret = 0; } /* Enqueue reply packet */ if (sshbuf_len(out) != 0 && (r = sshbuf_put_stringb(c->output, out)) != 0) fatal_fr(r, "enqueue"); out: sshbuf_free(in); sshbuf_free(out); return ret; } void mux_exit_message(struct ssh *ssh, Channel *c, int exitval) { struct sshbuf *m; Channel *mux_chan; int r; debug3_f("channel %d: exit message, exitval %d", c->self, exitval); if ((mux_chan = channel_by_id(ssh, c->ctl_chan)) == NULL) fatal_f("channel %d missing mux %d", c->self, c->ctl_chan); /* Append exit message packet to control socket output queue */ if ((m = sshbuf_new()) == NULL) fatal_f("sshbuf_new"); if ((r = sshbuf_put_u32(m, MUX_S_EXIT_MESSAGE)) != 0 || (r = sshbuf_put_u32(m, c->self)) != 0 || (r = sshbuf_put_u32(m, exitval)) != 0 || (r = sshbuf_put_stringb(mux_chan->output, m)) != 0) fatal_fr(r, "reply"); sshbuf_free(m); } void mux_tty_alloc_failed(struct ssh *ssh, Channel *c) { struct sshbuf *m; Channel *mux_chan; int r; debug3_f("channel %d: TTY alloc failed", c->self); if ((mux_chan = channel_by_id(ssh, c->ctl_chan)) == NULL) fatal_f("channel %d missing mux %d", c->self, c->ctl_chan); /* Append exit message packet to control socket output queue */ if ((m = sshbuf_new()) == NULL) fatal_f("sshbuf_new"); if ((r = sshbuf_put_u32(m, MUX_S_TTY_ALLOC_FAIL)) != 0 || (r = sshbuf_put_u32(m, c->self)) != 0 || (r = sshbuf_put_stringb(mux_chan->output, m)) != 0) fatal_fr(r, "reply"); sshbuf_free(m); } /* Prepare a mux master to listen on a Unix domain socket. */ void muxserver_listen(struct ssh *ssh) { mode_t old_umask; char *orig_control_path = options.control_path; char rbuf[16+1]; u_int i, r; int oerrno; if (options.control_path == NULL || options.control_master == SSHCTL_MASTER_NO) return; debug("setting up multiplex master socket"); /* * Use a temporary path before listen so we can pseudo-atomically * establish the listening socket in its final location to avoid * other processes racing in between bind() and listen() and hitting * an unready socket. */ for (i = 0; i < sizeof(rbuf) - 1; i++) { r = arc4random_uniform(26+26+10); rbuf[i] = (r < 26) ? 'a' + r : (r < 26*2) ? 'A' + r - 26 : '0' + r - 26 - 26; } rbuf[sizeof(rbuf) - 1] = '\0'; options.control_path = NULL; xasprintf(&options.control_path, "%s.%s", orig_control_path, rbuf); debug3_f("temporary control path %s", options.control_path); old_umask = umask(0177); muxserver_sock = unix_listener(options.control_path, 64, 0); oerrno = errno; umask(old_umask); if (muxserver_sock < 0) { if (oerrno == EINVAL || oerrno == EADDRINUSE) { error("ControlSocket %s already exists, " "disabling multiplexing", options.control_path); disable_mux_master: if (muxserver_sock != -1) { close(muxserver_sock); muxserver_sock = -1; } free(orig_control_path); free(options.control_path); options.control_path = NULL; options.control_master = SSHCTL_MASTER_NO; return; } else { /* unix_listener() logs the error */ cleanup_exit(255); } } /* Now atomically "move" the mux socket into position */ if (link(options.control_path, orig_control_path) != 0) { if (errno != EEXIST) { fatal_f("link mux listener %s => %s: %s", options.control_path, orig_control_path, strerror(errno)); } error("ControlSocket %s already exists, disabling multiplexing", orig_control_path); unlink(options.control_path); goto disable_mux_master; } unlink(options.control_path); free(options.control_path); options.control_path = orig_control_path; set_nonblock(muxserver_sock); mux_listener_channel = channel_new(ssh, "mux listener", SSH_CHANNEL_MUX_LISTENER, muxserver_sock, muxserver_sock, -1, CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, options.control_path, 1); mux_listener_channel->mux_rcb = mux_master_read_cb; debug3_f("mux listener channel %d fd %d", mux_listener_channel->self, mux_listener_channel->sock); } /* Callback on open confirmation in mux master for a mux client session. */ static void mux_session_confirm(struct ssh *ssh, int id, int success, void *arg) { struct mux_session_confirm_ctx *cctx = arg; const char *display; Channel *c, *cc; int i, r; struct sshbuf *reply; if (cctx == NULL) fatal_f("cctx == NULL"); if ((c = channel_by_id(ssh, id)) == NULL) fatal_f("no channel for id %d", id); if ((cc = channel_by_id(ssh, c->ctl_chan)) == NULL) fatal_f("channel %d lacks control channel %d", id, c->ctl_chan); if ((reply = sshbuf_new()) == NULL) fatal_f("sshbuf_new"); if (!success) { debug3_f("sending failure reply"); reply_error(reply, MUX_S_FAILURE, cctx->rid, "Session open refused by peer"); goto done; } display = getenv("DISPLAY"); if (cctx->want_x_fwd && options.forward_x11 && display != NULL) { char *proto, *data; /* Get reasonable local authentication information. */ if (client_x11_get_proto(ssh, display, options.xauth_location, options.forward_x11_trusted, options.forward_x11_timeout, &proto, &data) == 0) { /* Request forwarding with authentication spoofing. */ debug("Requesting X11 forwarding with authentication " "spoofing."); x11_request_forwarding_with_spoofing(ssh, id, display, proto, data, 1); /* XXX exit_on_forward_failure */ client_expect_confirm(ssh, id, "X11 forwarding", CONFIRM_WARN); } } if (cctx->want_agent_fwd && options.forward_agent) { debug("Requesting authentication agent forwarding."); channel_request_start(ssh, id, "auth-agent-req@openssh.com", 0); if ((r = sshpkt_send(ssh)) != 0) fatal_fr(r, "send"); } client_session2_setup(ssh, id, cctx->want_tty, cctx->want_subsys, cctx->term, &cctx->tio, c->rfd, cctx->cmd, cctx->env); debug3_f("sending success reply"); /* prepare reply */ if ((r = sshbuf_put_u32(reply, MUX_S_SESSION_OPENED)) != 0 || (r = sshbuf_put_u32(reply, cctx->rid)) != 0 || (r = sshbuf_put_u32(reply, c->self)) != 0) fatal_fr(r, "reply"); done: /* Send reply */ if ((r = sshbuf_put_stringb(cc->output, reply)) != 0) fatal_fr(r, "enqueue"); sshbuf_free(reply); if (cc->mux_pause <= 0) fatal_f("mux_pause %d", cc->mux_pause); cc->mux_pause = 0; /* start processing messages again */ c->open_confirm_ctx = NULL; sshbuf_free(cctx->cmd); free(cctx->term); if (cctx->env != NULL) { for (i = 0; cctx->env[i] != NULL; i++) free(cctx->env[i]); free(cctx->env); } free(cctx); } /* ** Multiplexing client support */ /* Exit signal handler */ static void control_client_sighandler(int signo) { muxclient_terminate = signo; } /* * Relay signal handler - used to pass some signals from mux client to * mux master. */ static void control_client_sigrelay(int signo) { int save_errno = errno; if (muxserver_pid > 1) kill(muxserver_pid, signo); errno = save_errno; } static int mux_client_read(int fd, struct sshbuf *b, size_t need, int timeout_ms) { size_t have; ssize_t len; u_char *p; int r; if ((r = sshbuf_reserve(b, need, &p)) != 0) fatal_fr(r, "reserve"); for (have = 0; have < need; ) { if (muxclient_terminate) { errno = EINTR; return -1; } len = read(fd, p + have, need - have); if (len == -1) { switch (errno) { #if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN) case EWOULDBLOCK: #endif case EAGAIN: - if (waitrfd(fd, &timeout_ms) == -1) + if (waitrfd(fd, &timeout_ms, + &muxclient_terminate) == -1 && + errno != EINTR) return -1; /* timeout */ /* FALLTHROUGH */ case EINTR: continue; default: return -1; } } if (len == 0) { errno = EPIPE; return -1; } have += (size_t)len; } return 0; } static int mux_client_write_packet(int fd, struct sshbuf *m) { struct sshbuf *queue; u_int have, need; int r, oerrno, len; const u_char *ptr; struct pollfd pfd; pfd.fd = fd; pfd.events = POLLOUT; if ((queue = sshbuf_new()) == NULL) fatal_f("sshbuf_new"); if ((r = sshbuf_put_stringb(queue, m)) != 0) fatal_fr(r, "enqueue"); need = sshbuf_len(queue); ptr = sshbuf_ptr(queue); for (have = 0; have < need; ) { if (muxclient_terminate) { sshbuf_free(queue); errno = EINTR; return -1; } len = write(fd, ptr + have, need - have); if (len == -1) { switch (errno) { #if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN) case EWOULDBLOCK: #endif case EAGAIN: (void)poll(&pfd, 1, -1); /* FALLTHROUGH */ case EINTR: continue; default: oerrno = errno; sshbuf_free(queue); errno = oerrno; return -1; } } if (len == 0) { sshbuf_free(queue); errno = EPIPE; return -1; } have += (u_int)len; } sshbuf_free(queue); return 0; } static int mux_client_read_packet_timeout(int fd, struct sshbuf *m, int timeout_ms) { struct sshbuf *queue; size_t need, have; const u_char *ptr; int r, oerrno; if ((queue = sshbuf_new()) == NULL) fatal_f("sshbuf_new"); if (mux_client_read(fd, queue, 4, timeout_ms) != 0) { if ((oerrno = errno) == EPIPE) debug3_f("read header failed: %s", strerror(errno)); sshbuf_free(queue); errno = oerrno; return -1; } need = PEEK_U32(sshbuf_ptr(queue)); if (mux_client_read(fd, queue, need, timeout_ms) != 0) { oerrno = errno; debug3_f("read body failed: %s", strerror(errno)); sshbuf_free(queue); errno = oerrno; return -1; } if ((r = sshbuf_get_string_direct(queue, &ptr, &have)) != 0 || (r = sshbuf_put(m, ptr, have)) != 0) fatal_fr(r, "dequeue"); sshbuf_free(queue); return 0; } static int mux_client_read_packet(int fd, struct sshbuf *m) { return mux_client_read_packet_timeout(fd, m, -1); } static int mux_client_hello_exchange(int fd, int timeout_ms) { struct sshbuf *m; u_int type, ver; int r, ret = -1; if ((m = sshbuf_new()) == NULL) fatal_f("sshbuf_new"); if ((r = sshbuf_put_u32(m, MUX_MSG_HELLO)) != 0 || (r = sshbuf_put_u32(m, SSHMUX_VER)) != 0) fatal_fr(r, "assemble hello"); /* no extensions */ if (mux_client_write_packet(fd, m) != 0) { debug_f("write packet: %s", strerror(errno)); goto out; } sshbuf_reset(m); /* Read their HELLO */ if (mux_client_read_packet_timeout(fd, m, timeout_ms) != 0) { debug_f("read packet failed"); goto out; } if ((r = sshbuf_get_u32(m, &type)) != 0) fatal_fr(r, "parse type"); if (type != MUX_MSG_HELLO) { error_f("expected HELLO (%u) got %u", MUX_MSG_HELLO, type); goto out; } if ((r = sshbuf_get_u32(m, &ver)) != 0) fatal_fr(r, "parse version"); if (ver != SSHMUX_VER) { error("Unsupported multiplexing protocol version %d " "(expected %d)", ver, SSHMUX_VER); goto out; } debug2_f("master version %u", ver); /* No extensions are presently defined */ while (sshbuf_len(m) > 0) { char *name = NULL; if ((r = sshbuf_get_cstring(m, &name, NULL)) != 0 || (r = sshbuf_skip_string(m)) != 0) { /* value */ error_fr(r, "parse extension"); goto out; } debug2("Unrecognised master extension \"%s\"", name); free(name); } /* success */ ret = 0; out: sshbuf_free(m); return ret; } static u_int mux_client_request_alive(int fd) { struct sshbuf *m; char *e; u_int pid, type, rid; int r; debug3_f("entering"); if ((m = sshbuf_new()) == NULL) fatal_f("sshbuf_new"); if ((r = sshbuf_put_u32(m, MUX_C_ALIVE_CHECK)) != 0 || (r = sshbuf_put_u32(m, muxclient_request_id)) != 0) fatal_fr(r, "assemble"); if (mux_client_write_packet(fd, m) != 0) fatal_f("write packet: %s", strerror(errno)); sshbuf_reset(m); /* Read their reply */ if (mux_client_read_packet(fd, m) != 0) { sshbuf_free(m); return 0; } if ((r = sshbuf_get_u32(m, &type)) != 0) fatal_fr(r, "parse type"); if (type != MUX_S_ALIVE) { if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) fatal_fr(r, "parse error message"); fatal_f("master returned error: %s", e); } if ((r = sshbuf_get_u32(m, &rid)) != 0) fatal_fr(r, "parse remote ID"); if (rid != muxclient_request_id) fatal_f("out of sequence reply: my id %u theirs %u", muxclient_request_id, rid); if ((r = sshbuf_get_u32(m, &pid)) != 0) fatal_fr(r, "parse PID"); sshbuf_free(m); debug3_f("done pid = %u", pid); muxclient_request_id++; return pid; } static void mux_client_request_terminate(int fd) { struct sshbuf *m; char *e; u_int type, rid; int r; debug3_f("entering"); if ((m = sshbuf_new()) == NULL) fatal_f("sshbuf_new"); if ((r = sshbuf_put_u32(m, MUX_C_TERMINATE)) != 0 || (r = sshbuf_put_u32(m, muxclient_request_id)) != 0) fatal_fr(r, "request"); if (mux_client_write_packet(fd, m) != 0) fatal_f("write packet: %s", strerror(errno)); sshbuf_reset(m); /* Read their reply */ if (mux_client_read_packet(fd, m) != 0) { /* Remote end exited already */ if (errno == EPIPE) { sshbuf_free(m); return; } fatal_f("read from master failed: %s", strerror(errno)); } if ((r = sshbuf_get_u32(m, &type)) != 0 || (r = sshbuf_get_u32(m, &rid)) != 0) fatal_fr(r, "parse"); if (rid != muxclient_request_id) fatal_f("out of sequence reply: my id %u theirs %u", muxclient_request_id, rid); switch (type) { case MUX_S_OK: break; case MUX_S_PERMISSION_DENIED: if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) fatal_fr(r, "parse error message"); fatal("Master refused termination request: %s", e); case MUX_S_FAILURE: if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) fatal_fr(r, "parse error message"); fatal_f("termination request failed: %s", e); default: fatal_f("unexpected response from master 0x%08x", type); } sshbuf_free(m); muxclient_request_id++; } static int mux_client_forward(int fd, int cancel_flag, u_int ftype, struct Forward *fwd) { struct sshbuf *m; char *e, *fwd_desc; const char *lhost, *chost; u_int type, rid; int r; fwd_desc = format_forward(ftype, fwd); debug("Requesting %s %s", cancel_flag ? "cancellation of" : "forwarding of", fwd_desc); free(fwd_desc); type = cancel_flag ? MUX_C_CLOSE_FWD : MUX_C_OPEN_FWD; if (fwd->listen_path != NULL) lhost = fwd->listen_path; else if (fwd->listen_host == NULL) lhost = ""; else if (*fwd->listen_host == '\0') lhost = "*"; else lhost = fwd->listen_host; if (fwd->connect_path != NULL) chost = fwd->connect_path; else if (fwd->connect_host == NULL) chost = ""; else chost = fwd->connect_host; if ((m = sshbuf_new()) == NULL) fatal_f("sshbuf_new"); if ((r = sshbuf_put_u32(m, type)) != 0 || (r = sshbuf_put_u32(m, muxclient_request_id)) != 0 || (r = sshbuf_put_u32(m, ftype)) != 0 || (r = sshbuf_put_cstring(m, lhost)) != 0 || (r = sshbuf_put_u32(m, fwd->listen_port)) != 0 || (r = sshbuf_put_cstring(m, chost)) != 0 || (r = sshbuf_put_u32(m, fwd->connect_port)) != 0) fatal_fr(r, "request"); if (mux_client_write_packet(fd, m) != 0) fatal_f("write packet: %s", strerror(errno)); sshbuf_reset(m); /* Read their reply */ if (mux_client_read_packet(fd, m) != 0) { sshbuf_free(m); return -1; } if ((r = sshbuf_get_u32(m, &type)) != 0 || (r = sshbuf_get_u32(m, &rid)) != 0) fatal_fr(r, "parse"); if (rid != muxclient_request_id) fatal_f("out of sequence reply: my id %u theirs %u", muxclient_request_id, rid); switch (type) { case MUX_S_OK: break; case MUX_S_REMOTE_PORT: if (cancel_flag) fatal_f("got MUX_S_REMOTE_PORT for cancel"); if ((r = sshbuf_get_u32(m, &fwd->allocated_port)) != 0) fatal_fr(r, "parse port"); verbose("Allocated port %u for remote forward to %s:%d", fwd->allocated_port, fwd->connect_host ? fwd->connect_host : "", fwd->connect_port); if (muxclient_command == SSHMUX_COMMAND_FORWARD) fprintf(stdout, "%i\n", fwd->allocated_port); break; case MUX_S_PERMISSION_DENIED: if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) fatal_fr(r, "parse error message"); sshbuf_free(m); error("Master refused forwarding request: %s", e); return -1; case MUX_S_FAILURE: if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) fatal_fr(r, "parse error message"); sshbuf_free(m); error_f("forwarding request failed: %s", e); return -1; default: fatal_f("unexpected response from master 0x%08x", type); } sshbuf_free(m); muxclient_request_id++; return 0; } static int mux_client_forwards(int fd, int cancel_flag) { int i, ret = 0; debug3_f("%s forwardings: %d local, %d remote", cancel_flag ? "cancel" : "request", options.num_local_forwards, options.num_remote_forwards); /* XXX ExitOnForwardingFailure */ for (i = 0; i < options.num_local_forwards; i++) { if (mux_client_forward(fd, cancel_flag, options.local_forwards[i].connect_port == 0 ? MUX_FWD_DYNAMIC : MUX_FWD_LOCAL, options.local_forwards + i) != 0) ret = -1; } for (i = 0; i < options.num_remote_forwards; i++) { if (mux_client_forward(fd, cancel_flag, MUX_FWD_REMOTE, options.remote_forwards + i) != 0) ret = -1; } return ret; } static int mux_client_request_session(int fd) { struct sshbuf *m; char *e; const char *term = NULL; u_int i, echar, rid, sid, esid, exitval, type, exitval_seen; extern char **environ; int r, rawmode = 0; debug3_f("entering"); if ((muxserver_pid = mux_client_request_alive(fd)) == 0) { error_f("master alive request failed"); return -1; } ssh_signal(SIGPIPE, SIG_IGN); if (options.stdin_null && stdfd_devnull(1, 0, 0) == -1) fatal_f("stdfd_devnull failed"); if ((term = lookup_env_in_list("TERM", options.setenv, options.num_setenv)) == NULL || *term == '\0') term = getenv("TERM"); echar = 0xffffffff; if (options.escape_char != SSH_ESCAPECHAR_NONE) echar = (u_int)options.escape_char; if ((m = sshbuf_new()) == NULL) fatal_f("sshbuf_new"); if ((r = sshbuf_put_u32(m, MUX_C_NEW_SESSION)) != 0 || (r = sshbuf_put_u32(m, muxclient_request_id)) != 0 || (r = sshbuf_put_string(m, NULL, 0)) != 0 || /* reserved */ (r = sshbuf_put_u32(m, tty_flag)) != 0 || (r = sshbuf_put_u32(m, options.forward_x11)) != 0 || (r = sshbuf_put_u32(m, options.forward_agent)) != 0 || (r = sshbuf_put_u32(m, options.session_type == SESSION_TYPE_SUBSYSTEM)) != 0 || (r = sshbuf_put_u32(m, echar)) != 0 || (r = sshbuf_put_cstring(m, term == NULL ? "" : term)) != 0 || (r = sshbuf_put_stringb(m, command)) != 0) fatal_fr(r, "request"); /* Pass environment */ if (options.num_send_env > 0 && environ != NULL) { for (i = 0; environ[i] != NULL; i++) { if (!env_permitted(environ[i])) continue; if ((r = sshbuf_put_cstring(m, environ[i])) != 0) fatal_fr(r, "request sendenv"); } } for (i = 0; i < options.num_setenv; i++) { if ((r = sshbuf_put_cstring(m, options.setenv[i])) != 0) fatal_fr(r, "request setenv"); } if (mux_client_write_packet(fd, m) != 0) fatal_f("write packet: %s", strerror(errno)); /* Send the stdio file descriptors */ if (mm_send_fd(fd, STDIN_FILENO) == -1 || mm_send_fd(fd, STDOUT_FILENO) == -1 || mm_send_fd(fd, STDERR_FILENO) == -1) fatal_f("send fds failed"); debug3_f("session request sent"); /* Read their reply */ sshbuf_reset(m); if (mux_client_read_packet(fd, m) != 0) { error_f("read from master failed: %s", strerror(errno)); sshbuf_free(m); return -1; } if ((r = sshbuf_get_u32(m, &type)) != 0 || (r = sshbuf_get_u32(m, &rid)) != 0) fatal_fr(r, "parse"); if (rid != muxclient_request_id) fatal_f("out of sequence reply: my id %u theirs %u", muxclient_request_id, rid); switch (type) { case MUX_S_SESSION_OPENED: if ((r = sshbuf_get_u32(m, &sid)) != 0) fatal_fr(r, "parse session ID"); debug_f("master session id: %u", sid); break; case MUX_S_PERMISSION_DENIED: if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) fatal_fr(r, "parse error message"); error("Master refused session request: %s", e); sshbuf_free(m); return -1; case MUX_S_FAILURE: if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) fatal_fr(r, "parse error message"); error_f("session request failed: %s", e); sshbuf_free(m); return -1; default: sshbuf_free(m); error_f("unexpected response from master 0x%08x", type); return -1; } muxclient_request_id++; if (pledge("stdio proc tty", NULL) == -1) fatal_f("pledge(): %s", strerror(errno)); platform_pledge_mux(); ssh_signal(SIGHUP, control_client_sighandler); ssh_signal(SIGINT, control_client_sighandler); ssh_signal(SIGTERM, control_client_sighandler); ssh_signal(SIGWINCH, control_client_sigrelay); if (options.fork_after_authentication) daemon(1, 1); else { rawmode = tty_flag; if (tty_flag) { enter_raw_mode( options.request_tty == REQUEST_TTY_FORCE); } } /* * Stick around until the controlee closes the client_fd. * Before it does, it is expected to write an exit message. * This process must read the value and wait for the closure of * the client_fd; if this one closes early, the multiplex master will * terminate early too (possibly losing data). */ for (exitval = 255, exitval_seen = 0;;) { sshbuf_reset(m); if (mux_client_read_packet(fd, m) != 0) break; if ((r = sshbuf_get_u32(m, &type)) != 0) fatal_fr(r, "parse type"); switch (type) { case MUX_S_TTY_ALLOC_FAIL: if ((r = sshbuf_get_u32(m, &esid)) != 0) fatal_fr(r, "parse session ID"); if (esid != sid) fatal_f("tty alloc fail on unknown session: " "my id %u theirs %u", sid, esid); leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); rawmode = 0; continue; case MUX_S_EXIT_MESSAGE: if ((r = sshbuf_get_u32(m, &esid)) != 0) fatal_fr(r, "parse session ID"); if (esid != sid) fatal_f("exit on unknown session: " "my id %u theirs %u", sid, esid); if (exitval_seen) fatal_f("exitval sent twice"); if ((r = sshbuf_get_u32(m, &exitval)) != 0) fatal_fr(r, "parse exitval"); exitval_seen = 1; continue; default: if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) fatal_fr(r, "parse error message"); fatal_f("master returned error: %s", e); } } close(fd); if (rawmode) leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); if (muxclient_terminate) { debug2("Exiting on signal: %s", strsignal(muxclient_terminate)); exitval = 255; } else if (!exitval_seen) { debug2("Control master terminated unexpectedly"); exitval = 255; } else debug2("Received exit status from master %d", exitval); if (tty_flag && options.log_level >= SYSLOG_LEVEL_INFO) fprintf(stderr, "Shared connection to %s closed.\r\n", host); exit(exitval); } static int mux_client_proxy(int fd) { struct sshbuf *m; char *e; u_int type, rid; int r; if ((m = sshbuf_new()) == NULL) fatal_f("sshbuf_new"); if ((r = sshbuf_put_u32(m, MUX_C_PROXY)) != 0 || (r = sshbuf_put_u32(m, muxclient_request_id)) != 0) fatal_fr(r, "request"); if (mux_client_write_packet(fd, m) != 0) fatal_f("write packet: %s", strerror(errno)); sshbuf_reset(m); /* Read their reply */ if (mux_client_read_packet(fd, m) != 0) { sshbuf_free(m); return 0; } if ((r = sshbuf_get_u32(m, &type)) != 0 || (r = sshbuf_get_u32(m, &rid)) != 0) fatal_fr(r, "parse"); if (rid != muxclient_request_id) fatal_f("out of sequence reply: my id %u theirs %u", muxclient_request_id, rid); if (type != MUX_S_PROXY) { if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) fatal_fr(r, "parse error message"); fatal_f("master returned error: %s", e); } sshbuf_free(m); debug3_f("done"); muxclient_request_id++; return 0; } static int mux_client_request_stdio_fwd(int fd) { struct sshbuf *m; char *e; u_int type, rid, sid; int r; debug3_f("entering"); if ((muxserver_pid = mux_client_request_alive(fd)) == 0) { error_f("master alive request failed"); return -1; } ssh_signal(SIGPIPE, SIG_IGN); if (options.stdin_null && stdfd_devnull(1, 0, 0) == -1) fatal_f("stdfd_devnull failed"); if ((m = sshbuf_new()) == NULL) fatal_f("sshbuf_new"); if ((r = sshbuf_put_u32(m, MUX_C_NEW_STDIO_FWD)) != 0 || (r = sshbuf_put_u32(m, muxclient_request_id)) != 0 || (r = sshbuf_put_string(m, NULL, 0)) != 0 || /* reserved */ (r = sshbuf_put_cstring(m, options.stdio_forward_host)) != 0 || (r = sshbuf_put_u32(m, options.stdio_forward_port)) != 0) fatal_fr(r, "request"); if (mux_client_write_packet(fd, m) != 0) fatal_f("write packet: %s", strerror(errno)); /* Send the stdio file descriptors */ if (mm_send_fd(fd, STDIN_FILENO) == -1 || mm_send_fd(fd, STDOUT_FILENO) == -1) fatal_f("send fds failed"); if (pledge("stdio proc tty", NULL) == -1) fatal_f("pledge(): %s", strerror(errno)); platform_pledge_mux(); debug3_f("stdio forward request sent"); /* Read their reply */ sshbuf_reset(m); if (mux_client_read_packet(fd, m) != 0) { error_f("read from master failed: %s", strerror(errno)); sshbuf_free(m); return -1; } if ((r = sshbuf_get_u32(m, &type)) != 0 || (r = sshbuf_get_u32(m, &rid)) != 0) fatal_fr(r, "parse"); if (rid != muxclient_request_id) fatal_f("out of sequence reply: my id %u theirs %u", muxclient_request_id, rid); switch (type) { case MUX_S_SESSION_OPENED: if ((r = sshbuf_get_u32(m, &sid)) != 0) fatal_fr(r, "parse session ID"); debug_f("master session id: %u", sid); break; case MUX_S_PERMISSION_DENIED: if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) fatal_fr(r, "parse error message"); sshbuf_free(m); fatal("Master refused stdio forwarding request: %s", e); case MUX_S_FAILURE: if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) fatal_fr(r, "parse error message"); sshbuf_free(m); fatal("Stdio forwarding request failed: %s", e); default: sshbuf_free(m); error_f("unexpected response from master 0x%08x", type); return -1; } muxclient_request_id++; ssh_signal(SIGHUP, control_client_sighandler); ssh_signal(SIGINT, control_client_sighandler); ssh_signal(SIGTERM, control_client_sighandler); ssh_signal(SIGWINCH, control_client_sigrelay); /* * Stick around until the controlee closes the client_fd. */ sshbuf_reset(m); if (mux_client_read_packet(fd, m) != 0) { if (errno == EPIPE || (errno == EINTR && muxclient_terminate != 0)) return 0; fatal_f("mux_client_read_packet: %s", strerror(errno)); } fatal_f("master returned unexpected message %u", type); } static void mux_client_request_stop_listening(int fd) { struct sshbuf *m; char *e; u_int type, rid; int r; debug3_f("entering"); if ((m = sshbuf_new()) == NULL) fatal_f("sshbuf_new"); if ((r = sshbuf_put_u32(m, MUX_C_STOP_LISTENING)) != 0 || (r = sshbuf_put_u32(m, muxclient_request_id)) != 0) fatal_fr(r, "request"); if (mux_client_write_packet(fd, m) != 0) fatal_f("write packet: %s", strerror(errno)); sshbuf_reset(m); /* Read their reply */ if (mux_client_read_packet(fd, m) != 0) fatal_f("read from master failed: %s", strerror(errno)); if ((r = sshbuf_get_u32(m, &type)) != 0 || (r = sshbuf_get_u32(m, &rid)) != 0) fatal_fr(r, "parse"); if (rid != muxclient_request_id) fatal_f("out of sequence reply: my id %u theirs %u", muxclient_request_id, rid); switch (type) { case MUX_S_OK: break; case MUX_S_PERMISSION_DENIED: if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) fatal_fr(r, "parse error message"); fatal("Master refused stop listening request: %s", e); case MUX_S_FAILURE: if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) fatal_fr(r, "parse error message"); fatal_f("stop listening request failed: %s", e); default: fatal_f("unexpected response from master 0x%08x", type); } sshbuf_free(m); muxclient_request_id++; } /* Multiplex client main loop. */ int muxclient(const char *path) { struct sockaddr_un addr; int sock, timeout = options.connection_timeout, timeout_ms = -1; u_int pid; if (muxclient_command == 0) { if (options.stdio_forward_host != NULL) muxclient_command = SSHMUX_COMMAND_STDIO_FWD; else muxclient_command = SSHMUX_COMMAND_OPEN; } switch (options.control_master) { case SSHCTL_MASTER_AUTO: case SSHCTL_MASTER_AUTO_ASK: debug("auto-mux: Trying existing master"); /* FALLTHROUGH */ case SSHCTL_MASTER_NO: break; default: return -1; } memset(&addr, '\0', sizeof(addr)); addr.sun_family = AF_UNIX; if (strlcpy(addr.sun_path, path, sizeof(addr.sun_path)) >= sizeof(addr.sun_path)) fatal("ControlPath too long ('%s' >= %u bytes)", path, (unsigned int)sizeof(addr.sun_path)); if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) fatal_f("socket(): %s", strerror(errno)); if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) { switch (muxclient_command) { case SSHMUX_COMMAND_OPEN: case SSHMUX_COMMAND_STDIO_FWD: break; default: fatal("Control socket connect(%.100s): %s", path, strerror(errno)); } if (errno == ECONNREFUSED && options.control_master != SSHCTL_MASTER_NO) { debug("Stale control socket %.100s, unlinking", path); unlink(path); } else if (errno == ENOENT) { debug("Control socket \"%.100s\" does not exist", path); } else { error("Control socket connect(%.100s): %s", path, strerror(errno)); } close(sock); return -1; } set_nonblock(sock); /* Timeout on initial connection only. */ if (timeout > 0 && timeout < INT_MAX / 1000) timeout_ms = timeout * 1000; if (mux_client_hello_exchange(sock, timeout_ms) != 0) { error_f("master hello exchange failed"); close(sock); return -1; } switch (muxclient_command) { case SSHMUX_COMMAND_ALIVE_CHECK: if ((pid = mux_client_request_alive(sock)) == 0) fatal_f("master alive check failed"); fprintf(stderr, "Master running (pid=%u)\r\n", pid); exit(0); case SSHMUX_COMMAND_TERMINATE: mux_client_request_terminate(sock); if (options.log_level != SYSLOG_LEVEL_QUIET) fprintf(stderr, "Exit request sent.\r\n"); exit(0); case SSHMUX_COMMAND_FORWARD: if (mux_client_forwards(sock, 0) != 0) fatal_f("master forward request failed"); exit(0); case SSHMUX_COMMAND_OPEN: if (mux_client_forwards(sock, 0) != 0) { error_f("master forward request failed"); return -1; } mux_client_request_session(sock); return -1; case SSHMUX_COMMAND_STDIO_FWD: mux_client_request_stdio_fwd(sock); exit(0); case SSHMUX_COMMAND_STOP: mux_client_request_stop_listening(sock); if (options.log_level != SYSLOG_LEVEL_QUIET) fprintf(stderr, "Stop listening request sent.\r\n"); exit(0); case SSHMUX_COMMAND_CANCEL_FWD: if (mux_client_forwards(sock, 1) != 0) error_f("master cancel forward request failed"); exit(0); case SSHMUX_COMMAND_PROXY: mux_client_proxy(sock); return (sock); default: fatal("unrecognised muxclient_command %d", muxclient_command); } } diff --git a/openbsd-compat/bsd-closefrom.c b/openbsd-compat/bsd-closefrom.c index 704da531fef9..49a4f35ff9c3 100644 --- a/openbsd-compat/bsd-closefrom.c +++ b/openbsd-compat/bsd-closefrom.c @@ -1,159 +1,158 @@ /* * Copyright (c) 2004-2005 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "includes.h" #if !defined(HAVE_CLOSEFROM) || defined(BROKEN_CLOSEFROM) #include #include #include #ifdef HAVE_FCNTL_H # include #endif #include #include #include #include -#include #ifdef HAVE_DIRENT_H # include # define NAMLEN(dirent) strlen((dirent)->d_name) #else # define dirent direct # define NAMLEN(dirent) (dirent)->d_namlen # ifdef HAVE_SYS_NDIR_H # include # endif # ifdef HAVE_SYS_DIR_H # include # endif # ifdef HAVE_NDIR_H # include # endif #endif #if defined(HAVE_LIBPROC_H) # include #endif #ifndef OPEN_MAX # define OPEN_MAX 256 #endif #if 0 __unused static const char rcsid[] = "$Sudo: closefrom.c,v 1.11 2006/08/17 15:26:54 millert Exp $"; #endif /* lint */ #ifndef HAVE_FCNTL_CLOSEM /* * Close all file descriptors greater than or equal to lowfd. */ static void closefrom_fallback(int lowfd) { long fd, maxfd; /* * Fall back on sysconf() or getdtablesize(). We avoid checking * resource limits since it is possible to open a file descriptor * and then drop the rlimit such that it is below the open fd. */ #ifdef HAVE_SYSCONF maxfd = sysconf(_SC_OPEN_MAX); #else maxfd = getdtablesize(); #endif /* HAVE_SYSCONF */ if (maxfd < 0) maxfd = OPEN_MAX; for (fd = lowfd; fd < maxfd; fd++) (void) close((int) fd); } #endif /* HAVE_FCNTL_CLOSEM */ #ifdef HAVE_FCNTL_CLOSEM void closefrom(int lowfd) { (void) fcntl(lowfd, F_CLOSEM, 0); } #elif defined(HAVE_LIBPROC_H) && defined(HAVE_PROC_PIDINFO) void closefrom(int lowfd) { int i, r, sz; pid_t pid = getpid(); struct proc_fdinfo *fdinfo_buf = NULL; sz = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, NULL, 0); if (sz == 0) return; /* no fds, really? */ else if (sz == -1) goto fallback; if ((fdinfo_buf = malloc(sz)) == NULL) goto fallback; r = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, fdinfo_buf, sz); if (r < 0 || r > sz) goto fallback; for (i = 0; i < r / (int)PROC_PIDLISTFD_SIZE; i++) { if (fdinfo_buf[i].proc_fd >= lowfd) close(fdinfo_buf[i].proc_fd); } free(fdinfo_buf); return; fallback: free(fdinfo_buf); closefrom_fallback(lowfd); return; } #elif defined(HAVE_DIRFD) && defined(HAVE_PROC_PID) void closefrom(int lowfd) { long fd; char fdpath[PATH_MAX], *endp; struct dirent *dent; DIR *dirp; int len; #ifdef HAVE_CLOSE_RANGE if (close_range(lowfd, INT_MAX, 0) == 0) return; #endif /* Check for a /proc/$$/fd directory. */ len = snprintf(fdpath, sizeof(fdpath), "/proc/%ld/fd", (long)getpid()); if (len > 0 && (size_t)len < sizeof(fdpath) && (dirp = opendir(fdpath))) { while ((dent = readdir(dirp)) != NULL) { fd = strtol(dent->d_name, &endp, 10); if (dent->d_name != endp && *endp == '\0' && fd >= 0 && fd < INT_MAX && fd >= lowfd && fd != dirfd(dirp)) (void) close((int) fd); } (void) closedir(dirp); return; } /* /proc/$$/fd strategy failed, fall back to brute force closure */ closefrom_fallback(lowfd); } #else void closefrom(int lowfd) { closefrom_fallback(lowfd); } #endif /* !HAVE_FCNTL_CLOSEM */ #endif /* HAVE_CLOSEFROM */ diff --git a/packet.c b/packet.c index fdb8783bc315..52017defb642 100644 --- a/packet.c +++ b/packet.c @@ -1,2719 +1,2750 @@ -/* $OpenBSD: packet.c,v 1.310 2023/04/06 03:21:31 djm Exp $ */ +/* $OpenBSD: packet.c,v 1.312 2023/08/28 03:31:16 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * This file contains code implementing the packet protocol and communication * with the other side. This same code is used both on client and server side. * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". * * * SSH2 packet format added by Markus Friedl. * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" #include #include "openbsd-compat/sys-queue.h" #include #ifdef HAVE_SYS_TIME_H # include #endif #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_POLL_H #include #endif #include #include /* * Explicitly include OpenSSL before zlib as some versions of OpenSSL have * "free_func" in their headers, which zlib typedefs. */ #ifdef WITH_OPENSSL # include # include # ifdef OPENSSL_HAS_ECC # include # endif #endif #ifdef WITH_ZLIB #include #endif #include "xmalloc.h" #include "compat.h" #include "ssh2.h" #include "cipher.h" #include "sshkey.h" #include "kex.h" #include "digest.h" #include "mac.h" #include "log.h" #include "canohost.h" #include "misc.h" #include "channels.h" #include "ssh.h" #include "packet.h" #include "ssherr.h" #include "sshbuf.h" #ifdef PACKET_DEBUG #define DBG(x) x #else #define DBG(x) #endif #define PACKET_MAX_SIZE (256 * 1024) struct packet_state { u_int32_t seqnr; u_int32_t packets; u_int64_t blocks; u_int64_t bytes; }; struct packet { TAILQ_ENTRY(packet) next; u_char type; struct sshbuf *payload; }; struct session_state { /* * This variable contains the file descriptors used for * communicating with the other side. connection_in is used for * reading; connection_out for writing. These can be the same * descriptor, in which case it is assumed to be a socket. */ int connection_in; int connection_out; /* Protocol flags for the remote side. */ u_int remote_protocol_flags; /* Encryption context for receiving data. Only used for decryption. */ struct sshcipher_ctx *receive_context; /* Encryption context for sending data. Only used for encryption. */ struct sshcipher_ctx *send_context; /* Buffer for raw input data from the socket. */ struct sshbuf *input; /* Buffer for raw output data going to the socket. */ struct sshbuf *output; /* Buffer for the partial outgoing packet being constructed. */ struct sshbuf *outgoing_packet; /* Buffer for the incoming packet currently being processed. */ struct sshbuf *incoming_packet; /* Scratch buffer for packet compression/decompression. */ struct sshbuf *compression_buffer; #ifdef WITH_ZLIB /* Incoming/outgoing compression dictionaries */ z_stream compression_in_stream; z_stream compression_out_stream; #endif int compression_in_started; int compression_out_started; int compression_in_failures; int compression_out_failures; /* default maximum packet size */ u_int max_packet_size; /* Flag indicating whether this module has been initialized. */ int initialized; /* Set to true if the connection is interactive. */ int interactive_mode; /* Set to true if we are the server side. */ int server_side; /* Set to true if we are authenticated. */ int after_authentication; int keep_alive_timeouts; /* The maximum time that we will wait to send or receive a packet */ int packet_timeout_ms; /* Session key information for Encryption and MAC */ struct newkeys *newkeys[MODE_MAX]; struct packet_state p_read, p_send; /* Volume-based rekeying */ u_int64_t max_blocks_in, max_blocks_out, rekey_limit; /* Time-based rekeying */ u_int32_t rekey_interval; /* how often in seconds */ time_t rekey_time; /* time of last rekeying */ /* roundup current message to extra_pad bytes */ u_char extra_pad; /* XXX discard incoming data after MAC error */ u_int packet_discard; size_t packet_discard_mac_already; struct sshmac *packet_discard_mac; /* Used in packet_read_poll2() */ u_int packlen; /* Used in packet_send2 */ int rekeying; /* Used in ssh_packet_send_mux() */ int mux; /* Used in packet_set_interactive */ int set_interactive_called; /* Used in packet_set_maxsize */ int set_maxsize_called; /* One-off warning about weak ciphers */ int cipher_warning_done; /* Hook for fuzzing inbound packets */ ssh_packet_hook_fn *hook_in; void *hook_in_ctx; TAILQ_HEAD(, packet) outgoing; }; struct ssh * ssh_alloc_session_state(void) { struct ssh *ssh = NULL; struct session_state *state = NULL; if ((ssh = calloc(1, sizeof(*ssh))) == NULL || (state = calloc(1, sizeof(*state))) == NULL || (ssh->kex = kex_new()) == NULL || (state->input = sshbuf_new()) == NULL || (state->output = sshbuf_new()) == NULL || (state->outgoing_packet = sshbuf_new()) == NULL || (state->incoming_packet = sshbuf_new()) == NULL) goto fail; TAILQ_INIT(&state->outgoing); TAILQ_INIT(&ssh->private_keys); TAILQ_INIT(&ssh->public_keys); state->connection_in = -1; state->connection_out = -1; state->max_packet_size = 32768; state->packet_timeout_ms = -1; state->p_send.packets = state->p_read.packets = 0; state->initialized = 1; /* * ssh_packet_send2() needs to queue packets until * we've done the initial key exchange. */ state->rekeying = 1; ssh->state = state; return ssh; fail: if (ssh) { kex_free(ssh->kex); free(ssh); } if (state) { sshbuf_free(state->input); sshbuf_free(state->output); sshbuf_free(state->incoming_packet); sshbuf_free(state->outgoing_packet); free(state); } return NULL; } void ssh_packet_set_input_hook(struct ssh *ssh, ssh_packet_hook_fn *hook, void *ctx) { ssh->state->hook_in = hook; ssh->state->hook_in_ctx = ctx; } /* Returns nonzero if rekeying is in progress */ int ssh_packet_is_rekeying(struct ssh *ssh) { return ssh->state->rekeying || (ssh->kex != NULL && ssh->kex->done == 0); } /* * Sets the descriptors used for communication. */ struct ssh * ssh_packet_set_connection(struct ssh *ssh, int fd_in, int fd_out) { struct session_state *state; const struct sshcipher *none = cipher_by_name("none"); int r; if (none == NULL) { error_f("cannot load cipher 'none'"); return NULL; } if (ssh == NULL) ssh = ssh_alloc_session_state(); if (ssh == NULL) { error_f("could not allocate state"); return NULL; } state = ssh->state; state->connection_in = fd_in; state->connection_out = fd_out; if ((r = cipher_init(&state->send_context, none, (const u_char *)"", 0, NULL, 0, CIPHER_ENCRYPT)) != 0 || (r = cipher_init(&state->receive_context, none, (const u_char *)"", 0, NULL, 0, CIPHER_DECRYPT)) != 0) { error_fr(r, "cipher_init failed"); free(ssh); /* XXX need ssh_free_session_state? */ return NULL; } state->newkeys[MODE_IN] = state->newkeys[MODE_OUT] = NULL; /* * Cache the IP address of the remote connection for use in error * messages that might be generated after the connection has closed. */ (void)ssh_remote_ipaddr(ssh); return ssh; } void ssh_packet_set_timeout(struct ssh *ssh, int timeout, int count) { struct session_state *state = ssh->state; if (timeout <= 0 || count <= 0) { state->packet_timeout_ms = -1; return; } if ((INT_MAX / 1000) / count < timeout) state->packet_timeout_ms = INT_MAX; else state->packet_timeout_ms = timeout * count * 1000; } void ssh_packet_set_mux(struct ssh *ssh) { ssh->state->mux = 1; ssh->state->rekeying = 0; kex_free(ssh->kex); ssh->kex = NULL; } int ssh_packet_get_mux(struct ssh *ssh) { return ssh->state->mux; } int ssh_packet_set_log_preamble(struct ssh *ssh, const char *fmt, ...) { va_list args; int r; free(ssh->log_preamble); if (fmt == NULL) ssh->log_preamble = NULL; else { va_start(args, fmt); r = vasprintf(&ssh->log_preamble, fmt, args); va_end(args); if (r < 0 || ssh->log_preamble == NULL) return SSH_ERR_ALLOC_FAIL; } return 0; } int ssh_packet_stop_discard(struct ssh *ssh) { struct session_state *state = ssh->state; int r; if (state->packet_discard_mac) { char buf[1024]; size_t dlen = PACKET_MAX_SIZE; if (dlen > state->packet_discard_mac_already) dlen -= state->packet_discard_mac_already; memset(buf, 'a', sizeof(buf)); while (sshbuf_len(state->incoming_packet) < dlen) if ((r = sshbuf_put(state->incoming_packet, buf, sizeof(buf))) != 0) return r; (void) mac_compute(state->packet_discard_mac, state->p_read.seqnr, sshbuf_ptr(state->incoming_packet), dlen, NULL, 0); } logit("Finished discarding for %.200s port %d", ssh_remote_ipaddr(ssh), ssh_remote_port(ssh)); return SSH_ERR_MAC_INVALID; } static int ssh_packet_start_discard(struct ssh *ssh, struct sshenc *enc, struct sshmac *mac, size_t mac_already, u_int discard) { struct session_state *state = ssh->state; int r; if (enc == NULL || !cipher_is_cbc(enc->cipher) || (mac && mac->etm)) { if ((r = sshpkt_disconnect(ssh, "Packet corrupt")) != 0) return r; return SSH_ERR_MAC_INVALID; } /* * Record number of bytes over which the mac has already * been computed in order to minimize timing attacks. */ if (mac && mac->enabled) { state->packet_discard_mac = mac; state->packet_discard_mac_already = mac_already; } if (sshbuf_len(state->input) >= discard) return ssh_packet_stop_discard(ssh); state->packet_discard = discard - sshbuf_len(state->input); return 0; } /* Returns 1 if remote host is connected via socket, 0 if not. */ int ssh_packet_connection_is_on_socket(struct ssh *ssh) { struct session_state *state; struct sockaddr_storage from, to; socklen_t fromlen, tolen; if (ssh == NULL || ssh->state == NULL) return 0; state = ssh->state; if (state->connection_in == -1 || state->connection_out == -1) return 0; /* filedescriptors in and out are the same, so it's a socket */ if (state->connection_in == state->connection_out) return 1; fromlen = sizeof(from); memset(&from, 0, sizeof(from)); if (getpeername(state->connection_in, (struct sockaddr *)&from, &fromlen) == -1) return 0; tolen = sizeof(to); memset(&to, 0, sizeof(to)); if (getpeername(state->connection_out, (struct sockaddr *)&to, &tolen) == -1) return 0; if (fromlen != tolen || memcmp(&from, &to, fromlen) != 0) return 0; if (from.ss_family != AF_INET && from.ss_family != AF_INET6) return 0; return 1; } void ssh_packet_get_bytes(struct ssh *ssh, u_int64_t *ibytes, u_int64_t *obytes) { if (ibytes) *ibytes = ssh->state->p_read.bytes; if (obytes) *obytes = ssh->state->p_send.bytes; } int ssh_packet_connection_af(struct ssh *ssh) { return get_sock_af(ssh->state->connection_out); } /* Sets the connection into non-blocking mode. */ void ssh_packet_set_nonblocking(struct ssh *ssh) { /* Set the socket into non-blocking mode. */ set_nonblock(ssh->state->connection_in); if (ssh->state->connection_out != ssh->state->connection_in) set_nonblock(ssh->state->connection_out); } /* Returns the socket used for reading. */ int ssh_packet_get_connection_in(struct ssh *ssh) { return ssh->state->connection_in; } /* Returns the descriptor used for writing. */ int ssh_packet_get_connection_out(struct ssh *ssh) { return ssh->state->connection_out; } /* * Returns the IP-address of the remote host as a string. The returned * string must not be freed. */ const char * ssh_remote_ipaddr(struct ssh *ssh) { int sock; /* Check whether we have cached the ipaddr. */ if (ssh->remote_ipaddr == NULL) { if (ssh_packet_connection_is_on_socket(ssh)) { sock = ssh->state->connection_in; ssh->remote_ipaddr = get_peer_ipaddr(sock); ssh->remote_port = get_peer_port(sock); ssh->local_ipaddr = get_local_ipaddr(sock); ssh->local_port = get_local_port(sock); } else { ssh->remote_ipaddr = xstrdup("UNKNOWN"); ssh->remote_port = 65535; ssh->local_ipaddr = xstrdup("UNKNOWN"); ssh->local_port = 65535; } } return ssh->remote_ipaddr; } /* Returns the port number of the remote host. */ int ssh_remote_port(struct ssh *ssh) { (void)ssh_remote_ipaddr(ssh); /* Will lookup and cache. */ return ssh->remote_port; } /* * Returns the IP-address of the local host as a string. The returned * string must not be freed. */ const char * ssh_local_ipaddr(struct ssh *ssh) { (void)ssh_remote_ipaddr(ssh); /* Will lookup and cache. */ return ssh->local_ipaddr; } /* Returns the port number of the local host. */ int ssh_local_port(struct ssh *ssh) { (void)ssh_remote_ipaddr(ssh); /* Will lookup and cache. */ return ssh->local_port; } /* Returns the routing domain of the input socket, or NULL if unavailable */ const char * ssh_packet_rdomain_in(struct ssh *ssh) { if (ssh->rdomain_in != NULL) return ssh->rdomain_in; if (!ssh_packet_connection_is_on_socket(ssh)) return NULL; ssh->rdomain_in = get_rdomain(ssh->state->connection_in); return ssh->rdomain_in; } /* Closes the connection and clears and frees internal data structures. */ static void ssh_packet_close_internal(struct ssh *ssh, int do_close) { struct session_state *state = ssh->state; u_int mode; if (!state->initialized) return; state->initialized = 0; if (do_close) { if (state->connection_in == state->connection_out) { close(state->connection_out); } else { close(state->connection_in); close(state->connection_out); } } sshbuf_free(state->input); sshbuf_free(state->output); sshbuf_free(state->outgoing_packet); sshbuf_free(state->incoming_packet); for (mode = 0; mode < MODE_MAX; mode++) { kex_free_newkeys(state->newkeys[mode]); /* current keys */ state->newkeys[mode] = NULL; ssh_clear_newkeys(ssh, mode); /* next keys */ } #ifdef WITH_ZLIB /* compression state is in shared mem, so we can only release it once */ if (do_close && state->compression_buffer) { sshbuf_free(state->compression_buffer); if (state->compression_out_started) { z_streamp stream = &state->compression_out_stream; debug("compress outgoing: " "raw data %llu, compressed %llu, factor %.2f", (unsigned long long)stream->total_in, (unsigned long long)stream->total_out, stream->total_in == 0 ? 0.0 : (double) stream->total_out / stream->total_in); if (state->compression_out_failures == 0) deflateEnd(stream); } if (state->compression_in_started) { z_streamp stream = &state->compression_in_stream; debug("compress incoming: " "raw data %llu, compressed %llu, factor %.2f", (unsigned long long)stream->total_out, (unsigned long long)stream->total_in, stream->total_out == 0 ? 0.0 : (double) stream->total_in / stream->total_out); if (state->compression_in_failures == 0) inflateEnd(stream); } } #endif /* WITH_ZLIB */ cipher_free(state->send_context); cipher_free(state->receive_context); state->send_context = state->receive_context = NULL; if (do_close) { free(ssh->local_ipaddr); ssh->local_ipaddr = NULL; free(ssh->remote_ipaddr); ssh->remote_ipaddr = NULL; free(ssh->state); ssh->state = NULL; kex_free(ssh->kex); ssh->kex = NULL; } } void ssh_packet_close(struct ssh *ssh) { ssh_packet_close_internal(ssh, 1); } void ssh_packet_clear_keys(struct ssh *ssh) { ssh_packet_close_internal(ssh, 0); } /* Sets remote side protocol flags. */ void ssh_packet_set_protocol_flags(struct ssh *ssh, u_int protocol_flags) { ssh->state->remote_protocol_flags = protocol_flags; } /* Returns the remote protocol flags set earlier by the above function. */ u_int ssh_packet_get_protocol_flags(struct ssh *ssh) { return ssh->state->remote_protocol_flags; } /* * Starts packet compression from the next packet on in both directions. * Level is compression level 1 (fastest) - 9 (slow, best) as in gzip. */ static int ssh_packet_init_compression(struct ssh *ssh) { if (!ssh->state->compression_buffer && ((ssh->state->compression_buffer = sshbuf_new()) == NULL)) return SSH_ERR_ALLOC_FAIL; return 0; } #ifdef WITH_ZLIB static int start_compression_out(struct ssh *ssh, int level) { if (level < 1 || level > 9) return SSH_ERR_INVALID_ARGUMENT; debug("Enabling compression at level %d.", level); if (ssh->state->compression_out_started == 1) deflateEnd(&ssh->state->compression_out_stream); switch (deflateInit(&ssh->state->compression_out_stream, level)) { case Z_OK: ssh->state->compression_out_started = 1; break; case Z_MEM_ERROR: return SSH_ERR_ALLOC_FAIL; default: return SSH_ERR_INTERNAL_ERROR; } return 0; } static int start_compression_in(struct ssh *ssh) { if (ssh->state->compression_in_started == 1) inflateEnd(&ssh->state->compression_in_stream); switch (inflateInit(&ssh->state->compression_in_stream)) { case Z_OK: ssh->state->compression_in_started = 1; break; case Z_MEM_ERROR: return SSH_ERR_ALLOC_FAIL; default: return SSH_ERR_INTERNAL_ERROR; } return 0; } /* XXX remove need for separate compression buffer */ static int compress_buffer(struct ssh *ssh, struct sshbuf *in, struct sshbuf *out) { u_char buf[4096]; int r, status; if (ssh->state->compression_out_started != 1) return SSH_ERR_INTERNAL_ERROR; /* This case is not handled below. */ if (sshbuf_len(in) == 0) return 0; /* Input is the contents of the input buffer. */ if ((ssh->state->compression_out_stream.next_in = sshbuf_mutable_ptr(in)) == NULL) return SSH_ERR_INTERNAL_ERROR; ssh->state->compression_out_stream.avail_in = sshbuf_len(in); /* Loop compressing until deflate() returns with avail_out != 0. */ do { /* Set up fixed-size output buffer. */ ssh->state->compression_out_stream.next_out = buf; ssh->state->compression_out_stream.avail_out = sizeof(buf); /* Compress as much data into the buffer as possible. */ status = deflate(&ssh->state->compression_out_stream, Z_PARTIAL_FLUSH); switch (status) { case Z_MEM_ERROR: return SSH_ERR_ALLOC_FAIL; case Z_OK: /* Append compressed data to output_buffer. */ if ((r = sshbuf_put(out, buf, sizeof(buf) - ssh->state->compression_out_stream.avail_out)) != 0) return r; break; case Z_STREAM_ERROR: default: ssh->state->compression_out_failures++; return SSH_ERR_INVALID_FORMAT; } } while (ssh->state->compression_out_stream.avail_out == 0); return 0; } static int uncompress_buffer(struct ssh *ssh, struct sshbuf *in, struct sshbuf *out) { u_char buf[4096]; int r, status; if (ssh->state->compression_in_started != 1) return SSH_ERR_INTERNAL_ERROR; if ((ssh->state->compression_in_stream.next_in = sshbuf_mutable_ptr(in)) == NULL) return SSH_ERR_INTERNAL_ERROR; ssh->state->compression_in_stream.avail_in = sshbuf_len(in); for (;;) { /* Set up fixed-size output buffer. */ ssh->state->compression_in_stream.next_out = buf; ssh->state->compression_in_stream.avail_out = sizeof(buf); status = inflate(&ssh->state->compression_in_stream, Z_SYNC_FLUSH); switch (status) { case Z_OK: if ((r = sshbuf_put(out, buf, sizeof(buf) - ssh->state->compression_in_stream.avail_out)) != 0) return r; break; case Z_BUF_ERROR: /* * Comments in zlib.h say that we should keep calling * inflate() until we get an error. This appears to * be the error that we get. */ return 0; case Z_DATA_ERROR: return SSH_ERR_INVALID_FORMAT; case Z_MEM_ERROR: return SSH_ERR_ALLOC_FAIL; case Z_STREAM_ERROR: default: ssh->state->compression_in_failures++; return SSH_ERR_INTERNAL_ERROR; } } /* NOTREACHED */ } #else /* WITH_ZLIB */ static int start_compression_out(struct ssh *ssh, int level) { return SSH_ERR_INTERNAL_ERROR; } static int start_compression_in(struct ssh *ssh) { return SSH_ERR_INTERNAL_ERROR; } static int compress_buffer(struct ssh *ssh, struct sshbuf *in, struct sshbuf *out) { return SSH_ERR_INTERNAL_ERROR; } static int uncompress_buffer(struct ssh *ssh, struct sshbuf *in, struct sshbuf *out) { return SSH_ERR_INTERNAL_ERROR; } #endif /* WITH_ZLIB */ void ssh_clear_newkeys(struct ssh *ssh, int mode) { if (ssh->kex && ssh->kex->newkeys[mode]) { kex_free_newkeys(ssh->kex->newkeys[mode]); ssh->kex->newkeys[mode] = NULL; } } int ssh_set_newkeys(struct ssh *ssh, int mode) { struct session_state *state = ssh->state; struct sshenc *enc; struct sshmac *mac; struct sshcomp *comp; struct sshcipher_ctx **ccp; struct packet_state *ps; u_int64_t *max_blocks; const char *wmsg; int r, crypt_type; const char *dir = mode == MODE_OUT ? "out" : "in"; debug2_f("mode %d", mode); if (mode == MODE_OUT) { ccp = &state->send_context; crypt_type = CIPHER_ENCRYPT; ps = &state->p_send; max_blocks = &state->max_blocks_out; } else { ccp = &state->receive_context; crypt_type = CIPHER_DECRYPT; ps = &state->p_read; max_blocks = &state->max_blocks_in; } if (state->newkeys[mode] != NULL) { debug_f("rekeying %s, input %llu bytes %llu blocks, " "output %llu bytes %llu blocks", dir, (unsigned long long)state->p_read.bytes, (unsigned long long)state->p_read.blocks, (unsigned long long)state->p_send.bytes, (unsigned long long)state->p_send.blocks); kex_free_newkeys(state->newkeys[mode]); state->newkeys[mode] = NULL; } /* note that both bytes and the seqnr are not reset */ ps->packets = ps->blocks = 0; /* move newkeys from kex to state */ if ((state->newkeys[mode] = ssh->kex->newkeys[mode]) == NULL) return SSH_ERR_INTERNAL_ERROR; ssh->kex->newkeys[mode] = NULL; enc = &state->newkeys[mode]->enc; mac = &state->newkeys[mode]->mac; comp = &state->newkeys[mode]->comp; if (cipher_authlen(enc->cipher) == 0) { if ((r = mac_init(mac)) != 0) return r; } mac->enabled = 1; DBG(debug_f("cipher_init: %s", dir)); cipher_free(*ccp); *ccp = NULL; if ((r = cipher_init(ccp, enc->cipher, enc->key, enc->key_len, enc->iv, enc->iv_len, crypt_type)) != 0) return r; if (!state->cipher_warning_done && (wmsg = cipher_warning_message(*ccp)) != NULL) { error("Warning: %s", wmsg); state->cipher_warning_done = 1; } /* Deleting the keys does not gain extra security */ /* explicit_bzero(enc->iv, enc->block_size); explicit_bzero(enc->key, enc->key_len); explicit_bzero(mac->key, mac->key_len); */ if ((comp->type == COMP_ZLIB || (comp->type == COMP_DELAYED && state->after_authentication)) && comp->enabled == 0) { if ((r = ssh_packet_init_compression(ssh)) < 0) return r; if (mode == MODE_OUT) { if ((r = start_compression_out(ssh, 6)) != 0) return r; } else { if ((r = start_compression_in(ssh)) != 0) return r; } comp->enabled = 1; } /* * The 2^(blocksize*2) limit is too expensive for 3DES, * so enforce a 1GB limit for small blocksizes. * See RFC4344 section 3.2. */ if (enc->block_size >= 16) *max_blocks = (u_int64_t)1 << (enc->block_size*2); else *max_blocks = ((u_int64_t)1 << 30) / enc->block_size; if (state->rekey_limit) *max_blocks = MINIMUM(*max_blocks, state->rekey_limit / enc->block_size); debug("rekey %s after %llu blocks", dir, (unsigned long long)*max_blocks); return 0; } #define MAX_PACKETS (1U<<31) static int ssh_packet_need_rekeying(struct ssh *ssh, u_int outbound_packet_len) { struct session_state *state = ssh->state; u_int32_t out_blocks; /* XXX client can't cope with rekeying pre-auth */ if (!state->after_authentication) return 0; /* Haven't keyed yet or KEX in progress. */ if (ssh_packet_is_rekeying(ssh)) return 0; /* Peer can't rekey */ if (ssh->compat & SSH_BUG_NOREKEY) return 0; /* * Permit one packet in or out per rekey - this allows us to * make progress when rekey limits are very small. */ if (state->p_send.packets == 0 && state->p_read.packets == 0) return 0; /* Time-based rekeying */ if (state->rekey_interval != 0 && (int64_t)state->rekey_time + state->rekey_interval <= monotime()) return 1; /* * Always rekey when MAX_PACKETS sent in either direction * As per RFC4344 section 3.1 we do this after 2^31 packets. */ if (state->p_send.packets > MAX_PACKETS || state->p_read.packets > MAX_PACKETS) return 1; /* Rekey after (cipher-specific) maximum blocks */ out_blocks = ROUNDUP(outbound_packet_len, state->newkeys[MODE_OUT]->enc.block_size); return (state->max_blocks_out && (state->p_send.blocks + out_blocks > state->max_blocks_out)) || (state->max_blocks_in && (state->p_read.blocks > state->max_blocks_in)); } int ssh_packet_check_rekey(struct ssh *ssh) { if (!ssh_packet_need_rekeying(ssh, 0)) return 0; debug3_f("rekex triggered"); return kex_start_rekex(ssh); } /* * Delayed compression for SSH2 is enabled after authentication: * This happens on the server side after a SSH2_MSG_USERAUTH_SUCCESS is sent, * and on the client side after a SSH2_MSG_USERAUTH_SUCCESS is received. */ static int ssh_packet_enable_delayed_compress(struct ssh *ssh) { struct session_state *state = ssh->state; struct sshcomp *comp = NULL; int r, mode; /* * Remember that we are past the authentication step, so rekeying * with COMP_DELAYED will turn on compression immediately. */ state->after_authentication = 1; for (mode = 0; mode < MODE_MAX; mode++) { /* protocol error: USERAUTH_SUCCESS received before NEWKEYS */ if (state->newkeys[mode] == NULL) continue; comp = &state->newkeys[mode]->comp; if (comp && !comp->enabled && comp->type == COMP_DELAYED) { if ((r = ssh_packet_init_compression(ssh)) != 0) return r; if (mode == MODE_OUT) { if ((r = start_compression_out(ssh, 6)) != 0) return r; } else { if ((r = start_compression_in(ssh)) != 0) return r; } comp->enabled = 1; } } return 0; } /* Used to mute debug logging for noisy packet types */ int ssh_packet_log_type(u_char type) { switch (type) { + case SSH2_MSG_PING: + case SSH2_MSG_PONG: case SSH2_MSG_CHANNEL_DATA: case SSH2_MSG_CHANNEL_EXTENDED_DATA: case SSH2_MSG_CHANNEL_WINDOW_ADJUST: return 0; default: return 1; } } /* * Finalize packet in SSH2 format (compress, mac, encrypt, enqueue) */ int ssh_packet_send2_wrapped(struct ssh *ssh) { struct session_state *state = ssh->state; u_char type, *cp, macbuf[SSH_DIGEST_MAX_LENGTH]; u_char tmp, padlen, pad = 0; u_int authlen = 0, aadlen = 0; u_int len; struct sshenc *enc = NULL; struct sshmac *mac = NULL; struct sshcomp *comp = NULL; int r, block_size; if (state->newkeys[MODE_OUT] != NULL) { enc = &state->newkeys[MODE_OUT]->enc; mac = &state->newkeys[MODE_OUT]->mac; comp = &state->newkeys[MODE_OUT]->comp; /* disable mac for authenticated encryption */ if ((authlen = cipher_authlen(enc->cipher)) != 0) mac = NULL; } block_size = enc ? enc->block_size : 8; aadlen = (mac && mac->enabled && mac->etm) || authlen ? 4 : 0; type = (sshbuf_ptr(state->outgoing_packet))[5]; if (ssh_packet_log_type(type)) debug3("send packet: type %u", type); #ifdef PACKET_DEBUG fprintf(stderr, "plain: "); sshbuf_dump(state->outgoing_packet, stderr); #endif if (comp && comp->enabled) { len = sshbuf_len(state->outgoing_packet); /* skip header, compress only payload */ if ((r = sshbuf_consume(state->outgoing_packet, 5)) != 0) goto out; sshbuf_reset(state->compression_buffer); if ((r = compress_buffer(ssh, state->outgoing_packet, state->compression_buffer)) != 0) goto out; sshbuf_reset(state->outgoing_packet); if ((r = sshbuf_put(state->outgoing_packet, "\0\0\0\0\0", 5)) != 0 || (r = sshbuf_putb(state->outgoing_packet, state->compression_buffer)) != 0) goto out; DBG(debug("compression: raw %d compressed %zd", len, sshbuf_len(state->outgoing_packet))); } /* sizeof (packet_len + pad_len + payload) */ len = sshbuf_len(state->outgoing_packet); /* * calc size of padding, alloc space, get random data, * minimum padding is 4 bytes */ len -= aadlen; /* packet length is not encrypted for EtM modes */ padlen = block_size - (len % block_size); if (padlen < 4) padlen += block_size; if (state->extra_pad) { tmp = state->extra_pad; state->extra_pad = ROUNDUP(state->extra_pad, block_size); /* check if roundup overflowed */ if (state->extra_pad < tmp) return SSH_ERR_INVALID_ARGUMENT; tmp = (len + padlen) % state->extra_pad; /* Check whether pad calculation below will underflow */ if (tmp > state->extra_pad) return SSH_ERR_INVALID_ARGUMENT; pad = state->extra_pad - tmp; DBG(debug3_f("adding %d (len %d padlen %d extra_pad %d)", pad, len, padlen, state->extra_pad)); tmp = padlen; padlen += pad; /* Check whether padlen calculation overflowed */ if (padlen < tmp) return SSH_ERR_INVALID_ARGUMENT; /* overflow */ state->extra_pad = 0; } if ((r = sshbuf_reserve(state->outgoing_packet, padlen, &cp)) != 0) goto out; if (enc && !cipher_ctx_is_plaintext(state->send_context)) { /* random padding */ arc4random_buf(cp, padlen); } else { /* clear padding */ explicit_bzero(cp, padlen); } /* sizeof (packet_len + pad_len + payload + padding) */ len = sshbuf_len(state->outgoing_packet); cp = sshbuf_mutable_ptr(state->outgoing_packet); if (cp == NULL) { r = SSH_ERR_INTERNAL_ERROR; goto out; } /* packet_length includes payload, padding and padding length field */ POKE_U32(cp, len - 4); cp[4] = padlen; DBG(debug("send: len %d (includes padlen %d, aadlen %d)", len, padlen, aadlen)); /* compute MAC over seqnr and packet(length fields, payload, padding) */ if (mac && mac->enabled && !mac->etm) { if ((r = mac_compute(mac, state->p_send.seqnr, sshbuf_ptr(state->outgoing_packet), len, macbuf, sizeof(macbuf))) != 0) goto out; DBG(debug("done calc MAC out #%d", state->p_send.seqnr)); } /* encrypt packet and append to output buffer. */ if ((r = sshbuf_reserve(state->output, sshbuf_len(state->outgoing_packet) + authlen, &cp)) != 0) goto out; if ((r = cipher_crypt(state->send_context, state->p_send.seqnr, cp, sshbuf_ptr(state->outgoing_packet), len - aadlen, aadlen, authlen)) != 0) goto out; /* append unencrypted MAC */ if (mac && mac->enabled) { if (mac->etm) { /* EtM: compute mac over aadlen + cipher text */ if ((r = mac_compute(mac, state->p_send.seqnr, cp, len, macbuf, sizeof(macbuf))) != 0) goto out; DBG(debug("done calc MAC(EtM) out #%d", state->p_send.seqnr)); } if ((r = sshbuf_put(state->output, macbuf, mac->mac_len)) != 0) goto out; } #ifdef PACKET_DEBUG fprintf(stderr, "encrypted: "); sshbuf_dump(state->output, stderr); #endif /* increment sequence number for outgoing packets */ if (++state->p_send.seqnr == 0) logit("outgoing seqnr wraps around"); if (++state->p_send.packets == 0) if (!(ssh->compat & SSH_BUG_NOREKEY)) return SSH_ERR_NEED_REKEY; state->p_send.blocks += len / block_size; state->p_send.bytes += len; sshbuf_reset(state->outgoing_packet); if (type == SSH2_MSG_NEWKEYS) r = ssh_set_newkeys(ssh, MODE_OUT); else if (type == SSH2_MSG_USERAUTH_SUCCESS && state->server_side) r = ssh_packet_enable_delayed_compress(ssh); else r = 0; out: return r; } /* returns non-zero if the specified packet type is usec by KEX */ static int ssh_packet_type_is_kex(u_char type) { return type >= SSH2_MSG_TRANSPORT_MIN && type <= SSH2_MSG_TRANSPORT_MAX && type != SSH2_MSG_SERVICE_REQUEST && type != SSH2_MSG_SERVICE_ACCEPT && type != SSH2_MSG_EXT_INFO; } int ssh_packet_send2(struct ssh *ssh) { struct session_state *state = ssh->state; struct packet *p; u_char type; int r, need_rekey; if (sshbuf_len(state->outgoing_packet) < 6) return SSH_ERR_INTERNAL_ERROR; type = sshbuf_ptr(state->outgoing_packet)[5]; need_rekey = !ssh_packet_type_is_kex(type) && ssh_packet_need_rekeying(ssh, sshbuf_len(state->outgoing_packet)); /* * During rekeying we can only send key exchange messages. * Queue everything else. */ if ((need_rekey || state->rekeying) && !ssh_packet_type_is_kex(type)) { if (need_rekey) debug3_f("rekex triggered"); debug("enqueue packet: %u", type); p = calloc(1, sizeof(*p)); if (p == NULL) return SSH_ERR_ALLOC_FAIL; p->type = type; p->payload = state->outgoing_packet; TAILQ_INSERT_TAIL(&state->outgoing, p, next); state->outgoing_packet = sshbuf_new(); if (state->outgoing_packet == NULL) return SSH_ERR_ALLOC_FAIL; if (need_rekey) { /* * This packet triggered a rekey, so send the * KEXINIT now. * NB. reenters this function via kex_start_rekex(). */ return kex_start_rekex(ssh); } return 0; } /* rekeying starts with sending KEXINIT */ if (type == SSH2_MSG_KEXINIT) state->rekeying = 1; if ((r = ssh_packet_send2_wrapped(ssh)) != 0) return r; /* after a NEWKEYS message we can send the complete queue */ if (type == SSH2_MSG_NEWKEYS) { state->rekeying = 0; state->rekey_time = monotime(); while ((p = TAILQ_FIRST(&state->outgoing))) { type = p->type; /* * If this packet triggers a rekex, then skip the * remaining packets in the queue for now. * NB. re-enters this function via kex_start_rekex. */ if (ssh_packet_need_rekeying(ssh, sshbuf_len(p->payload))) { debug3_f("queued packet triggered rekex"); return kex_start_rekex(ssh); } debug("dequeue packet: %u", type); sshbuf_free(state->outgoing_packet); state->outgoing_packet = p->payload; TAILQ_REMOVE(&state->outgoing, p, next); memset(p, 0, sizeof(*p)); free(p); if ((r = ssh_packet_send2_wrapped(ssh)) != 0) return r; } } return 0; } /* * Waits until a packet has been received, and returns its type. Note that * no other data is processed until this returns, so this function should not * be used during the interactive session. */ int ssh_packet_read_seqnr(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p) { struct session_state *state = ssh->state; int len, r, ms_remain = 0; struct pollfd pfd; char buf[8192]; struct timeval start; struct timespec timespec, *timespecp = NULL; DBG(debug("packet_read()")); /* * Since we are blocking, ensure that all written packets have * been sent. */ if ((r = ssh_packet_write_wait(ssh)) != 0) goto out; /* Stay in the loop until we have received a complete packet. */ for (;;) { /* Try to read a packet from the buffer. */ r = ssh_packet_read_poll_seqnr(ssh, typep, seqnr_p); if (r != 0) break; /* If we got a packet, return it. */ if (*typep != SSH_MSG_NONE) break; /* * Otherwise, wait for some data to arrive, add it to the * buffer, and try again. */ pfd.fd = state->connection_in; pfd.events = POLLIN; if (state->packet_timeout_ms > 0) { ms_remain = state->packet_timeout_ms; timespecp = ×pec; } /* Wait for some data to arrive. */ for (;;) { if (state->packet_timeout_ms > 0) { ms_to_timespec(×pec, ms_remain); monotime_tv(&start); } if ((r = ppoll(&pfd, 1, timespecp, NULL)) >= 0) break; if (errno != EAGAIN && errno != EINTR && errno != EWOULDBLOCK) { r = SSH_ERR_SYSTEM_ERROR; goto out; } if (state->packet_timeout_ms <= 0) continue; ms_subtract_diff(&start, &ms_remain); if (ms_remain <= 0) { r = 0; break; } } if (r == 0) { r = SSH_ERR_CONN_TIMEOUT; goto out; } /* Read data from the socket. */ len = read(state->connection_in, buf, sizeof(buf)); if (len == 0) { r = SSH_ERR_CONN_CLOSED; goto out; } if (len == -1) { r = SSH_ERR_SYSTEM_ERROR; goto out; } /* Append it to the buffer. */ if ((r = ssh_packet_process_incoming(ssh, buf, len)) != 0) goto out; } out: return r; } int ssh_packet_read(struct ssh *ssh) { u_char type; int r; if ((r = ssh_packet_read_seqnr(ssh, &type, NULL)) != 0) fatal_fr(r, "read"); return type; } /* * Waits until a packet has been received, verifies that its type matches * that given, and gives a fatal error and exits if there is a mismatch. */ int ssh_packet_read_expect(struct ssh *ssh, u_int expected_type) { int r; u_char type; if ((r = ssh_packet_read_seqnr(ssh, &type, NULL)) != 0) return r; if (type != expected_type) { if ((r = sshpkt_disconnect(ssh, "Protocol error: expected packet type %d, got %d", expected_type, type)) != 0) return r; return SSH_ERR_PROTOCOL_ERROR; } return 0; } static int ssh_packet_read_poll2_mux(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p) { struct session_state *state = ssh->state; const u_char *cp; size_t need; int r; if (ssh->kex) return SSH_ERR_INTERNAL_ERROR; *typep = SSH_MSG_NONE; cp = sshbuf_ptr(state->input); if (state->packlen == 0) { if (sshbuf_len(state->input) < 4 + 1) return 0; /* packet is incomplete */ state->packlen = PEEK_U32(cp); if (state->packlen < 4 + 1 || state->packlen > PACKET_MAX_SIZE) return SSH_ERR_MESSAGE_INCOMPLETE; } need = state->packlen + 4; if (sshbuf_len(state->input) < need) return 0; /* packet is incomplete */ sshbuf_reset(state->incoming_packet); if ((r = sshbuf_put(state->incoming_packet, cp + 4, state->packlen)) != 0 || (r = sshbuf_consume(state->input, need)) != 0 || (r = sshbuf_get_u8(state->incoming_packet, NULL)) != 0 || (r = sshbuf_get_u8(state->incoming_packet, typep)) != 0) return r; if (ssh_packet_log_type(*typep)) debug3_f("type %u", *typep); /* sshbuf_dump(state->incoming_packet, stderr); */ /* reset for next packet */ state->packlen = 0; return r; } int ssh_packet_read_poll2(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p) { struct session_state *state = ssh->state; u_int padlen, need; u_char *cp; u_int maclen, aadlen = 0, authlen = 0, block_size; struct sshenc *enc = NULL; struct sshmac *mac = NULL; struct sshcomp *comp = NULL; int r; if (state->mux) return ssh_packet_read_poll2_mux(ssh, typep, seqnr_p); *typep = SSH_MSG_NONE; if (state->packet_discard) return 0; if (state->newkeys[MODE_IN] != NULL) { enc = &state->newkeys[MODE_IN]->enc; mac = &state->newkeys[MODE_IN]->mac; comp = &state->newkeys[MODE_IN]->comp; /* disable mac for authenticated encryption */ if ((authlen = cipher_authlen(enc->cipher)) != 0) mac = NULL; } maclen = mac && mac->enabled ? mac->mac_len : 0; block_size = enc ? enc->block_size : 8; aadlen = (mac && mac->enabled && mac->etm) || authlen ? 4 : 0; if (aadlen && state->packlen == 0) { if (cipher_get_length(state->receive_context, &state->packlen, state->p_read.seqnr, sshbuf_ptr(state->input), sshbuf_len(state->input)) != 0) return 0; if (state->packlen < 1 + 4 || state->packlen > PACKET_MAX_SIZE) { #ifdef PACKET_DEBUG sshbuf_dump(state->input, stderr); #endif logit("Bad packet length %u.", state->packlen); if ((r = sshpkt_disconnect(ssh, "Packet corrupt")) != 0) return r; return SSH_ERR_CONN_CORRUPT; } sshbuf_reset(state->incoming_packet); } else if (state->packlen == 0) { /* * check if input size is less than the cipher block size, * decrypt first block and extract length of incoming packet */ if (sshbuf_len(state->input) < block_size) return 0; sshbuf_reset(state->incoming_packet); if ((r = sshbuf_reserve(state->incoming_packet, block_size, &cp)) != 0) goto out; if ((r = cipher_crypt(state->receive_context, state->p_send.seqnr, cp, sshbuf_ptr(state->input), block_size, 0, 0)) != 0) goto out; state->packlen = PEEK_U32(sshbuf_ptr(state->incoming_packet)); if (state->packlen < 1 + 4 || state->packlen > PACKET_MAX_SIZE) { #ifdef PACKET_DEBUG fprintf(stderr, "input: \n"); sshbuf_dump(state->input, stderr); fprintf(stderr, "incoming_packet: \n"); sshbuf_dump(state->incoming_packet, stderr); #endif logit("Bad packet length %u.", state->packlen); return ssh_packet_start_discard(ssh, enc, mac, 0, PACKET_MAX_SIZE); } if ((r = sshbuf_consume(state->input, block_size)) != 0) goto out; } DBG(debug("input: packet len %u", state->packlen+4)); if (aadlen) { /* only the payload is encrypted */ need = state->packlen; } else { /* * the payload size and the payload are encrypted, but we * have a partial packet of block_size bytes */ need = 4 + state->packlen - block_size; } DBG(debug("partial packet: block %d, need %d, maclen %d, authlen %d," " aadlen %d", block_size, need, maclen, authlen, aadlen)); if (need % block_size != 0) { logit("padding error: need %d block %d mod %d", need, block_size, need % block_size); return ssh_packet_start_discard(ssh, enc, mac, 0, PACKET_MAX_SIZE - block_size); } /* * check if the entire packet has been received and * decrypt into incoming_packet: * 'aadlen' bytes are unencrypted, but authenticated. * 'need' bytes are encrypted, followed by either * 'authlen' bytes of authentication tag or * 'maclen' bytes of message authentication code. */ if (sshbuf_len(state->input) < aadlen + need + authlen + maclen) return 0; /* packet is incomplete */ #ifdef PACKET_DEBUG fprintf(stderr, "read_poll enc/full: "); sshbuf_dump(state->input, stderr); #endif /* EtM: check mac over encrypted input */ if (mac && mac->enabled && mac->etm) { if ((r = mac_check(mac, state->p_read.seqnr, sshbuf_ptr(state->input), aadlen + need, sshbuf_ptr(state->input) + aadlen + need + authlen, maclen)) != 0) { if (r == SSH_ERR_MAC_INVALID) logit("Corrupted MAC on input."); goto out; } } if ((r = sshbuf_reserve(state->incoming_packet, aadlen + need, &cp)) != 0) goto out; if ((r = cipher_crypt(state->receive_context, state->p_read.seqnr, cp, sshbuf_ptr(state->input), need, aadlen, authlen)) != 0) goto out; if ((r = sshbuf_consume(state->input, aadlen + need + authlen)) != 0) goto out; if (mac && mac->enabled) { /* Not EtM: check MAC over cleartext */ if (!mac->etm && (r = mac_check(mac, state->p_read.seqnr, sshbuf_ptr(state->incoming_packet), sshbuf_len(state->incoming_packet), sshbuf_ptr(state->input), maclen)) != 0) { if (r != SSH_ERR_MAC_INVALID) goto out; logit("Corrupted MAC on input."); if (need + block_size > PACKET_MAX_SIZE) return SSH_ERR_INTERNAL_ERROR; return ssh_packet_start_discard(ssh, enc, mac, sshbuf_len(state->incoming_packet), PACKET_MAX_SIZE - need - block_size); } /* Remove MAC from input buffer */ DBG(debug("MAC #%d ok", state->p_read.seqnr)); if ((r = sshbuf_consume(state->input, mac->mac_len)) != 0) goto out; } if (seqnr_p != NULL) *seqnr_p = state->p_read.seqnr; if (++state->p_read.seqnr == 0) logit("incoming seqnr wraps around"); if (++state->p_read.packets == 0) if (!(ssh->compat & SSH_BUG_NOREKEY)) return SSH_ERR_NEED_REKEY; state->p_read.blocks += (state->packlen + 4) / block_size; state->p_read.bytes += state->packlen + 4; /* get padlen */ padlen = sshbuf_ptr(state->incoming_packet)[4]; DBG(debug("input: padlen %d", padlen)); if (padlen < 4) { if ((r = sshpkt_disconnect(ssh, "Corrupted padlen %d on input.", padlen)) != 0 || (r = ssh_packet_write_wait(ssh)) != 0) return r; return SSH_ERR_CONN_CORRUPT; } /* skip packet size + padlen, discard padding */ if ((r = sshbuf_consume(state->incoming_packet, 4 + 1)) != 0 || ((r = sshbuf_consume_end(state->incoming_packet, padlen)) != 0)) goto out; DBG(debug("input: len before de-compress %zd", sshbuf_len(state->incoming_packet))); if (comp && comp->enabled) { sshbuf_reset(state->compression_buffer); if ((r = uncompress_buffer(ssh, state->incoming_packet, state->compression_buffer)) != 0) goto out; sshbuf_reset(state->incoming_packet); if ((r = sshbuf_putb(state->incoming_packet, state->compression_buffer)) != 0) goto out; DBG(debug("input: len after de-compress %zd", sshbuf_len(state->incoming_packet))); } /* * get packet type, implies consume. * return length of payload (without type field) */ if ((r = sshbuf_get_u8(state->incoming_packet, typep)) != 0) goto out; if (ssh_packet_log_type(*typep)) debug3("receive packet: type %u", *typep); - if (*typep < SSH2_MSG_MIN || *typep >= SSH2_MSG_LOCAL_MIN) { + if (*typep < SSH2_MSG_MIN) { if ((r = sshpkt_disconnect(ssh, "Invalid ssh2 packet type: %d", *typep)) != 0 || (r = ssh_packet_write_wait(ssh)) != 0) return r; return SSH_ERR_PROTOCOL_ERROR; } if (state->hook_in != NULL && (r = state->hook_in(ssh, state->incoming_packet, typep, state->hook_in_ctx)) != 0) return r; if (*typep == SSH2_MSG_USERAUTH_SUCCESS && !state->server_side) r = ssh_packet_enable_delayed_compress(ssh); else r = 0; #ifdef PACKET_DEBUG fprintf(stderr, "read/plain[%d]:\r\n", *typep); sshbuf_dump(state->incoming_packet, stderr); #endif /* reset for next packet */ state->packlen = 0; if ((r = ssh_packet_check_rekey(ssh)) != 0) return r; out: return r; } int ssh_packet_read_poll_seqnr(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p) { struct session_state *state = ssh->state; u_int reason, seqnr; int r; u_char *msg; + const u_char *d; + size_t len; for (;;) { msg = NULL; r = ssh_packet_read_poll2(ssh, typep, seqnr_p); if (r != 0) return r; if (*typep) { state->keep_alive_timeouts = 0; DBG(debug("received packet type %d", *typep)); } switch (*typep) { case SSH2_MSG_IGNORE: debug3("Received SSH2_MSG_IGNORE"); break; case SSH2_MSG_DEBUG: if ((r = sshpkt_get_u8(ssh, NULL)) != 0 || (r = sshpkt_get_string(ssh, &msg, NULL)) != 0 || (r = sshpkt_get_string(ssh, NULL, NULL)) != 0) { free(msg); return r; } debug("Remote: %.900s", msg); free(msg); break; case SSH2_MSG_DISCONNECT: if ((r = sshpkt_get_u32(ssh, &reason)) != 0 || (r = sshpkt_get_string(ssh, &msg, NULL)) != 0) return r; /* Ignore normal client exit notifications */ do_log2(ssh->state->server_side && reason == SSH2_DISCONNECT_BY_APPLICATION ? SYSLOG_LEVEL_INFO : SYSLOG_LEVEL_ERROR, "Received disconnect from %s port %d:" "%u: %.400s", ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), reason, msg); free(msg); return SSH_ERR_DISCONNECTED; case SSH2_MSG_UNIMPLEMENTED: if ((r = sshpkt_get_u32(ssh, &seqnr)) != 0) return r; debug("Received SSH2_MSG_UNIMPLEMENTED for %u", seqnr); break; + case SSH2_MSG_PING: + if ((r = sshpkt_get_string_direct(ssh, &d, &len)) != 0) + return r; + DBG(debug("Received SSH2_MSG_PING len %zu", len)); + if ((r = sshpkt_start(ssh, SSH2_MSG_PONG)) != 0 || + (r = sshpkt_put_string(ssh, d, len)) != 0 || + (r = sshpkt_send(ssh)) != 0) + return r; + break; + case SSH2_MSG_PONG: + if ((r = sshpkt_get_string_direct(ssh, + NULL, &len)) != 0) + return r; + DBG(debug("Received SSH2_MSG_PONG len %zu", len)); + break; default: return 0; } } } /* * Buffers the supplied input data. This is intended to be used together * with packet_read_poll(). */ int ssh_packet_process_incoming(struct ssh *ssh, const char *buf, u_int len) { struct session_state *state = ssh->state; int r; if (state->packet_discard) { state->keep_alive_timeouts = 0; /* ?? */ if (len >= state->packet_discard) { if ((r = ssh_packet_stop_discard(ssh)) != 0) return r; } state->packet_discard -= len; return 0; } if ((r = sshbuf_put(state->input, buf, len)) != 0) return r; return 0; } /* Reads and buffers data from the specified fd */ int ssh_packet_process_read(struct ssh *ssh, int fd) { struct session_state *state = ssh->state; int r; size_t rlen; if ((r = sshbuf_read(fd, state->input, PACKET_MAX_SIZE, &rlen)) != 0) return r; if (state->packet_discard) { if ((r = sshbuf_consume_end(state->input, rlen)) != 0) return r; state->keep_alive_timeouts = 0; /* ?? */ if (rlen >= state->packet_discard) { if ((r = ssh_packet_stop_discard(ssh)) != 0) return r; } state->packet_discard -= rlen; return 0; } return 0; } int ssh_packet_remaining(struct ssh *ssh) { return sshbuf_len(ssh->state->incoming_packet); } /* * Sends a diagnostic message from the server to the client. This message * can be sent at any time (but not while constructing another message). The * message is printed immediately, but only if the client is being executed * in verbose mode. These messages are primarily intended to ease debugging * authentication problems. The length of the formatted message must not * exceed 1024 bytes. This will automatically call ssh_packet_write_wait. */ void ssh_packet_send_debug(struct ssh *ssh, const char *fmt,...) { char buf[1024]; va_list args; int r; if ((ssh->compat & SSH_BUG_DEBUG)) return; va_start(args, fmt); vsnprintf(buf, sizeof(buf), fmt, args); va_end(args); debug3("sending debug message: %s", buf); if ((r = sshpkt_start(ssh, SSH2_MSG_DEBUG)) != 0 || (r = sshpkt_put_u8(ssh, 0)) != 0 || /* always display */ (r = sshpkt_put_cstring(ssh, buf)) != 0 || (r = sshpkt_put_cstring(ssh, "")) != 0 || (r = sshpkt_send(ssh)) != 0 || (r = ssh_packet_write_wait(ssh)) != 0) fatal_fr(r, "send DEBUG"); } void sshpkt_fmt_connection_id(struct ssh *ssh, char *s, size_t l) { snprintf(s, l, "%.200s%s%s port %d", ssh->log_preamble ? ssh->log_preamble : "", ssh->log_preamble ? " " : "", ssh_remote_ipaddr(ssh), ssh_remote_port(ssh)); } /* * Pretty-print connection-terminating errors and exit. */ static void sshpkt_vfatal(struct ssh *ssh, int r, const char *fmt, va_list ap) { char *tag = NULL, remote_id[512]; int oerrno = errno; sshpkt_fmt_connection_id(ssh, remote_id, sizeof(remote_id)); switch (r) { case SSH_ERR_CONN_CLOSED: ssh_packet_clear_keys(ssh); logdie("Connection closed by %s", remote_id); case SSH_ERR_CONN_TIMEOUT: ssh_packet_clear_keys(ssh); logdie("Connection %s %s timed out", ssh->state->server_side ? "from" : "to", remote_id); case SSH_ERR_DISCONNECTED: ssh_packet_clear_keys(ssh); logdie("Disconnected from %s", remote_id); case SSH_ERR_SYSTEM_ERROR: if (errno == ECONNRESET) { ssh_packet_clear_keys(ssh); logdie("Connection reset by %s", remote_id); } /* FALLTHROUGH */ case SSH_ERR_NO_CIPHER_ALG_MATCH: case SSH_ERR_NO_MAC_ALG_MATCH: case SSH_ERR_NO_COMPRESS_ALG_MATCH: case SSH_ERR_NO_KEX_ALG_MATCH: case SSH_ERR_NO_HOSTKEY_ALG_MATCH: if (ssh->kex && ssh->kex->failed_choice) { ssh_packet_clear_keys(ssh); errno = oerrno; logdie("Unable to negotiate with %s: %s. " "Their offer: %s", remote_id, ssh_err(r), ssh->kex->failed_choice); } /* FALLTHROUGH */ default: if (vasprintf(&tag, fmt, ap) == -1) { ssh_packet_clear_keys(ssh); logdie_f("could not allocate failure message"); } ssh_packet_clear_keys(ssh); errno = oerrno; logdie_r(r, "%s%sConnection %s %s", tag != NULL ? tag : "", tag != NULL ? ": " : "", ssh->state->server_side ? "from" : "to", remote_id); } } void sshpkt_fatal(struct ssh *ssh, int r, const char *fmt, ...) { va_list ap; va_start(ap, fmt); sshpkt_vfatal(ssh, r, fmt, ap); /* NOTREACHED */ va_end(ap); logdie_f("should have exited"); } /* * Logs the error plus constructs and sends a disconnect packet, closes the * connection, and exits. This function never returns. The error message * should not contain a newline. The length of the formatted message must * not exceed 1024 bytes. */ void ssh_packet_disconnect(struct ssh *ssh, const char *fmt,...) { char buf[1024], remote_id[512]; va_list args; static int disconnecting = 0; int r; if (disconnecting) /* Guard against recursive invocations. */ fatal("packet_disconnect called recursively."); disconnecting = 1; /* * Format the message. Note that the caller must make sure the * message is of limited size. */ sshpkt_fmt_connection_id(ssh, remote_id, sizeof(remote_id)); va_start(args, fmt); vsnprintf(buf, sizeof(buf), fmt, args); va_end(args); /* Display the error locally */ logit("Disconnecting %s: %.100s", remote_id, buf); /* * Send the disconnect message to the other side, and wait * for it to get sent. */ if ((r = sshpkt_disconnect(ssh, "%s", buf)) != 0) sshpkt_fatal(ssh, r, "%s", __func__); if ((r = ssh_packet_write_wait(ssh)) != 0) sshpkt_fatal(ssh, r, "%s", __func__); /* Close the connection. */ ssh_packet_close(ssh); cleanup_exit(255); } /* * Checks if there is any buffered output, and tries to write some of * the output. */ int ssh_packet_write_poll(struct ssh *ssh) { struct session_state *state = ssh->state; int len = sshbuf_len(state->output); int r; if (len > 0) { len = write(state->connection_out, sshbuf_ptr(state->output), len); if (len == -1) { if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) return 0; return SSH_ERR_SYSTEM_ERROR; } if (len == 0) return SSH_ERR_CONN_CLOSED; if ((r = sshbuf_consume(state->output, len)) != 0) return r; } return 0; } /* * Calls packet_write_poll repeatedly until all pending output data has been * written. */ int ssh_packet_write_wait(struct ssh *ssh) { int ret, r, ms_remain = 0; struct timeval start; struct timespec timespec, *timespecp = NULL; struct session_state *state = ssh->state; struct pollfd pfd; if ((r = ssh_packet_write_poll(ssh)) != 0) return r; while (ssh_packet_have_data_to_write(ssh)) { pfd.fd = state->connection_out; pfd.events = POLLOUT; if (state->packet_timeout_ms > 0) { ms_remain = state->packet_timeout_ms; timespecp = ×pec; } for (;;) { if (state->packet_timeout_ms > 0) { ms_to_timespec(×pec, ms_remain); monotime_tv(&start); } if ((ret = ppoll(&pfd, 1, timespecp, NULL)) >= 0) break; if (errno != EAGAIN && errno != EINTR && errno != EWOULDBLOCK) break; if (state->packet_timeout_ms <= 0) continue; ms_subtract_diff(&start, &ms_remain); if (ms_remain <= 0) { ret = 0; break; } } if (ret == 0) return SSH_ERR_CONN_TIMEOUT; if ((r = ssh_packet_write_poll(ssh)) != 0) return r; } return 0; } /* Returns true if there is buffered data to write to the connection. */ int ssh_packet_have_data_to_write(struct ssh *ssh) { return sshbuf_len(ssh->state->output) != 0; } /* Returns true if there is not too much data to write to the connection. */ int ssh_packet_not_very_much_data_to_write(struct ssh *ssh) { if (ssh->state->interactive_mode) return sshbuf_len(ssh->state->output) < 16384; else return sshbuf_len(ssh->state->output) < 128 * 1024; } +/* + * returns true when there are at most a few keystrokes of data to write + * and the connection is in interactive mode. + */ + +int +ssh_packet_interactive_data_to_write(struct ssh *ssh) +{ + return ssh->state->interactive_mode && + sshbuf_len(ssh->state->output) < 256; +} + void ssh_packet_set_tos(struct ssh *ssh, int tos) { if (!ssh_packet_connection_is_on_socket(ssh) || tos == INT_MAX) return; set_sock_tos(ssh->state->connection_in, tos); } /* Informs that the current session is interactive. Sets IP flags for that. */ void ssh_packet_set_interactive(struct ssh *ssh, int interactive, int qos_interactive, int qos_bulk) { struct session_state *state = ssh->state; if (state->set_interactive_called) return; state->set_interactive_called = 1; /* Record that we are in interactive mode. */ state->interactive_mode = interactive; /* Only set socket options if using a socket. */ if (!ssh_packet_connection_is_on_socket(ssh)) return; set_nodelay(state->connection_in); ssh_packet_set_tos(ssh, interactive ? qos_interactive : qos_bulk); } /* Returns true if the current connection is interactive. */ int ssh_packet_is_interactive(struct ssh *ssh) { return ssh->state->interactive_mode; } int ssh_packet_set_maxsize(struct ssh *ssh, u_int s) { struct session_state *state = ssh->state; if (state->set_maxsize_called) { logit_f("called twice: old %d new %d", state->max_packet_size, s); return -1; } if (s < 4 * 1024 || s > 1024 * 1024) { logit_f("bad size %d", s); return -1; } state->set_maxsize_called = 1; debug_f("setting to %d", s); state->max_packet_size = s; return s; } int ssh_packet_inc_alive_timeouts(struct ssh *ssh) { return ++ssh->state->keep_alive_timeouts; } void ssh_packet_set_alive_timeouts(struct ssh *ssh, int ka) { ssh->state->keep_alive_timeouts = ka; } u_int ssh_packet_get_maxsize(struct ssh *ssh) { return ssh->state->max_packet_size; } void ssh_packet_set_rekey_limits(struct ssh *ssh, u_int64_t bytes, u_int32_t seconds) { debug3("rekey after %llu bytes, %u seconds", (unsigned long long)bytes, (unsigned int)seconds); ssh->state->rekey_limit = bytes; ssh->state->rekey_interval = seconds; } time_t ssh_packet_get_rekey_timeout(struct ssh *ssh) { time_t seconds; seconds = ssh->state->rekey_time + ssh->state->rekey_interval - monotime(); return (seconds <= 0 ? 1 : seconds); } void ssh_packet_set_server(struct ssh *ssh) { ssh->state->server_side = 1; ssh->kex->server = 1; /* XXX unify? */ } void ssh_packet_set_authenticated(struct ssh *ssh) { ssh->state->after_authentication = 1; } void * ssh_packet_get_input(struct ssh *ssh) { return (void *)ssh->state->input; } void * ssh_packet_get_output(struct ssh *ssh) { return (void *)ssh->state->output; } /* Reset after_authentication and reset compression in post-auth privsep */ static int ssh_packet_set_postauth(struct ssh *ssh) { int r; debug_f("called"); /* This was set in net child, but is not visible in user child */ ssh->state->after_authentication = 1; ssh->state->rekeying = 0; if ((r = ssh_packet_enable_delayed_compress(ssh)) != 0) return r; return 0; } /* Packet state (de-)serialization for privsep */ /* turn kex into a blob for packet state serialization */ static int kex_to_blob(struct sshbuf *m, struct kex *kex) { int r; if ((r = sshbuf_put_u32(m, kex->we_need)) != 0 || (r = sshbuf_put_cstring(m, kex->hostkey_alg)) != 0 || (r = sshbuf_put_u32(m, kex->hostkey_type)) != 0 || (r = sshbuf_put_u32(m, kex->hostkey_nid)) != 0 || (r = sshbuf_put_u32(m, kex->kex_type)) != 0 || (r = sshbuf_put_stringb(m, kex->my)) != 0 || (r = sshbuf_put_stringb(m, kex->peer)) != 0 || (r = sshbuf_put_stringb(m, kex->client_version)) != 0 || (r = sshbuf_put_stringb(m, kex->server_version)) != 0 || (r = sshbuf_put_stringb(m, kex->session_id)) != 0 || (r = sshbuf_put_u32(m, kex->flags)) != 0) return r; return 0; } /* turn key exchange results into a blob for packet state serialization */ static int newkeys_to_blob(struct sshbuf *m, struct ssh *ssh, int mode) { struct sshbuf *b; struct sshcipher_ctx *cc; struct sshcomp *comp; struct sshenc *enc; struct sshmac *mac; struct newkeys *newkey; int r; if ((newkey = ssh->state->newkeys[mode]) == NULL) return SSH_ERR_INTERNAL_ERROR; enc = &newkey->enc; mac = &newkey->mac; comp = &newkey->comp; cc = (mode == MODE_OUT) ? ssh->state->send_context : ssh->state->receive_context; if ((r = cipher_get_keyiv(cc, enc->iv, enc->iv_len)) != 0) return r; if ((b = sshbuf_new()) == NULL) return SSH_ERR_ALLOC_FAIL; if ((r = sshbuf_put_cstring(b, enc->name)) != 0 || (r = sshbuf_put_u32(b, enc->enabled)) != 0 || (r = sshbuf_put_u32(b, enc->block_size)) != 0 || (r = sshbuf_put_string(b, enc->key, enc->key_len)) != 0 || (r = sshbuf_put_string(b, enc->iv, enc->iv_len)) != 0) goto out; if (cipher_authlen(enc->cipher) == 0) { if ((r = sshbuf_put_cstring(b, mac->name)) != 0 || (r = sshbuf_put_u32(b, mac->enabled)) != 0 || (r = sshbuf_put_string(b, mac->key, mac->key_len)) != 0) goto out; } if ((r = sshbuf_put_u32(b, comp->type)) != 0 || (r = sshbuf_put_cstring(b, comp->name)) != 0) goto out; r = sshbuf_put_stringb(m, b); out: sshbuf_free(b); return r; } /* serialize packet state into a blob */ int ssh_packet_get_state(struct ssh *ssh, struct sshbuf *m) { struct session_state *state = ssh->state; int r; if ((r = kex_to_blob(m, ssh->kex)) != 0 || (r = newkeys_to_blob(m, ssh, MODE_OUT)) != 0 || (r = newkeys_to_blob(m, ssh, MODE_IN)) != 0 || (r = sshbuf_put_u64(m, state->rekey_limit)) != 0 || (r = sshbuf_put_u32(m, state->rekey_interval)) != 0 || (r = sshbuf_put_u32(m, state->p_send.seqnr)) != 0 || (r = sshbuf_put_u64(m, state->p_send.blocks)) != 0 || (r = sshbuf_put_u32(m, state->p_send.packets)) != 0 || (r = sshbuf_put_u64(m, state->p_send.bytes)) != 0 || (r = sshbuf_put_u32(m, state->p_read.seqnr)) != 0 || (r = sshbuf_put_u64(m, state->p_read.blocks)) != 0 || (r = sshbuf_put_u32(m, state->p_read.packets)) != 0 || (r = sshbuf_put_u64(m, state->p_read.bytes)) != 0 || (r = sshbuf_put_stringb(m, state->input)) != 0 || (r = sshbuf_put_stringb(m, state->output)) != 0) return r; return 0; } /* restore key exchange results from blob for packet state de-serialization */ static int newkeys_from_blob(struct sshbuf *m, struct ssh *ssh, int mode) { struct sshbuf *b = NULL; struct sshcomp *comp; struct sshenc *enc; struct sshmac *mac; struct newkeys *newkey = NULL; size_t keylen, ivlen, maclen; int r; if ((newkey = calloc(1, sizeof(*newkey))) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } if ((r = sshbuf_froms(m, &b)) != 0) goto out; #ifdef DEBUG_PK sshbuf_dump(b, stderr); #endif enc = &newkey->enc; mac = &newkey->mac; comp = &newkey->comp; if ((r = sshbuf_get_cstring(b, &enc->name, NULL)) != 0 || (r = sshbuf_get_u32(b, (u_int *)&enc->enabled)) != 0 || (r = sshbuf_get_u32(b, &enc->block_size)) != 0 || (r = sshbuf_get_string(b, &enc->key, &keylen)) != 0 || (r = sshbuf_get_string(b, &enc->iv, &ivlen)) != 0) goto out; if ((enc->cipher = cipher_by_name(enc->name)) == NULL) { r = SSH_ERR_INVALID_FORMAT; goto out; } if (cipher_authlen(enc->cipher) == 0) { if ((r = sshbuf_get_cstring(b, &mac->name, NULL)) != 0) goto out; if ((r = mac_setup(mac, mac->name)) != 0) goto out; if ((r = sshbuf_get_u32(b, (u_int *)&mac->enabled)) != 0 || (r = sshbuf_get_string(b, &mac->key, &maclen)) != 0) goto out; if (maclen > mac->key_len) { r = SSH_ERR_INVALID_FORMAT; goto out; } mac->key_len = maclen; } if ((r = sshbuf_get_u32(b, &comp->type)) != 0 || (r = sshbuf_get_cstring(b, &comp->name, NULL)) != 0) goto out; if (sshbuf_len(b) != 0) { r = SSH_ERR_INVALID_FORMAT; goto out; } enc->key_len = keylen; enc->iv_len = ivlen; ssh->kex->newkeys[mode] = newkey; newkey = NULL; r = 0; out: free(newkey); sshbuf_free(b); return r; } /* restore kex from blob for packet state de-serialization */ static int kex_from_blob(struct sshbuf *m, struct kex **kexp) { struct kex *kex; int r; if ((kex = kex_new()) == NULL) return SSH_ERR_ALLOC_FAIL; if ((r = sshbuf_get_u32(m, &kex->we_need)) != 0 || (r = sshbuf_get_cstring(m, &kex->hostkey_alg, NULL)) != 0 || (r = sshbuf_get_u32(m, (u_int *)&kex->hostkey_type)) != 0 || (r = sshbuf_get_u32(m, (u_int *)&kex->hostkey_nid)) != 0 || (r = sshbuf_get_u32(m, &kex->kex_type)) != 0 || (r = sshbuf_get_stringb(m, kex->my)) != 0 || (r = sshbuf_get_stringb(m, kex->peer)) != 0 || (r = sshbuf_get_stringb(m, kex->client_version)) != 0 || (r = sshbuf_get_stringb(m, kex->server_version)) != 0 || (r = sshbuf_get_stringb(m, kex->session_id)) != 0 || (r = sshbuf_get_u32(m, &kex->flags)) != 0) goto out; kex->server = 1; kex->done = 1; r = 0; out: if (r != 0 || kexp == NULL) { kex_free(kex); if (kexp != NULL) *kexp = NULL; } else { kex_free(*kexp); *kexp = kex; } return r; } /* * Restore packet state from content of blob 'm' (de-serialization). * Note that 'm' will be partially consumed on parsing or any other errors. */ int ssh_packet_set_state(struct ssh *ssh, struct sshbuf *m) { struct session_state *state = ssh->state; const u_char *input, *output; size_t ilen, olen; int r; if ((r = kex_from_blob(m, &ssh->kex)) != 0 || (r = newkeys_from_blob(m, ssh, MODE_OUT)) != 0 || (r = newkeys_from_blob(m, ssh, MODE_IN)) != 0 || (r = sshbuf_get_u64(m, &state->rekey_limit)) != 0 || (r = sshbuf_get_u32(m, &state->rekey_interval)) != 0 || (r = sshbuf_get_u32(m, &state->p_send.seqnr)) != 0 || (r = sshbuf_get_u64(m, &state->p_send.blocks)) != 0 || (r = sshbuf_get_u32(m, &state->p_send.packets)) != 0 || (r = sshbuf_get_u64(m, &state->p_send.bytes)) != 0 || (r = sshbuf_get_u32(m, &state->p_read.seqnr)) != 0 || (r = sshbuf_get_u64(m, &state->p_read.blocks)) != 0 || (r = sshbuf_get_u32(m, &state->p_read.packets)) != 0 || (r = sshbuf_get_u64(m, &state->p_read.bytes)) != 0) return r; /* * We set the time here so that in post-auth privsep child we * count from the completion of the authentication. */ state->rekey_time = monotime(); /* XXX ssh_set_newkeys overrides p_read.packets? XXX */ if ((r = ssh_set_newkeys(ssh, MODE_IN)) != 0 || (r = ssh_set_newkeys(ssh, MODE_OUT)) != 0) return r; if ((r = ssh_packet_set_postauth(ssh)) != 0) return r; sshbuf_reset(state->input); sshbuf_reset(state->output); if ((r = sshbuf_get_string_direct(m, &input, &ilen)) != 0 || (r = sshbuf_get_string_direct(m, &output, &olen)) != 0 || (r = sshbuf_put(state->input, input, ilen)) != 0 || (r = sshbuf_put(state->output, output, olen)) != 0) return r; if (sshbuf_len(m)) return SSH_ERR_INVALID_FORMAT; debug3_f("done"); return 0; } /* NEW API */ /* put data to the outgoing packet */ int sshpkt_put(struct ssh *ssh, const void *v, size_t len) { return sshbuf_put(ssh->state->outgoing_packet, v, len); } int sshpkt_putb(struct ssh *ssh, const struct sshbuf *b) { return sshbuf_putb(ssh->state->outgoing_packet, b); } int sshpkt_put_u8(struct ssh *ssh, u_char val) { return sshbuf_put_u8(ssh->state->outgoing_packet, val); } int sshpkt_put_u32(struct ssh *ssh, u_int32_t val) { return sshbuf_put_u32(ssh->state->outgoing_packet, val); } int sshpkt_put_u64(struct ssh *ssh, u_int64_t val) { return sshbuf_put_u64(ssh->state->outgoing_packet, val); } int sshpkt_put_string(struct ssh *ssh, const void *v, size_t len) { return sshbuf_put_string(ssh->state->outgoing_packet, v, len); } int sshpkt_put_cstring(struct ssh *ssh, const void *v) { return sshbuf_put_cstring(ssh->state->outgoing_packet, v); } int sshpkt_put_stringb(struct ssh *ssh, const struct sshbuf *v) { return sshbuf_put_stringb(ssh->state->outgoing_packet, v); } int sshpkt_getb_froms(struct ssh *ssh, struct sshbuf **valp) { return sshbuf_froms(ssh->state->incoming_packet, valp); } #ifdef WITH_OPENSSL #ifdef OPENSSL_HAS_ECC int sshpkt_put_ec(struct ssh *ssh, const EC_POINT *v, const EC_GROUP *g) { return sshbuf_put_ec(ssh->state->outgoing_packet, v, g); } #endif /* OPENSSL_HAS_ECC */ int sshpkt_put_bignum2(struct ssh *ssh, const BIGNUM *v) { return sshbuf_put_bignum2(ssh->state->outgoing_packet, v); } #endif /* WITH_OPENSSL */ /* fetch data from the incoming packet */ int sshpkt_get(struct ssh *ssh, void *valp, size_t len) { return sshbuf_get(ssh->state->incoming_packet, valp, len); } int sshpkt_get_u8(struct ssh *ssh, u_char *valp) { return sshbuf_get_u8(ssh->state->incoming_packet, valp); } int sshpkt_get_u32(struct ssh *ssh, u_int32_t *valp) { return sshbuf_get_u32(ssh->state->incoming_packet, valp); } int sshpkt_get_u64(struct ssh *ssh, u_int64_t *valp) { return sshbuf_get_u64(ssh->state->incoming_packet, valp); } int sshpkt_get_string(struct ssh *ssh, u_char **valp, size_t *lenp) { return sshbuf_get_string(ssh->state->incoming_packet, valp, lenp); } int sshpkt_get_string_direct(struct ssh *ssh, const u_char **valp, size_t *lenp) { return sshbuf_get_string_direct(ssh->state->incoming_packet, valp, lenp); } int sshpkt_peek_string_direct(struct ssh *ssh, const u_char **valp, size_t *lenp) { return sshbuf_peek_string_direct(ssh->state->incoming_packet, valp, lenp); } int sshpkt_get_cstring(struct ssh *ssh, char **valp, size_t *lenp) { return sshbuf_get_cstring(ssh->state->incoming_packet, valp, lenp); } #ifdef WITH_OPENSSL #ifdef OPENSSL_HAS_ECC int sshpkt_get_ec(struct ssh *ssh, EC_POINT *v, const EC_GROUP *g) { return sshbuf_get_ec(ssh->state->incoming_packet, v, g); } #endif /* OPENSSL_HAS_ECC */ int sshpkt_get_bignum2(struct ssh *ssh, BIGNUM **valp) { return sshbuf_get_bignum2(ssh->state->incoming_packet, valp); } #endif /* WITH_OPENSSL */ int sshpkt_get_end(struct ssh *ssh) { if (sshbuf_len(ssh->state->incoming_packet) > 0) return SSH_ERR_UNEXPECTED_TRAILING_DATA; return 0; } const u_char * sshpkt_ptr(struct ssh *ssh, size_t *lenp) { if (lenp != NULL) *lenp = sshbuf_len(ssh->state->incoming_packet); return sshbuf_ptr(ssh->state->incoming_packet); } /* start a new packet */ int sshpkt_start(struct ssh *ssh, u_char type) { u_char buf[6]; /* u32 packet length, u8 pad len, u8 type */ DBG(debug("packet_start[%d]", type)); memset(buf, 0, sizeof(buf)); buf[sizeof(buf) - 1] = type; sshbuf_reset(ssh->state->outgoing_packet); return sshbuf_put(ssh->state->outgoing_packet, buf, sizeof(buf)); } static int ssh_packet_send_mux(struct ssh *ssh) { struct session_state *state = ssh->state; u_char type, *cp; size_t len; int r; if (ssh->kex) return SSH_ERR_INTERNAL_ERROR; len = sshbuf_len(state->outgoing_packet); if (len < 6) return SSH_ERR_INTERNAL_ERROR; cp = sshbuf_mutable_ptr(state->outgoing_packet); type = cp[5]; if (ssh_packet_log_type(type)) debug3_f("type %u", type); /* drop everything, but the connection protocol */ if (type >= SSH2_MSG_CONNECTION_MIN && type <= SSH2_MSG_CONNECTION_MAX) { POKE_U32(cp, len - 4); if ((r = sshbuf_putb(state->output, state->outgoing_packet)) != 0) return r; /* sshbuf_dump(state->output, stderr); */ } sshbuf_reset(state->outgoing_packet); return 0; } /* * 9.2. Ignored Data Message * * byte SSH_MSG_IGNORE * string data * * All implementations MUST understand (and ignore) this message at any * time (after receiving the protocol version). No implementation is * required to send them. This message can be used as an additional * protection measure against advanced traffic analysis techniques. */ int sshpkt_msg_ignore(struct ssh *ssh, u_int nbytes) { u_int32_t rnd = 0; int r; u_int i; if ((r = sshpkt_start(ssh, SSH2_MSG_IGNORE)) != 0 || (r = sshpkt_put_u32(ssh, nbytes)) != 0) return r; for (i = 0; i < nbytes; i++) { if (i % 4 == 0) rnd = arc4random(); if ((r = sshpkt_put_u8(ssh, (u_char)rnd & 0xff)) != 0) return r; rnd >>= 8; } return 0; } /* send it */ int sshpkt_send(struct ssh *ssh) { if (ssh->state && ssh->state->mux) return ssh_packet_send_mux(ssh); return ssh_packet_send2(ssh); } int sshpkt_disconnect(struct ssh *ssh, const char *fmt,...) { char buf[1024]; va_list args; int r; va_start(args, fmt); vsnprintf(buf, sizeof(buf), fmt, args); va_end(args); if ((r = sshpkt_start(ssh, SSH2_MSG_DISCONNECT)) != 0 || (r = sshpkt_put_u32(ssh, SSH2_DISCONNECT_PROTOCOL_ERROR)) != 0 || (r = sshpkt_put_cstring(ssh, buf)) != 0 || (r = sshpkt_put_cstring(ssh, "")) != 0 || (r = sshpkt_send(ssh)) != 0) return r; return 0; } /* roundup current message to pad bytes */ int sshpkt_add_padding(struct ssh *ssh, u_char pad) { ssh->state->extra_pad = pad; return 0; } diff --git a/packet.h b/packet.h index 176488b1e5d2..11925a27d438 100644 --- a/packet.h +++ b/packet.h @@ -1,223 +1,224 @@ -/* $OpenBSD: packet.h,v 1.94 2022/01/22 00:49:34 djm Exp $ */ +/* $OpenBSD: packet.h,v 1.95 2023/08/28 03:31:16 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * Interface for the packet protocol functions. * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". */ #ifndef PACKET_H #define PACKET_H #include #ifdef WITH_OPENSSL # include # ifdef OPENSSL_HAS_ECC # include # else /* OPENSSL_HAS_ECC */ # define EC_KEY void # define EC_GROUP void # define EC_POINT void # endif /* OPENSSL_HAS_ECC */ #else /* WITH_OPENSSL */ # define BIGNUM void # define EC_KEY void # define EC_GROUP void # define EC_POINT void #endif /* WITH_OPENSSL */ #include #include "openbsd-compat/sys-queue.h" struct kex; struct sshkey; struct sshbuf; struct session_state; /* private session data */ #include "dispatch.h" /* typedef, DISPATCH_MAX */ struct key_entry { TAILQ_ENTRY(key_entry) next; struct sshkey *key; }; struct ssh { /* Session state */ struct session_state *state; /* Key exchange */ struct kex *kex; /* cached local and remote ip addresses and ports */ char *remote_ipaddr; int remote_port; char *local_ipaddr; int local_port; char *rdomain_in; /* Optional preamble for log messages (e.g. username) */ char *log_preamble; /* Dispatcher table */ dispatch_fn *dispatch[DISPATCH_MAX]; /* number of packets to ignore in the dispatcher */ int dispatch_skip_packets; /* datafellows */ int compat; /* Lists for private and public keys */ TAILQ_HEAD(, key_entry) private_keys; TAILQ_HEAD(, key_entry) public_keys; /* Client/Server authentication context */ void *authctxt; /* Channels context */ struct ssh_channels *chanctxt; /* APP data */ void *app_data; }; typedef int (ssh_packet_hook_fn)(struct ssh *, struct sshbuf *, u_char *, void *); struct ssh *ssh_alloc_session_state(void); struct ssh *ssh_packet_set_connection(struct ssh *, int, int); void ssh_packet_set_timeout(struct ssh *, int, int); int ssh_packet_stop_discard(struct ssh *); int ssh_packet_connection_af(struct ssh *); void ssh_packet_set_nonblocking(struct ssh *); int ssh_packet_get_connection_in(struct ssh *); int ssh_packet_get_connection_out(struct ssh *); void ssh_packet_close(struct ssh *); void ssh_packet_set_input_hook(struct ssh *, ssh_packet_hook_fn *, void *); void ssh_packet_clear_keys(struct ssh *); void ssh_clear_newkeys(struct ssh *, int); int ssh_packet_is_rekeying(struct ssh *); int ssh_packet_check_rekey(struct ssh *); void ssh_packet_set_protocol_flags(struct ssh *, u_int); u_int ssh_packet_get_protocol_flags(struct ssh *); void ssh_packet_set_tos(struct ssh *, int); void ssh_packet_set_interactive(struct ssh *, int, int, int); int ssh_packet_is_interactive(struct ssh *); void ssh_packet_set_server(struct ssh *); void ssh_packet_set_authenticated(struct ssh *); void ssh_packet_set_mux(struct ssh *); int ssh_packet_get_mux(struct ssh *); int ssh_packet_set_log_preamble(struct ssh *, const char *, ...) __attribute__((format(printf, 2, 3))); int ssh_packet_log_type(u_char); int ssh_packet_send2_wrapped(struct ssh *); int ssh_packet_send2(struct ssh *); int ssh_packet_read(struct ssh *); int ssh_packet_read_expect(struct ssh *, u_int type); int ssh_packet_read_poll(struct ssh *); int ssh_packet_read_poll2(struct ssh *, u_char *, u_int32_t *seqnr_p); int ssh_packet_process_incoming(struct ssh *, const char *buf, u_int len); int ssh_packet_process_read(struct ssh *, int); int ssh_packet_read_seqnr(struct ssh *, u_char *, u_int32_t *seqnr_p); int ssh_packet_read_poll_seqnr(struct ssh *, u_char *, u_int32_t *seqnr_p); const void *ssh_packet_get_string_ptr(struct ssh *, u_int *length_ptr); void ssh_packet_disconnect(struct ssh *, const char *fmt, ...) __attribute__((format(printf, 2, 3))) __attribute__((noreturn)); void ssh_packet_send_debug(struct ssh *, const char *fmt, ...) __attribute__((format(printf, 2, 3))); int ssh_set_newkeys(struct ssh *, int mode); void ssh_packet_get_bytes(struct ssh *, u_int64_t *, u_int64_t *); int ssh_packet_write_poll(struct ssh *); int ssh_packet_write_wait(struct ssh *); int ssh_packet_have_data_to_write(struct ssh *); int ssh_packet_not_very_much_data_to_write(struct ssh *); +int ssh_packet_interactive_data_to_write(struct ssh *); int ssh_packet_connection_is_on_socket(struct ssh *); int ssh_packet_remaining(struct ssh *); void ssh_tty_make_modes(struct ssh *, int, struct termios *); void ssh_tty_parse_modes(struct ssh *, int); void ssh_packet_set_alive_timeouts(struct ssh *, int); int ssh_packet_inc_alive_timeouts(struct ssh *); int ssh_packet_set_maxsize(struct ssh *, u_int); u_int ssh_packet_get_maxsize(struct ssh *); int ssh_packet_get_state(struct ssh *, struct sshbuf *); int ssh_packet_set_state(struct ssh *, struct sshbuf *); const char *ssh_remote_ipaddr(struct ssh *); int ssh_remote_port(struct ssh *); const char *ssh_local_ipaddr(struct ssh *); int ssh_local_port(struct ssh *); const char *ssh_packet_rdomain_in(struct ssh *); void ssh_packet_set_rekey_limits(struct ssh *, u_int64_t, u_int32_t); time_t ssh_packet_get_rekey_timeout(struct ssh *); void *ssh_packet_get_input(struct ssh *); void *ssh_packet_get_output(struct ssh *); /* new API */ int sshpkt_start(struct ssh *ssh, u_char type); int sshpkt_send(struct ssh *ssh); int sshpkt_disconnect(struct ssh *, const char *fmt, ...) __attribute__((format(printf, 2, 3))); int sshpkt_add_padding(struct ssh *, u_char); void sshpkt_fatal(struct ssh *ssh, int r, const char *fmt, ...) __attribute__((format(printf, 3, 4))) __attribute__((noreturn)); int sshpkt_msg_ignore(struct ssh *, u_int); int sshpkt_put(struct ssh *ssh, const void *v, size_t len); int sshpkt_putb(struct ssh *ssh, const struct sshbuf *b); int sshpkt_put_u8(struct ssh *ssh, u_char val); int sshpkt_put_u32(struct ssh *ssh, u_int32_t val); int sshpkt_put_u64(struct ssh *ssh, u_int64_t val); int sshpkt_put_string(struct ssh *ssh, const void *v, size_t len); int sshpkt_put_cstring(struct ssh *ssh, const void *v); int sshpkt_put_stringb(struct ssh *ssh, const struct sshbuf *v); int sshpkt_put_ec(struct ssh *ssh, const EC_POINT *v, const EC_GROUP *g); int sshpkt_put_bignum2(struct ssh *ssh, const BIGNUM *v); int sshpkt_get(struct ssh *ssh, void *valp, size_t len); int sshpkt_get_u8(struct ssh *ssh, u_char *valp); int sshpkt_get_u32(struct ssh *ssh, u_int32_t *valp); int sshpkt_get_u64(struct ssh *ssh, u_int64_t *valp); int sshpkt_get_string(struct ssh *ssh, u_char **valp, size_t *lenp); int sshpkt_get_string_direct(struct ssh *ssh, const u_char **valp, size_t *lenp); int sshpkt_peek_string_direct(struct ssh *ssh, const u_char **valp, size_t *lenp); int sshpkt_get_cstring(struct ssh *ssh, char **valp, size_t *lenp); int sshpkt_getb_froms(struct ssh *ssh, struct sshbuf **valp); int sshpkt_get_ec(struct ssh *ssh, EC_POINT *v, const EC_GROUP *g); int sshpkt_get_bignum2(struct ssh *ssh, BIGNUM **valp); int sshpkt_get_end(struct ssh *ssh); void sshpkt_fmt_connection_id(struct ssh *ssh, char *s, size_t l); const u_char *sshpkt_ptr(struct ssh *, size_t *lenp); #if !defined(WITH_OPENSSL) # undef BIGNUM # undef EC_KEY # undef EC_GROUP # undef EC_POINT #elif !defined(OPENSSL_HAS_ECC) # undef EC_KEY # undef EC_GROUP # undef EC_POINT #endif #endif /* PACKET_H */ diff --git a/readconf.c b/readconf.c index 0d50e89b129f..131c24f526d4 100644 --- a/readconf.c +++ b/readconf.c @@ -1,3577 +1,3637 @@ -/* $OpenBSD: readconf.c,v 1.380 2023/07/17 06:16:33 djm Exp $ */ +/* $OpenBSD: readconf.c,v 1.381 2023/08/28 03:31:16 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * Functions for reading the configuration files. * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". */ #include "includes.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_IFADDRS_H # include #endif #include #include #ifdef HAVE_PATHS_H # include #endif #include #include #include #include #include #include #ifdef USE_SYSTEM_GLOB # include #else # include "openbsd-compat/glob.h" #endif #ifdef HAVE_UTIL_H #include #endif #if defined(HAVE_STRNVIS) && defined(HAVE_VIS_H) && !defined(BROKEN_STRNVIS) # include #endif #include "xmalloc.h" #include "ssh.h" #include "ssherr.h" #include "cipher.h" #include "pathnames.h" #include "log.h" #include "sshkey.h" #include "misc.h" #include "readconf.h" #include "match.h" #include "kex.h" #include "mac.h" #include "uidswap.h" #include "myproposal.h" #include "digest.h" /* Format of the configuration file: # Configuration data is parsed as follows: # 1. command line options # 2. user-specific file # 3. system-wide file # Any configuration value is only changed the first time it is set. # Thus, host-specific definitions should be at the beginning of the # configuration file, and defaults at the end. # Host-specific declarations. These may override anything above. A single # host may match multiple declarations; these are processed in the order # that they are given in. Host *.ngs.fi ngs.fi User foo Host fake.com Hostname another.host.name.real.org User blaah Port 34289 ForwardX11 no ForwardAgent no Host books.com RemoteForward 9999 shadows.cs.hut.fi:9999 Ciphers 3des-cbc Host fascist.blob.com Port 23123 User tylonen PasswordAuthentication no Host puukko.hut.fi User t35124p ProxyCommand ssh-proxy %h %p Host *.fr PublicKeyAuthentication no Host *.su Ciphers aes128-ctr PasswordAuthentication no Host vpn.fake.com Tunnel yes TunnelDevice 3 # Defaults for various options Host * ForwardAgent no ForwardX11 no PasswordAuthentication yes StrictHostKeyChecking yes TcpKeepAlive no IdentityFile ~/.ssh/identity Port 22 EscapeChar ~ */ static int read_config_file_depth(const char *filename, struct passwd *pw, const char *host, const char *original_host, Options *options, int flags, int *activep, int *want_final_pass, int depth); static int process_config_line_depth(Options *options, struct passwd *pw, const char *host, const char *original_host, char *line, const char *filename, int linenum, int *activep, int flags, int *want_final_pass, int depth); /* Keyword tokens. */ typedef enum { oBadOption, oHost, oMatch, oInclude, oTag, oForwardAgent, oForwardX11, oForwardX11Trusted, oForwardX11Timeout, oGatewayPorts, oExitOnForwardFailure, oPasswordAuthentication, oXAuthLocation, oIdentityFile, oHostname, oPort, oRemoteForward, oLocalForward, oPermitRemoteOpen, oCertificateFile, oAddKeysToAgent, oIdentityAgent, oUser, oEscapeChar, oProxyCommand, oGlobalKnownHostsFile, oUserKnownHostsFile, oConnectionAttempts, oBatchMode, oCheckHostIP, oStrictHostKeyChecking, oCompression, oTCPKeepAlive, oNumberOfPasswordPrompts, oLogFacility, oLogLevel, oLogVerbose, oCiphers, oMacs, oPubkeyAuthentication, oKbdInteractiveAuthentication, oKbdInteractiveDevices, oHostKeyAlias, oDynamicForward, oPreferredAuthentications, oHostbasedAuthentication, oHostKeyAlgorithms, oBindAddress, oBindInterface, oPKCS11Provider, oClearAllForwardings, oNoHostAuthenticationForLocalhost, oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout, oAddressFamily, oGssAuthentication, oGssDelegateCreds, oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly, oSendEnv, oSetEnv, oControlPath, oControlMaster, oControlPersist, oHashKnownHosts, oTunnel, oTunnelDevice, oLocalCommand, oPermitLocalCommand, oRemoteCommand, oVisualHostKey, oKexAlgorithms, oIPQoS, oRequestTTY, oSessionType, oStdinNull, oForkAfterAuthentication, oIgnoreUnknown, oProxyUseFdpass, oCanonicalDomains, oCanonicalizeHostname, oCanonicalizeMaxDots, oCanonicalizeFallbackLocal, oCanonicalizePermittedCNAMEs, oStreamLocalBindMask, oStreamLocalBindUnlink, oRevokedHostKeys, oFingerprintHash, oUpdateHostkeys, oHostbasedAcceptedAlgorithms, oPubkeyAcceptedAlgorithms, oCASignatureAlgorithms, oProxyJump, oSecurityKeyProvider, oKnownHostsCommand, oRequiredRSASize, - oEnableEscapeCommandline, + oEnableEscapeCommandline, oObscureKeystrokeTiming, oIgnore, oIgnoredUnknownOption, oDeprecated, oUnsupported } OpCodes; /* Textual representations of the tokens. */ static struct { const char *name; OpCodes opcode; } keywords[] = { /* Deprecated options */ { "protocol", oIgnore }, /* NB. silently ignored */ { "cipher", oDeprecated }, { "fallbacktorsh", oDeprecated }, { "globalknownhostsfile2", oDeprecated }, { "rhostsauthentication", oDeprecated }, { "userknownhostsfile2", oDeprecated }, { "useroaming", oDeprecated }, { "usersh", oDeprecated }, { "useprivilegedport", oDeprecated }, /* Unsupported options */ { "afstokenpassing", oUnsupported }, { "kerberosauthentication", oUnsupported }, { "kerberostgtpassing", oUnsupported }, { "rsaauthentication", oUnsupported }, { "rhostsrsaauthentication", oUnsupported }, { "compressionlevel", oUnsupported }, /* Sometimes-unsupported options */ #if defined(GSSAPI) { "gssapiauthentication", oGssAuthentication }, { "gssapidelegatecredentials", oGssDelegateCreds }, # else { "gssapiauthentication", oUnsupported }, { "gssapidelegatecredentials", oUnsupported }, #endif #ifdef ENABLE_PKCS11 { "pkcs11provider", oPKCS11Provider }, { "smartcarddevice", oPKCS11Provider }, # else { "smartcarddevice", oUnsupported }, { "pkcs11provider", oUnsupported }, #endif { "forwardagent", oForwardAgent }, { "forwardx11", oForwardX11 }, { "forwardx11trusted", oForwardX11Trusted }, { "forwardx11timeout", oForwardX11Timeout }, { "exitonforwardfailure", oExitOnForwardFailure }, { "xauthlocation", oXAuthLocation }, { "gatewayports", oGatewayPorts }, { "passwordauthentication", oPasswordAuthentication }, { "kbdinteractiveauthentication", oKbdInteractiveAuthentication }, { "kbdinteractivedevices", oKbdInteractiveDevices }, { "challengeresponseauthentication", oKbdInteractiveAuthentication }, /* alias */ { "skeyauthentication", oKbdInteractiveAuthentication }, /* alias */ { "tisauthentication", oKbdInteractiveAuthentication }, /* alias */ { "pubkeyauthentication", oPubkeyAuthentication }, { "dsaauthentication", oPubkeyAuthentication }, /* alias */ { "hostbasedauthentication", oHostbasedAuthentication }, { "identityfile", oIdentityFile }, { "identityfile2", oIdentityFile }, /* obsolete */ { "identitiesonly", oIdentitiesOnly }, { "certificatefile", oCertificateFile }, { "addkeystoagent", oAddKeysToAgent }, { "identityagent", oIdentityAgent }, { "hostname", oHostname }, { "hostkeyalias", oHostKeyAlias }, { "proxycommand", oProxyCommand }, { "port", oPort }, { "ciphers", oCiphers }, { "macs", oMacs }, { "remoteforward", oRemoteForward }, { "localforward", oLocalForward }, { "permitremoteopen", oPermitRemoteOpen }, { "user", oUser }, { "host", oHost }, { "match", oMatch }, { "tag", oTag }, { "escapechar", oEscapeChar }, { "globalknownhostsfile", oGlobalKnownHostsFile }, { "userknownhostsfile", oUserKnownHostsFile }, { "connectionattempts", oConnectionAttempts }, { "batchmode", oBatchMode }, { "checkhostip", oCheckHostIP }, { "stricthostkeychecking", oStrictHostKeyChecking }, { "compression", oCompression }, { "tcpkeepalive", oTCPKeepAlive }, { "keepalive", oTCPKeepAlive }, /* obsolete */ { "numberofpasswordprompts", oNumberOfPasswordPrompts }, { "syslogfacility", oLogFacility }, { "loglevel", oLogLevel }, { "logverbose", oLogVerbose }, { "dynamicforward", oDynamicForward }, { "preferredauthentications", oPreferredAuthentications }, { "hostkeyalgorithms", oHostKeyAlgorithms }, { "casignaturealgorithms", oCASignatureAlgorithms }, { "bindaddress", oBindAddress }, { "bindinterface", oBindInterface }, { "clearallforwardings", oClearAllForwardings }, { "enablesshkeysign", oEnableSSHKeysign }, { "verifyhostkeydns", oVerifyHostKeyDNS }, { "nohostauthenticationforlocalhost", oNoHostAuthenticationForLocalhost }, { "rekeylimit", oRekeyLimit }, { "connecttimeout", oConnectTimeout }, { "addressfamily", oAddressFamily }, { "serveraliveinterval", oServerAliveInterval }, { "serveralivecountmax", oServerAliveCountMax }, { "sendenv", oSendEnv }, { "setenv", oSetEnv }, { "controlpath", oControlPath }, { "controlmaster", oControlMaster }, { "controlpersist", oControlPersist }, { "hashknownhosts", oHashKnownHosts }, { "include", oInclude }, { "tunnel", oTunnel }, { "tunneldevice", oTunnelDevice }, { "localcommand", oLocalCommand }, { "permitlocalcommand", oPermitLocalCommand }, { "remotecommand", oRemoteCommand }, { "visualhostkey", oVisualHostKey }, { "kexalgorithms", oKexAlgorithms }, { "ipqos", oIPQoS }, { "requesttty", oRequestTTY }, { "sessiontype", oSessionType }, { "stdinnull", oStdinNull }, { "forkafterauthentication", oForkAfterAuthentication }, { "proxyusefdpass", oProxyUseFdpass }, { "canonicaldomains", oCanonicalDomains }, { "canonicalizefallbacklocal", oCanonicalizeFallbackLocal }, { "canonicalizehostname", oCanonicalizeHostname }, { "canonicalizemaxdots", oCanonicalizeMaxDots }, { "canonicalizepermittedcnames", oCanonicalizePermittedCNAMEs }, { "streamlocalbindmask", oStreamLocalBindMask }, { "streamlocalbindunlink", oStreamLocalBindUnlink }, { "revokedhostkeys", oRevokedHostKeys }, { "fingerprinthash", oFingerprintHash }, { "updatehostkeys", oUpdateHostkeys }, { "hostbasedacceptedalgorithms", oHostbasedAcceptedAlgorithms }, { "hostbasedkeytypes", oHostbasedAcceptedAlgorithms }, /* obsolete */ { "pubkeyacceptedalgorithms", oPubkeyAcceptedAlgorithms }, { "pubkeyacceptedkeytypes", oPubkeyAcceptedAlgorithms }, /* obsolete */ { "ignoreunknown", oIgnoreUnknown }, { "proxyjump", oProxyJump }, { "securitykeyprovider", oSecurityKeyProvider }, { "knownhostscommand", oKnownHostsCommand }, { "requiredrsasize", oRequiredRSASize }, { "enableescapecommandline", oEnableEscapeCommandline }, + { "obscurekeystroketiming", oObscureKeystrokeTiming }, { NULL, oBadOption } }; static const char *lookup_opcode_name(OpCodes code); const char * kex_default_pk_alg(void) { static char *pkalgs; if (pkalgs == NULL) { char *all_key; all_key = sshkey_alg_list(0, 0, 1, ','); pkalgs = match_filter_allowlist(KEX_DEFAULT_PK_ALG, all_key); free(all_key); } return pkalgs; } char * ssh_connection_hash(const char *thishost, const char *host, const char *portstr, const char *user) { struct ssh_digest_ctx *md; u_char conn_hash[SSH_DIGEST_MAX_LENGTH]; if ((md = ssh_digest_start(SSH_DIGEST_SHA1)) == NULL || ssh_digest_update(md, thishost, strlen(thishost)) < 0 || ssh_digest_update(md, host, strlen(host)) < 0 || ssh_digest_update(md, portstr, strlen(portstr)) < 0 || ssh_digest_update(md, user, strlen(user)) < 0 || ssh_digest_final(md, conn_hash, sizeof(conn_hash)) < 0) fatal_f("mux digest failed"); ssh_digest_free(md); return tohex(conn_hash, ssh_digest_bytes(SSH_DIGEST_SHA1)); } /* * Adds a local TCP/IP port forward to options. Never returns if there is an * error. */ void add_local_forward(Options *options, const struct Forward *newfwd) { struct Forward *fwd; int i; /* Don't add duplicates */ for (i = 0; i < options->num_local_forwards; i++) { if (forward_equals(newfwd, options->local_forwards + i)) return; } options->local_forwards = xreallocarray(options->local_forwards, options->num_local_forwards + 1, sizeof(*options->local_forwards)); fwd = &options->local_forwards[options->num_local_forwards++]; fwd->listen_host = newfwd->listen_host; fwd->listen_port = newfwd->listen_port; fwd->listen_path = newfwd->listen_path; fwd->connect_host = newfwd->connect_host; fwd->connect_port = newfwd->connect_port; fwd->connect_path = newfwd->connect_path; } /* * Adds a remote TCP/IP port forward to options. Never returns if there is * an error. */ void add_remote_forward(Options *options, const struct Forward *newfwd) { struct Forward *fwd; int i; /* Don't add duplicates */ for (i = 0; i < options->num_remote_forwards; i++) { if (forward_equals(newfwd, options->remote_forwards + i)) return; } options->remote_forwards = xreallocarray(options->remote_forwards, options->num_remote_forwards + 1, sizeof(*options->remote_forwards)); fwd = &options->remote_forwards[options->num_remote_forwards++]; fwd->listen_host = newfwd->listen_host; fwd->listen_port = newfwd->listen_port; fwd->listen_path = newfwd->listen_path; fwd->connect_host = newfwd->connect_host; fwd->connect_port = newfwd->connect_port; fwd->connect_path = newfwd->connect_path; fwd->handle = newfwd->handle; fwd->allocated_port = 0; } static void clear_forwardings(Options *options) { int i; for (i = 0; i < options->num_local_forwards; i++) { free(options->local_forwards[i].listen_host); free(options->local_forwards[i].listen_path); free(options->local_forwards[i].connect_host); free(options->local_forwards[i].connect_path); } if (options->num_local_forwards > 0) { free(options->local_forwards); options->local_forwards = NULL; } options->num_local_forwards = 0; for (i = 0; i < options->num_remote_forwards; i++) { free(options->remote_forwards[i].listen_host); free(options->remote_forwards[i].listen_path); free(options->remote_forwards[i].connect_host); free(options->remote_forwards[i].connect_path); } if (options->num_remote_forwards > 0) { free(options->remote_forwards); options->remote_forwards = NULL; } options->num_remote_forwards = 0; options->tun_open = SSH_TUNMODE_NO; } void add_certificate_file(Options *options, const char *path, int userprovided) { int i; if (options->num_certificate_files >= SSH_MAX_CERTIFICATE_FILES) fatal("Too many certificate files specified (max %d)", SSH_MAX_CERTIFICATE_FILES); /* Avoid registering duplicates */ for (i = 0; i < options->num_certificate_files; i++) { if (options->certificate_file_userprovided[i] == userprovided && strcmp(options->certificate_files[i], path) == 0) { debug2_f("ignoring duplicate key %s", path); return; } } options->certificate_file_userprovided[options->num_certificate_files] = userprovided; options->certificate_files[options->num_certificate_files++] = xstrdup(path); } void add_identity_file(Options *options, const char *dir, const char *filename, int userprovided) { char *path; int i; if (options->num_identity_files >= SSH_MAX_IDENTITY_FILES) fatal("Too many identity files specified (max %d)", SSH_MAX_IDENTITY_FILES); if (dir == NULL) /* no dir, filename is absolute */ path = xstrdup(filename); else if (xasprintf(&path, "%s%s", dir, filename) >= PATH_MAX) fatal("Identity file path %s too long", path); /* Avoid registering duplicates */ for (i = 0; i < options->num_identity_files; i++) { if (options->identity_file_userprovided[i] == userprovided && strcmp(options->identity_files[i], path) == 0) { debug2_f("ignoring duplicate key %s", path); free(path); return; } } options->identity_file_userprovided[options->num_identity_files] = userprovided; options->identity_files[options->num_identity_files++] = path; } int default_ssh_port(void) { static int port; struct servent *sp; if (port == 0) { sp = getservbyname(SSH_SERVICE_NAME, "tcp"); port = sp ? ntohs(sp->s_port) : SSH_DEFAULT_PORT; } return port; } /* * Execute a command in a shell. * Return its exit status or -1 on abnormal exit. */ static int execute_in_shell(const char *cmd) { char *shell; pid_t pid; int status; if ((shell = getenv("SHELL")) == NULL) shell = _PATH_BSHELL; if (access(shell, X_OK) == -1) { fatal("Shell \"%s\" is not executable: %s", shell, strerror(errno)); } debug("Executing command: '%.500s'", cmd); /* Fork and execute the command. */ if ((pid = fork()) == 0) { char *argv[4]; if (stdfd_devnull(1, 1, 0) == -1) fatal_f("stdfd_devnull failed"); closefrom(STDERR_FILENO + 1); argv[0] = shell; argv[1] = "-c"; argv[2] = xstrdup(cmd); argv[3] = NULL; execv(argv[0], argv); error("Unable to execute '%.100s': %s", cmd, strerror(errno)); /* Die with signal to make this error apparent to parent. */ ssh_signal(SIGTERM, SIG_DFL); kill(getpid(), SIGTERM); _exit(1); } /* Parent. */ if (pid == -1) fatal_f("fork: %.100s", strerror(errno)); while (waitpid(pid, &status, 0) == -1) { if (errno != EINTR && errno != EAGAIN) fatal_f("waitpid: %s", strerror(errno)); } if (!WIFEXITED(status)) { error("command '%.100s' exited abnormally", cmd); return -1; } debug3("command returned status %d", WEXITSTATUS(status)); return WEXITSTATUS(status); } /* * Check whether a local network interface address appears in CIDR pattern- * list 'addrlist'. Returns 1 if matched or 0 otherwise. */ static int check_match_ifaddrs(const char *addrlist) { #ifdef HAVE_IFADDRS_H struct ifaddrs *ifa, *ifaddrs = NULL; int r, found = 0; char addr[NI_MAXHOST]; socklen_t salen; if (getifaddrs(&ifaddrs) != 0) { error("match localnetwork: getifaddrs failed: %s", strerror(errno)); return 0; } for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) { if (ifa->ifa_addr == NULL || ifa->ifa_name == NULL || (ifa->ifa_flags & IFF_UP) == 0) continue; switch (ifa->ifa_addr->sa_family) { case AF_INET: salen = sizeof(struct sockaddr_in); break; case AF_INET6: salen = sizeof(struct sockaddr_in6); break; #ifdef AF_LINK case AF_LINK: /* ignore */ continue; #endif /* AF_LINK */ default: debug2_f("interface %s: unsupported address family %d", ifa->ifa_name, ifa->ifa_addr->sa_family); continue; } if ((r = getnameinfo(ifa->ifa_addr, salen, addr, sizeof(addr), NULL, 0, NI_NUMERICHOST)) != 0) { debug2_f("interface %s getnameinfo failed: %s", ifa->ifa_name, gai_strerror(r)); continue; } debug3_f("interface %s addr %s", ifa->ifa_name, addr); if (addr_match_cidr_list(addr, addrlist) == 1) { debug3_f("matched interface %s: address %s in %s", ifa->ifa_name, addr, addrlist); found = 1; break; } } freeifaddrs(ifaddrs); return found; #else /* HAVE_IFADDRS_H */ error("match localnetwork: not supported on this platform"); return 0; #endif /* HAVE_IFADDRS_H */ } /* * Parse and execute a Match directive. */ static int match_cfg_line(Options *options, char **condition, struct passwd *pw, const char *host_arg, const char *original_host, int final_pass, int *want_final_pass, const char *filename, int linenum) { char *arg, *oattrib, *attrib, *cmd, *cp = *condition, *host, *criteria; const char *ruser; int r, port, this_result, result = 1, attributes = 0, negate; char thishost[NI_MAXHOST], shorthost[NI_MAXHOST], portstr[NI_MAXSERV]; char uidstr[32]; /* * Configuration is likely to be incomplete at this point so we * must be prepared to use default values. */ port = options->port <= 0 ? default_ssh_port() : options->port; ruser = options->user == NULL ? pw->pw_name : options->user; if (final_pass) { host = xstrdup(options->hostname); } else if (options->hostname != NULL) { /* NB. Please keep in sync with ssh.c:main() */ host = percent_expand(options->hostname, "h", host_arg, (char *)NULL); } else { host = xstrdup(host_arg); } debug2("checking match for '%s' host %s originally %s", cp, host, original_host); while ((oattrib = attrib = strdelim(&cp)) && *attrib != '\0') { /* Terminate on comment */ if (*attrib == '#') { cp = NULL; /* mark all arguments consumed */ break; } arg = criteria = NULL; this_result = 1; if ((negate = (attrib[0] == '!'))) attrib++; /* Criterion "all" has no argument and must appear alone */ if (strcasecmp(attrib, "all") == 0) { if (attributes > 1 || ((arg = strdelim(&cp)) != NULL && *arg != '\0' && *arg != '#')) { error("%.200s line %d: '%s' cannot be combined " "with other Match attributes", filename, linenum, oattrib); result = -1; goto out; } if (arg != NULL && *arg == '#') cp = NULL; /* mark all arguments consumed */ if (result) result = negate ? 0 : 1; goto out; } attributes++; /* criteria "final" and "canonical" have no argument */ if (strcasecmp(attrib, "canonical") == 0 || strcasecmp(attrib, "final") == 0) { /* * If the config requests "Match final" then remember * this so we can perform a second pass later. */ if (strcasecmp(attrib, "final") == 0 && want_final_pass != NULL) *want_final_pass = 1; r = !!final_pass; /* force bitmask member to boolean */ if (r == (negate ? 1 : 0)) this_result = result = 0; debug3("%.200s line %d: %smatched '%s'", filename, linenum, this_result ? "" : "not ", oattrib); continue; } /* All other criteria require an argument */ if ((arg = strdelim(&cp)) == NULL || *arg == '\0' || *arg == '#') { error("Missing Match criteria for %s", attrib); result = -1; goto out; } if (strcasecmp(attrib, "host") == 0) { criteria = xstrdup(host); r = match_hostname(host, arg) == 1; if (r == (negate ? 1 : 0)) this_result = result = 0; } else if (strcasecmp(attrib, "originalhost") == 0) { criteria = xstrdup(original_host); r = match_hostname(original_host, arg) == 1; if (r == (negate ? 1 : 0)) this_result = result = 0; } else if (strcasecmp(attrib, "user") == 0) { criteria = xstrdup(ruser); r = match_pattern_list(ruser, arg, 0) == 1; if (r == (negate ? 1 : 0)) this_result = result = 0; } else if (strcasecmp(attrib, "localuser") == 0) { criteria = xstrdup(pw->pw_name); r = match_pattern_list(pw->pw_name, arg, 0) == 1; if (r == (negate ? 1 : 0)) this_result = result = 0; } else if (strcasecmp(attrib, "localnetwork") == 0) { if (addr_match_cidr_list(NULL, arg) == -1) { /* Error already printed */ result = -1; goto out; } r = check_match_ifaddrs(arg) == 1; if (r == (negate ? 1 : 0)) this_result = result = 0; } else if (strcasecmp(attrib, "tagged") == 0) { criteria = xstrdup(options->tag == NULL ? "" : options->tag); r = match_pattern_list(criteria, arg, 0) == 1; if (r == (negate ? 1 : 0)) this_result = result = 0; } else if (strcasecmp(attrib, "exec") == 0) { char *conn_hash_hex, *keyalias; if (gethostname(thishost, sizeof(thishost)) == -1) fatal("gethostname: %s", strerror(errno)); strlcpy(shorthost, thishost, sizeof(shorthost)); shorthost[strcspn(thishost, ".")] = '\0'; snprintf(portstr, sizeof(portstr), "%d", port); snprintf(uidstr, sizeof(uidstr), "%llu", (unsigned long long)pw->pw_uid); conn_hash_hex = ssh_connection_hash(thishost, host, portstr, ruser); keyalias = options->host_key_alias ? options->host_key_alias : host; cmd = percent_expand(arg, "C", conn_hash_hex, "L", shorthost, "d", pw->pw_dir, "h", host, "k", keyalias, "l", thishost, "n", original_host, "p", portstr, "r", ruser, "u", pw->pw_name, "i", uidstr, (char *)NULL); free(conn_hash_hex); if (result != 1) { /* skip execution if prior predicate failed */ debug3("%.200s line %d: skipped exec " "\"%.100s\"", filename, linenum, cmd); free(cmd); continue; } r = execute_in_shell(cmd); if (r == -1) { fatal("%.200s line %d: match exec " "'%.100s' error", filename, linenum, cmd); } criteria = xstrdup(cmd); free(cmd); /* Force exit status to boolean */ r = r == 0; if (r == (negate ? 1 : 0)) this_result = result = 0; } else { error("Unsupported Match attribute %s", attrib); result = -1; goto out; } debug3("%.200s line %d: %smatched '%s%s%.100s%s' ", filename, linenum, this_result ? "": "not ", oattrib, criteria == NULL ? "" : " \"", criteria == NULL ? "" : criteria, criteria == NULL ? "" : "\""); free(criteria); } if (attributes == 0) { error("One or more attributes required for Match"); result = -1; goto out; } out: if (result != -1) debug2("match %sfound", result ? "" : "not "); *condition = cp; free(host); return result; } /* Remove environment variable by pattern */ static void rm_env(Options *options, const char *arg, const char *filename, int linenum) { u_int i, j, onum_send_env = options->num_send_env; /* Remove an environment variable */ for (i = 0; i < options->num_send_env; ) { if (!match_pattern(options->send_env[i], arg + 1)) { i++; continue; } debug3("%s line %d: removing environment %s", filename, linenum, options->send_env[i]); free(options->send_env[i]); options->send_env[i] = NULL; for (j = i; j < options->num_send_env - 1; j++) { options->send_env[j] = options->send_env[j + 1]; options->send_env[j + 1] = NULL; } options->num_send_env--; /* NB. don't increment i */ } if (onum_send_env != options->num_send_env) { options->send_env = xrecallocarray(options->send_env, onum_send_env, options->num_send_env, sizeof(*options->send_env)); } } /* * Returns the number of the token pointed to by cp or oBadOption. */ static OpCodes parse_token(const char *cp, const char *filename, int linenum, const char *ignored_unknown) { int i; for (i = 0; keywords[i].name; i++) if (strcmp(cp, keywords[i].name) == 0) return keywords[i].opcode; if (ignored_unknown != NULL && match_pattern_list(cp, ignored_unknown, 1) == 1) return oIgnoredUnknownOption; error("%s: line %d: Bad configuration option: %s", filename, linenum, cp); return oBadOption; } /* Multistate option parsing */ struct multistate { char *key; int value; }; static const struct multistate multistate_flag[] = { { "true", 1 }, { "false", 0 }, { "yes", 1 }, { "no", 0 }, { NULL, -1 } }; static const struct multistate multistate_yesnoask[] = { { "true", 1 }, { "false", 0 }, { "yes", 1 }, { "no", 0 }, { "ask", 2 }, { NULL, -1 } }; static const struct multistate multistate_strict_hostkey[] = { { "true", SSH_STRICT_HOSTKEY_YES }, { "false", SSH_STRICT_HOSTKEY_OFF }, { "yes", SSH_STRICT_HOSTKEY_YES }, { "no", SSH_STRICT_HOSTKEY_OFF }, { "ask", SSH_STRICT_HOSTKEY_ASK }, { "off", SSH_STRICT_HOSTKEY_OFF }, { "accept-new", SSH_STRICT_HOSTKEY_NEW }, { NULL, -1 } }; static const struct multistate multistate_yesnoaskconfirm[] = { { "true", 1 }, { "false", 0 }, { "yes", 1 }, { "no", 0 }, { "ask", 2 }, { "confirm", 3 }, { NULL, -1 } }; static const struct multistate multistate_addressfamily[] = { { "inet", AF_INET }, { "inet6", AF_INET6 }, { "any", AF_UNSPEC }, { NULL, -1 } }; static const struct multistate multistate_controlmaster[] = { { "true", SSHCTL_MASTER_YES }, { "yes", SSHCTL_MASTER_YES }, { "false", SSHCTL_MASTER_NO }, { "no", SSHCTL_MASTER_NO }, { "auto", SSHCTL_MASTER_AUTO }, { "ask", SSHCTL_MASTER_ASK }, { "autoask", SSHCTL_MASTER_AUTO_ASK }, { NULL, -1 } }; static const struct multistate multistate_tunnel[] = { { "ethernet", SSH_TUNMODE_ETHERNET }, { "point-to-point", SSH_TUNMODE_POINTOPOINT }, { "true", SSH_TUNMODE_DEFAULT }, { "yes", SSH_TUNMODE_DEFAULT }, { "false", SSH_TUNMODE_NO }, { "no", SSH_TUNMODE_NO }, { NULL, -1 } }; static const struct multistate multistate_requesttty[] = { { "true", REQUEST_TTY_YES }, { "yes", REQUEST_TTY_YES }, { "false", REQUEST_TTY_NO }, { "no", REQUEST_TTY_NO }, { "force", REQUEST_TTY_FORCE }, { "auto", REQUEST_TTY_AUTO }, { NULL, -1 } }; static const struct multistate multistate_sessiontype[] = { { "none", SESSION_TYPE_NONE }, { "subsystem", SESSION_TYPE_SUBSYSTEM }, { "default", SESSION_TYPE_DEFAULT }, { NULL, -1 } }; static const struct multistate multistate_canonicalizehostname[] = { { "true", SSH_CANONICALISE_YES }, { "false", SSH_CANONICALISE_NO }, { "yes", SSH_CANONICALISE_YES }, { "no", SSH_CANONICALISE_NO }, { "always", SSH_CANONICALISE_ALWAYS }, { NULL, -1 } }; static const struct multistate multistate_pubkey_auth[] = { { "true", SSH_PUBKEY_AUTH_ALL }, { "false", SSH_PUBKEY_AUTH_NO }, { "yes", SSH_PUBKEY_AUTH_ALL }, { "no", SSH_PUBKEY_AUTH_NO }, { "unbound", SSH_PUBKEY_AUTH_UNBOUND }, { "host-bound", SSH_PUBKEY_AUTH_HBOUND }, { NULL, -1 } }; static const struct multistate multistate_compression[] = { #ifdef WITH_ZLIB { "yes", COMP_ZLIB }, #endif { "no", COMP_NONE }, { NULL, -1 } }; static int parse_multistate_value(const char *arg, const char *filename, int linenum, const struct multistate *multistate_ptr) { int i; if (!arg || *arg == '\0') { error("%s line %d: missing argument.", filename, linenum); return -1; } for (i = 0; multistate_ptr[i].key != NULL; i++) { if (strcasecmp(arg, multistate_ptr[i].key) == 0) return multistate_ptr[i].value; } return -1; } /* * Processes a single option line as used in the configuration files. This * only sets those values that have not already been set. */ int process_config_line(Options *options, struct passwd *pw, const char *host, const char *original_host, char *line, const char *filename, int linenum, int *activep, int flags) { return process_config_line_depth(options, pw, host, original_host, line, filename, linenum, activep, flags, NULL, 0); } #define WHITESPACE " \t\r\n" static int process_config_line_depth(Options *options, struct passwd *pw, const char *host, const char *original_host, char *line, const char *filename, int linenum, int *activep, int flags, int *want_final_pass, int depth) { char *str, **charptr, *endofnumber, *keyword, *arg, *arg2, *p; char **cpptr, ***cppptr, fwdarg[256]; u_int i, *uintptr, uvalue, max_entries = 0; int r, oactive, negated, opcode, *intptr, value, value2, cmdline = 0; int remotefwd, dynamicfwd, ca_only = 0; LogLevel *log_level_ptr; SyslogFacility *log_facility_ptr; long long val64; size_t len; struct Forward fwd; const struct multistate *multistate_ptr; struct allowed_cname *cname; glob_t gl; const char *errstr; char **oav = NULL, **av; int oac = 0, ac; int ret = -1; if (activep == NULL) { /* We are processing a command line directive */ cmdline = 1; activep = &cmdline; } /* Strip trailing whitespace. Allow \f (form feed) at EOL only */ if ((len = strlen(line)) == 0) return 0; for (len--; len > 0; len--) { if (strchr(WHITESPACE "\f", line[len]) == NULL) break; line[len] = '\0'; } str = line; /* Get the keyword. (Each line is supposed to begin with a keyword). */ if ((keyword = strdelim(&str)) == NULL) return 0; /* Ignore leading whitespace. */ if (*keyword == '\0') keyword = strdelim(&str); if (keyword == NULL || !*keyword || *keyword == '\n' || *keyword == '#') return 0; /* Match lowercase keyword */ lowercase(keyword); /* Prepare to parse remainder of line */ if (str != NULL) str += strspn(str, WHITESPACE); if (str == NULL || *str == '\0') { error("%s line %d: no argument after keyword \"%s\"", filename, linenum, keyword); return -1; } opcode = parse_token(keyword, filename, linenum, options->ignored_unknown); if (argv_split(str, &oac, &oav, 1) != 0) { error("%s line %d: invalid quotes", filename, linenum); return -1; } ac = oac; av = oav; switch (opcode) { case oBadOption: /* don't panic, but count bad options */ goto out; case oIgnore: argv_consume(&ac); break; case oIgnoredUnknownOption: debug("%s line %d: Ignored unknown option \"%s\"", filename, linenum, keyword); argv_consume(&ac); break; case oConnectTimeout: intptr = &options->connection_timeout; parse_time: arg = argv_next(&ac, &av); if (!arg || *arg == '\0') { error("%s line %d: missing time value.", filename, linenum); goto out; } if (strcmp(arg, "none") == 0) value = -1; else if ((value = convtime(arg)) == -1) { error("%s line %d: invalid time value.", filename, linenum); goto out; } if (*activep && *intptr == -1) *intptr = value; break; case oForwardAgent: intptr = &options->forward_agent; arg = argv_next(&ac, &av); if (!arg || *arg == '\0') { error("%s line %d: missing argument.", filename, linenum); goto out; } value = -1; multistate_ptr = multistate_flag; for (i = 0; multistate_ptr[i].key != NULL; i++) { if (strcasecmp(arg, multistate_ptr[i].key) == 0) { value = multistate_ptr[i].value; break; } } if (value != -1) { if (*activep && *intptr == -1) *intptr = value; break; } /* ForwardAgent wasn't 'yes' or 'no', assume a path */ if (*activep && *intptr == -1) *intptr = 1; charptr = &options->forward_agent_sock_path; goto parse_agent_path; case oForwardX11: intptr = &options->forward_x11; parse_flag: multistate_ptr = multistate_flag; parse_multistate: arg = argv_next(&ac, &av); if ((value = parse_multistate_value(arg, filename, linenum, multistate_ptr)) == -1) { error("%s line %d: unsupported option \"%s\".", filename, linenum, arg); goto out; } if (*activep && *intptr == -1) *intptr = value; break; case oForwardX11Trusted: intptr = &options->forward_x11_trusted; goto parse_flag; case oForwardX11Timeout: intptr = &options->forward_x11_timeout; goto parse_time; case oGatewayPorts: intptr = &options->fwd_opts.gateway_ports; goto parse_flag; case oExitOnForwardFailure: intptr = &options->exit_on_forward_failure; goto parse_flag; case oPasswordAuthentication: intptr = &options->password_authentication; goto parse_flag; case oKbdInteractiveAuthentication: intptr = &options->kbd_interactive_authentication; goto parse_flag; case oKbdInteractiveDevices: charptr = &options->kbd_interactive_devices; goto parse_string; case oPubkeyAuthentication: multistate_ptr = multistate_pubkey_auth; intptr = &options->pubkey_authentication; goto parse_multistate; case oHostbasedAuthentication: intptr = &options->hostbased_authentication; goto parse_flag; case oGssAuthentication: intptr = &options->gss_authentication; goto parse_flag; case oGssDelegateCreds: intptr = &options->gss_deleg_creds; goto parse_flag; case oBatchMode: intptr = &options->batch_mode; goto parse_flag; case oCheckHostIP: intptr = &options->check_host_ip; goto parse_flag; case oVerifyHostKeyDNS: intptr = &options->verify_host_key_dns; multistate_ptr = multistate_yesnoask; goto parse_multistate; case oStrictHostKeyChecking: intptr = &options->strict_host_key_checking; multistate_ptr = multistate_strict_hostkey; goto parse_multistate; case oCompression: intptr = &options->compression; multistate_ptr = multistate_compression; goto parse_multistate; case oTCPKeepAlive: intptr = &options->tcp_keep_alive; goto parse_flag; case oNoHostAuthenticationForLocalhost: intptr = &options->no_host_authentication_for_localhost; goto parse_flag; case oNumberOfPasswordPrompts: intptr = &options->number_of_password_prompts; goto parse_int; case oRekeyLimit: arg = argv_next(&ac, &av); if (!arg || *arg == '\0') { error("%.200s line %d: Missing argument.", filename, linenum); goto out; } if (strcmp(arg, "default") == 0) { val64 = 0; } else { if (scan_scaled(arg, &val64) == -1) { error("%.200s line %d: Bad number '%s': %s", filename, linenum, arg, strerror(errno)); goto out; } if (val64 != 0 && val64 < 16) { error("%.200s line %d: RekeyLimit too small", filename, linenum); goto out; } } if (*activep && options->rekey_limit == -1) options->rekey_limit = val64; if (ac != 0) { /* optional rekey interval present */ if (strcmp(av[0], "none") == 0) { (void)argv_next(&ac, &av); /* discard */ break; } intptr = &options->rekey_interval; goto parse_time; } break; case oIdentityFile: arg = argv_next(&ac, &av); if (!arg || *arg == '\0') { error("%.200s line %d: Missing argument.", filename, linenum); goto out; } if (*activep) { intptr = &options->num_identity_files; if (*intptr >= SSH_MAX_IDENTITY_FILES) { error("%.200s line %d: Too many identity files " "specified (max %d).", filename, linenum, SSH_MAX_IDENTITY_FILES); goto out; } add_identity_file(options, NULL, arg, flags & SSHCONF_USERCONF); } break; case oCertificateFile: arg = argv_next(&ac, &av); if (!arg || *arg == '\0') { error("%.200s line %d: Missing argument.", filename, linenum); goto out; } if (*activep) { intptr = &options->num_certificate_files; if (*intptr >= SSH_MAX_CERTIFICATE_FILES) { error("%.200s line %d: Too many certificate " "files specified (max %d).", filename, linenum, SSH_MAX_CERTIFICATE_FILES); goto out; } add_certificate_file(options, arg, flags & SSHCONF_USERCONF); } break; case oXAuthLocation: charptr=&options->xauth_location; goto parse_string; case oUser: charptr = &options->user; parse_string: arg = argv_next(&ac, &av); if (!arg || *arg == '\0') { error("%.200s line %d: Missing argument.", filename, linenum); goto out; } if (*activep && *charptr == NULL) *charptr = xstrdup(arg); break; case oGlobalKnownHostsFile: cpptr = (char **)&options->system_hostfiles; uintptr = &options->num_system_hostfiles; max_entries = SSH_MAX_HOSTS_FILES; parse_char_array: i = 0; value = *uintptr == 0; /* was array empty when we started? */ while ((arg = argv_next(&ac, &av)) != NULL) { if (*arg == '\0') { error("%s line %d: keyword %s empty argument", filename, linenum, keyword); goto out; } /* Allow "none" only in first position */ if (strcasecmp(arg, "none") == 0) { if (i > 0 || ac > 0) { error("%s line %d: keyword %s \"none\" " "argument must appear alone.", filename, linenum, keyword); goto out; } } i++; if (*activep && value) { if ((*uintptr) >= max_entries) { error("%s line %d: too many %s " "entries.", filename, linenum, keyword); goto out; } cpptr[(*uintptr)++] = xstrdup(arg); } } break; case oUserKnownHostsFile: cpptr = (char **)&options->user_hostfiles; uintptr = &options->num_user_hostfiles; max_entries = SSH_MAX_HOSTS_FILES; goto parse_char_array; case oHostname: charptr = &options->hostname; goto parse_string; case oTag: charptr = &options->tag; goto parse_string; case oHostKeyAlias: charptr = &options->host_key_alias; goto parse_string; case oPreferredAuthentications: charptr = &options->preferred_authentications; goto parse_string; case oBindAddress: charptr = &options->bind_address; goto parse_string; case oBindInterface: charptr = &options->bind_interface; goto parse_string; case oPKCS11Provider: charptr = &options->pkcs11_provider; goto parse_string; case oSecurityKeyProvider: charptr = &options->sk_provider; goto parse_string; case oKnownHostsCommand: charptr = &options->known_hosts_command; goto parse_command; case oProxyCommand: charptr = &options->proxy_command; /* Ignore ProxyCommand if ProxyJump already specified */ if (options->jump_host != NULL) charptr = &options->jump_host; /* Skip below */ parse_command: if (str == NULL) { error("%.200s line %d: Missing argument.", filename, linenum); goto out; } len = strspn(str, WHITESPACE "="); if (*activep && *charptr == NULL) *charptr = xstrdup(str + len); argv_consume(&ac); break; case oProxyJump: if (str == NULL) { error("%.200s line %d: Missing argument.", filename, linenum); goto out; } len = strspn(str, WHITESPACE "="); /* XXX use argv? */ if (parse_jump(str + len, options, *activep) == -1) { error("%.200s line %d: Invalid ProxyJump \"%s\"", filename, linenum, str + len); goto out; } argv_consume(&ac); break; case oPort: arg = argv_next(&ac, &av); if (!arg || *arg == '\0') { error("%.200s line %d: Missing argument.", filename, linenum); goto out; } value = a2port(arg); if (value <= 0) { error("%.200s line %d: Bad port '%s'.", filename, linenum, arg); goto out; } if (*activep && options->port == -1) options->port = value; break; case oConnectionAttempts: intptr = &options->connection_attempts; parse_int: arg = argv_next(&ac, &av); if ((errstr = atoi_err(arg, &value)) != NULL) { error("%s line %d: integer value %s.", filename, linenum, errstr); goto out; } if (*activep && *intptr == -1) *intptr = value; break; case oCiphers: arg = argv_next(&ac, &av); if (!arg || *arg == '\0') { error("%.200s line %d: Missing argument.", filename, linenum); goto out; } if (*arg != '-' && !ciphers_valid(*arg == '+' || *arg == '^' ? arg + 1 : arg)){ error("%.200s line %d: Bad SSH2 cipher spec '%s'.", filename, linenum, arg ? arg : ""); goto out; } if (*activep && options->ciphers == NULL) options->ciphers = xstrdup(arg); break; case oMacs: arg = argv_next(&ac, &av); if (!arg || *arg == '\0') { error("%.200s line %d: Missing argument.", filename, linenum); goto out; } if (*arg != '-' && !mac_valid(*arg == '+' || *arg == '^' ? arg + 1 : arg)) { error("%.200s line %d: Bad SSH2 MAC spec '%s'.", filename, linenum, arg ? arg : ""); goto out; } if (*activep && options->macs == NULL) options->macs = xstrdup(arg); break; case oKexAlgorithms: arg = argv_next(&ac, &av); if (!arg || *arg == '\0') { error("%.200s line %d: Missing argument.", filename, linenum); goto out; } if (*arg != '-' && !kex_names_valid(*arg == '+' || *arg == '^' ? arg + 1 : arg)) { error("%.200s line %d: Bad SSH2 KexAlgorithms '%s'.", filename, linenum, arg ? arg : ""); goto out; } if (*activep && options->kex_algorithms == NULL) options->kex_algorithms = xstrdup(arg); break; case oHostKeyAlgorithms: charptr = &options->hostkeyalgorithms; ca_only = 0; parse_pubkey_algos: arg = argv_next(&ac, &av); if (!arg || *arg == '\0') { error("%.200s line %d: Missing argument.", filename, linenum); goto out; } if (*arg != '-' && !sshkey_names_valid2(*arg == '+' || *arg == '^' ? arg + 1 : arg, 1, ca_only)) { error("%s line %d: Bad key types '%s'.", filename, linenum, arg ? arg : ""); goto out; } if (*activep && *charptr == NULL) *charptr = xstrdup(arg); break; case oCASignatureAlgorithms: charptr = &options->ca_sign_algorithms; ca_only = 1; goto parse_pubkey_algos; case oLogLevel: log_level_ptr = &options->log_level; arg = argv_next(&ac, &av); value = log_level_number(arg); if (value == SYSLOG_LEVEL_NOT_SET) { error("%.200s line %d: unsupported log level '%s'", filename, linenum, arg ? arg : ""); goto out; } if (*activep && *log_level_ptr == SYSLOG_LEVEL_NOT_SET) *log_level_ptr = (LogLevel) value; break; case oLogFacility: log_facility_ptr = &options->log_facility; arg = argv_next(&ac, &av); value = log_facility_number(arg); if (value == SYSLOG_FACILITY_NOT_SET) { error("%.200s line %d: unsupported log facility '%s'", filename, linenum, arg ? arg : ""); goto out; } if (*log_facility_ptr == -1) *log_facility_ptr = (SyslogFacility) value; break; case oLogVerbose: cppptr = &options->log_verbose; uintptr = &options->num_log_verbose; i = 0; while ((arg = argv_next(&ac, &av)) != NULL) { if (*arg == '\0') { error("%s line %d: keyword %s empty argument", filename, linenum, keyword); goto out; } /* Allow "none" only in first position */ if (strcasecmp(arg, "none") == 0) { if (i > 0 || ac > 0) { error("%s line %d: keyword %s \"none\" " "argument must appear alone.", filename, linenum, keyword); goto out; } } i++; if (*activep && *uintptr == 0) { *cppptr = xrecallocarray(*cppptr, *uintptr, *uintptr + 1, sizeof(**cppptr)); (*cppptr)[(*uintptr)++] = xstrdup(arg); } } break; case oLocalForward: case oRemoteForward: case oDynamicForward: arg = argv_next(&ac, &av); if (!arg || *arg == '\0') { error("%.200s line %d: Missing argument.", filename, linenum); goto out; } remotefwd = (opcode == oRemoteForward); dynamicfwd = (opcode == oDynamicForward); if (!dynamicfwd) { arg2 = argv_next(&ac, &av); if (arg2 == NULL || *arg2 == '\0') { if (remotefwd) dynamicfwd = 1; else { error("%.200s line %d: Missing target " "argument.", filename, linenum); goto out; } } else { /* construct a string for parse_forward */ snprintf(fwdarg, sizeof(fwdarg), "%s:%s", arg, arg2); } } if (dynamicfwd) strlcpy(fwdarg, arg, sizeof(fwdarg)); if (parse_forward(&fwd, fwdarg, dynamicfwd, remotefwd) == 0) { error("%.200s line %d: Bad forwarding specification.", filename, linenum); goto out; } if (*activep) { if (remotefwd) { add_remote_forward(options, &fwd); } else { add_local_forward(options, &fwd); } } break; case oPermitRemoteOpen: uintptr = &options->num_permitted_remote_opens; cppptr = &options->permitted_remote_opens; uvalue = *uintptr; /* modified later */ i = 0; while ((arg = argv_next(&ac, &av)) != NULL) { arg2 = xstrdup(arg); /* Allow any/none only in first position */ if (strcasecmp(arg, "none") == 0 || strcasecmp(arg, "any") == 0) { if (i > 0 || ac > 0) { error("%s line %d: keyword %s \"%s\" " "argument must appear alone.", filename, linenum, keyword, arg); free(arg2); goto out; } } else { p = hpdelim(&arg); if (p == NULL) { fatal("%s line %d: missing host in %s", filename, linenum, lookup_opcode_name(opcode)); } p = cleanhostname(p); /* * don't want to use permitopen_port to avoid * dependency on channels.[ch] here. */ if (arg == NULL || (strcmp(arg, "*") != 0 && a2port(arg) <= 0)) { fatal("%s line %d: bad port number " "in %s", filename, linenum, lookup_opcode_name(opcode)); } } if (*activep && uvalue == 0) { opt_array_append(filename, linenum, lookup_opcode_name(opcode), cppptr, uintptr, arg2); } free(arg2); i++; } if (i == 0) fatal("%s line %d: missing %s specification", filename, linenum, lookup_opcode_name(opcode)); break; case oClearAllForwardings: intptr = &options->clear_forwardings; goto parse_flag; case oHost: if (cmdline) { error("Host directive not supported as a command-line " "option"); goto out; } *activep = 0; arg2 = NULL; while ((arg = argv_next(&ac, &av)) != NULL) { if (*arg == '\0') { error("%s line %d: keyword %s empty argument", filename, linenum, keyword); goto out; } if ((flags & SSHCONF_NEVERMATCH) != 0) { argv_consume(&ac); break; } negated = *arg == '!'; if (negated) arg++; if (match_pattern(host, arg)) { if (negated) { debug("%.200s line %d: Skipping Host " "block because of negated match " "for %.100s", filename, linenum, arg); *activep = 0; argv_consume(&ac); break; } if (!*activep) arg2 = arg; /* logged below */ *activep = 1; } } if (*activep) debug("%.200s line %d: Applying options for %.100s", filename, linenum, arg2); break; case oMatch: if (cmdline) { error("Host directive not supported as a command-line " "option"); goto out; } value = match_cfg_line(options, &str, pw, host, original_host, flags & SSHCONF_FINAL, want_final_pass, filename, linenum); if (value < 0) { error("%.200s line %d: Bad Match condition", filename, linenum); goto out; } *activep = (flags & SSHCONF_NEVERMATCH) ? 0 : value; /* * If match_cfg_line() didn't consume all its arguments then * arrange for the extra arguments check below to fail. */ if (str == NULL || *str == '\0') argv_consume(&ac); break; case oEscapeChar: intptr = &options->escape_char; arg = argv_next(&ac, &av); if (!arg || *arg == '\0') { error("%.200s line %d: Missing argument.", filename, linenum); goto out; } if (strcmp(arg, "none") == 0) value = SSH_ESCAPECHAR_NONE; else if (arg[1] == '\0') value = (u_char) arg[0]; else if (arg[0] == '^' && arg[2] == 0 && (u_char) arg[1] >= 64 && (u_char) arg[1] < 128) value = (u_char) arg[1] & 31; else { error("%.200s line %d: Bad escape character.", filename, linenum); goto out; } if (*activep && *intptr == -1) *intptr = value; break; case oAddressFamily: intptr = &options->address_family; multistate_ptr = multistate_addressfamily; goto parse_multistate; case oEnableSSHKeysign: intptr = &options->enable_ssh_keysign; goto parse_flag; case oIdentitiesOnly: intptr = &options->identities_only; goto parse_flag; case oServerAliveInterval: intptr = &options->server_alive_interval; goto parse_time; case oServerAliveCountMax: intptr = &options->server_alive_count_max; goto parse_int; case oSendEnv: while ((arg = argv_next(&ac, &av)) != NULL) { if (*arg == '\0' || strchr(arg, '=') != NULL) { error("%s line %d: Invalid environment name.", filename, linenum); goto out; } if (!*activep) continue; if (*arg == '-') { /* Removing an env var */ rm_env(options, arg, filename, linenum); continue; } opt_array_append(filename, linenum, lookup_opcode_name(opcode), &options->send_env, &options->num_send_env, arg); } break; case oSetEnv: value = options->num_setenv; while ((arg = argv_next(&ac, &av)) != NULL) { if (strchr(arg, '=') == NULL) { error("%s line %d: Invalid SetEnv.", filename, linenum); goto out; } if (!*activep || value != 0) continue; if (lookup_setenv_in_list(arg, options->setenv, options->num_setenv) != NULL) { debug2("%s line %d: ignoring duplicate env " "name \"%.64s\"", filename, linenum, arg); continue; } opt_array_append(filename, linenum, lookup_opcode_name(opcode), &options->setenv, &options->num_setenv, arg); } break; case oControlPath: charptr = &options->control_path; goto parse_string; case oControlMaster: intptr = &options->control_master; multistate_ptr = multistate_controlmaster; goto parse_multistate; case oControlPersist: /* no/false/yes/true, or a time spec */ intptr = &options->control_persist; arg = argv_next(&ac, &av); if (!arg || *arg == '\0') { error("%.200s line %d: Missing ControlPersist" " argument.", filename, linenum); goto out; } value = 0; value2 = 0; /* timeout */ if (strcmp(arg, "no") == 0 || strcmp(arg, "false") == 0) value = 0; else if (strcmp(arg, "yes") == 0 || strcmp(arg, "true") == 0) value = 1; else if ((value2 = convtime(arg)) >= 0) value = 1; else { error("%.200s line %d: Bad ControlPersist argument.", filename, linenum); goto out; } if (*activep && *intptr == -1) { *intptr = value; options->control_persist_timeout = value2; } break; case oHashKnownHosts: intptr = &options->hash_known_hosts; goto parse_flag; case oTunnel: intptr = &options->tun_open; multistate_ptr = multistate_tunnel; goto parse_multistate; case oTunnelDevice: arg = argv_next(&ac, &av); if (!arg || *arg == '\0') { error("%.200s line %d: Missing argument.", filename, linenum); goto out; } value = a2tun(arg, &value2); if (value == SSH_TUNID_ERR) { error("%.200s line %d: Bad tun device.", filename, linenum); goto out; } if (*activep && options->tun_local == -1) { options->tun_local = value; options->tun_remote = value2; } break; case oLocalCommand: charptr = &options->local_command; goto parse_command; case oPermitLocalCommand: intptr = &options->permit_local_command; goto parse_flag; case oRemoteCommand: charptr = &options->remote_command; goto parse_command; case oVisualHostKey: intptr = &options->visual_host_key; goto parse_flag; case oInclude: if (cmdline) { error("Include directive not supported as a " "command-line option"); goto out; } value = 0; while ((arg = argv_next(&ac, &av)) != NULL) { if (*arg == '\0') { error("%s line %d: keyword %s empty argument", filename, linenum, keyword); goto out; } /* * Ensure all paths are anchored. User configuration * files may begin with '~/' but system configurations * must not. If the path is relative, then treat it * as living in ~/.ssh for user configurations or * /etc/ssh for system ones. */ if (*arg == '~' && (flags & SSHCONF_USERCONF) == 0) { error("%.200s line %d: bad include path %s.", filename, linenum, arg); goto out; } if (!path_absolute(arg) && *arg != '~') { xasprintf(&arg2, "%s/%s", (flags & SSHCONF_USERCONF) ? "~/" _PATH_SSH_USER_DIR : SSHDIR, arg); } else arg2 = xstrdup(arg); memset(&gl, 0, sizeof(gl)); r = glob(arg2, GLOB_TILDE, NULL, &gl); if (r == GLOB_NOMATCH) { debug("%.200s line %d: include %s matched no " "files",filename, linenum, arg2); free(arg2); continue; } else if (r != 0) { error("%.200s line %d: glob failed for %s.", filename, linenum, arg2); goto out; } free(arg2); oactive = *activep; for (i = 0; i < gl.gl_pathc; i++) { debug3("%.200s line %d: Including file %s " "depth %d%s", filename, linenum, gl.gl_pathv[i], depth, oactive ? "" : " (parse only)"); r = read_config_file_depth(gl.gl_pathv[i], pw, host, original_host, options, flags | SSHCONF_CHECKPERM | (oactive ? 0 : SSHCONF_NEVERMATCH), activep, want_final_pass, depth + 1); if (r != 1 && errno != ENOENT) { error("Can't open user config file " "%.100s: %.100s", gl.gl_pathv[i], strerror(errno)); globfree(&gl); goto out; } /* * don't let Match in includes clobber the * containing file's Match state. */ *activep = oactive; if (r != 1) value = -1; } globfree(&gl); } if (value != 0) ret = value; break; case oIPQoS: arg = argv_next(&ac, &av); if ((value = parse_ipqos(arg)) == -1) { error("%s line %d: Bad IPQoS value: %s", filename, linenum, arg); goto out; } arg = argv_next(&ac, &av); if (arg == NULL) value2 = value; else if ((value2 = parse_ipqos(arg)) == -1) { error("%s line %d: Bad IPQoS value: %s", filename, linenum, arg); goto out; } if (*activep && options->ip_qos_interactive == -1) { options->ip_qos_interactive = value; options->ip_qos_bulk = value2; } break; case oRequestTTY: intptr = &options->request_tty; multistate_ptr = multistate_requesttty; goto parse_multistate; case oSessionType: intptr = &options->session_type; multistate_ptr = multistate_sessiontype; goto parse_multistate; case oStdinNull: intptr = &options->stdin_null; goto parse_flag; case oForkAfterAuthentication: intptr = &options->fork_after_authentication; goto parse_flag; case oIgnoreUnknown: charptr = &options->ignored_unknown; goto parse_string; case oProxyUseFdpass: intptr = &options->proxy_use_fdpass; goto parse_flag; case oCanonicalDomains: value = options->num_canonical_domains != 0; i = 0; while ((arg = argv_next(&ac, &av)) != NULL) { if (*arg == '\0') { error("%s line %d: keyword %s empty argument", filename, linenum, keyword); goto out; } /* Allow "none" only in first position */ if (strcasecmp(arg, "none") == 0) { if (i > 0 || ac > 0) { error("%s line %d: keyword %s \"none\" " "argument must appear alone.", filename, linenum, keyword); goto out; } } i++; if (!valid_domain(arg, 1, &errstr)) { error("%s line %d: %s", filename, linenum, errstr); goto out; } if (!*activep || value) continue; if (options->num_canonical_domains >= MAX_CANON_DOMAINS) { error("%s line %d: too many hostname suffixes.", filename, linenum); goto out; } options->canonical_domains[ options->num_canonical_domains++] = xstrdup(arg); } break; case oCanonicalizePermittedCNAMEs: value = options->num_permitted_cnames != 0; i = 0; while ((arg = argv_next(&ac, &av)) != NULL) { /* * Either 'none' (only in first position), '*' for * everything or 'list:list' */ if (strcasecmp(arg, "none") == 0) { if (i > 0 || ac > 0) { error("%s line %d: keyword %s \"none\" " "argument must appear alone.", filename, linenum, keyword); goto out; } arg2 = ""; } else if (strcmp(arg, "*") == 0) { arg2 = arg; } else { lowercase(arg); if ((arg2 = strchr(arg, ':')) == NULL || arg2[1] == '\0') { error("%s line %d: " "Invalid permitted CNAME \"%s\"", filename, linenum, arg); goto out; } *arg2 = '\0'; arg2++; } i++; if (!*activep || value) continue; if (options->num_permitted_cnames >= MAX_CANON_DOMAINS) { error("%s line %d: too many permitted CNAMEs.", filename, linenum); goto out; } cname = options->permitted_cnames + options->num_permitted_cnames++; cname->source_list = xstrdup(arg); cname->target_list = xstrdup(arg2); } break; case oCanonicalizeHostname: intptr = &options->canonicalize_hostname; multistate_ptr = multistate_canonicalizehostname; goto parse_multistate; case oCanonicalizeMaxDots: intptr = &options->canonicalize_max_dots; goto parse_int; case oCanonicalizeFallbackLocal: intptr = &options->canonicalize_fallback_local; goto parse_flag; case oStreamLocalBindMask: arg = argv_next(&ac, &av); if (!arg || *arg == '\0') { error("%.200s line %d: Missing StreamLocalBindMask " "argument.", filename, linenum); goto out; } /* Parse mode in octal format */ value = strtol(arg, &endofnumber, 8); if (arg == endofnumber || value < 0 || value > 0777) { error("%.200s line %d: Bad mask.", filename, linenum); goto out; } options->fwd_opts.streamlocal_bind_mask = (mode_t)value; break; case oStreamLocalBindUnlink: intptr = &options->fwd_opts.streamlocal_bind_unlink; goto parse_flag; case oRevokedHostKeys: charptr = &options->revoked_host_keys; goto parse_string; case oFingerprintHash: intptr = &options->fingerprint_hash; arg = argv_next(&ac, &av); if (!arg || *arg == '\0') { error("%.200s line %d: Missing argument.", filename, linenum); goto out; } if ((value = ssh_digest_alg_by_name(arg)) == -1) { error("%.200s line %d: Invalid hash algorithm \"%s\".", filename, linenum, arg); goto out; } if (*activep && *intptr == -1) *intptr = value; break; case oUpdateHostkeys: intptr = &options->update_hostkeys; multistate_ptr = multistate_yesnoask; goto parse_multistate; case oHostbasedAcceptedAlgorithms: charptr = &options->hostbased_accepted_algos; ca_only = 0; goto parse_pubkey_algos; case oPubkeyAcceptedAlgorithms: charptr = &options->pubkey_accepted_algos; ca_only = 0; goto parse_pubkey_algos; case oAddKeysToAgent: arg = argv_next(&ac, &av); arg2 = argv_next(&ac, &av); value = parse_multistate_value(arg, filename, linenum, multistate_yesnoaskconfirm); value2 = 0; /* unlimited lifespan by default */ if (value == 3 && arg2 != NULL) { /* allow "AddKeysToAgent confirm 5m" */ if ((value2 = convtime(arg2)) == -1) { error("%s line %d: invalid time value.", filename, linenum); goto out; } } else if (value == -1 && arg2 == NULL) { if ((value2 = convtime(arg)) == -1) { error("%s line %d: unsupported option", filename, linenum); goto out; } value = 1; /* yes */ } else if (value == -1 || arg2 != NULL) { error("%s line %d: unsupported option", filename, linenum); goto out; } if (*activep && options->add_keys_to_agent == -1) { options->add_keys_to_agent = value; options->add_keys_to_agent_lifespan = value2; } break; case oIdentityAgent: charptr = &options->identity_agent; arg = argv_next(&ac, &av); if (!arg || *arg == '\0') { error("%.200s line %d: Missing argument.", filename, linenum); goto out; } parse_agent_path: /* Extra validation if the string represents an env var. */ if ((arg2 = dollar_expand(&r, arg)) == NULL || r) { error("%.200s line %d: Invalid environment expansion " "%s.", filename, linenum, arg); goto out; } free(arg2); /* check for legacy environment format */ if (arg[0] == '$' && arg[1] != '{' && !valid_env_name(arg + 1)) { error("%.200s line %d: Invalid environment name %s.", filename, linenum, arg); goto out; } if (*activep && *charptr == NULL) *charptr = xstrdup(arg); break; case oEnableEscapeCommandline: intptr = &options->enable_escape_commandline; goto parse_flag; case oRequiredRSASize: intptr = &options->required_rsa_size; goto parse_int; + case oObscureKeystrokeTiming: + value = -1; + while ((arg = argv_next(&ac, &av)) != NULL) { + if (value != -1) { + error("%s line %d: invalid arguments", + filename, linenum); + goto out; + } + if (strcmp(arg, "yes") == 0 || + strcmp(arg, "true") == 0) + value = SSH_KEYSTROKE_DEFAULT_INTERVAL_MS; + else if (strcmp(arg, "no") == 0 || + strcmp(arg, "false") == 0) + value = 0; + else if (strncmp(arg, "interval:", 9) == 0) { + if ((errstr = atoi_err(arg + 9, + &value)) != NULL) { + error("%s line %d: integer value %s.", + filename, linenum, errstr); + goto out; + } + if (value <= 0 || value > 1000) { + error("%s line %d: value out of range.", + filename, linenum); + goto out; + } + } else { + error("%s line %d: unsupported argument \"%s\"", + filename, linenum, arg); + goto out; + } + } + if (value == -1) { + error("%s line %d: missing argument", + filename, linenum); + goto out; + } + intptr = &options->obscure_keystroke_timing_interval; + if (*activep && *intptr == -1) + *intptr = value; + break; + case oDeprecated: debug("%s line %d: Deprecated option \"%s\"", filename, linenum, keyword); argv_consume(&ac); break; case oUnsupported: error("%s line %d: Unsupported option \"%s\"", filename, linenum, keyword); argv_consume(&ac); break; default: error("%s line %d: Unimplemented opcode %d", filename, linenum, opcode); goto out; } /* Check that there is no garbage at end of line. */ if (ac > 0) { error("%.200s line %d: keyword %s extra arguments " "at end of line", filename, linenum, keyword); goto out; } /* success */ ret = 0; out: argv_free(oav, oac); return ret; } /* * Reads the config file and modifies the options accordingly. Options * should already be initialized before this call. This never returns if * there is an error. If the file does not exist, this returns 0. */ int read_config_file(const char *filename, struct passwd *pw, const char *host, const char *original_host, Options *options, int flags, int *want_final_pass) { int active = 1; return read_config_file_depth(filename, pw, host, original_host, options, flags, &active, want_final_pass, 0); } #define READCONF_MAX_DEPTH 16 static int read_config_file_depth(const char *filename, struct passwd *pw, const char *host, const char *original_host, Options *options, int flags, int *activep, int *want_final_pass, int depth) { FILE *f; char *line = NULL; size_t linesize = 0; int linenum; int bad_options = 0; if (depth < 0 || depth > READCONF_MAX_DEPTH) fatal("Too many recursive configuration includes"); if ((f = fopen(filename, "r")) == NULL) return 0; if (flags & SSHCONF_CHECKPERM) { struct stat sb; if (fstat(fileno(f), &sb) == -1) fatal("fstat %s: %s", filename, strerror(errno)); if (((sb.st_uid != 0 && sb.st_uid != getuid()) || (sb.st_mode & 022) != 0)) fatal("Bad owner or permissions on %s", filename); } debug("Reading configuration data %.200s", filename); /* * Mark that we are now processing the options. This flag is turned * on/off by Host specifications. */ linenum = 0; while (getline(&line, &linesize, f) != -1) { /* Update line number counter. */ linenum++; /* * Trim out comments and strip whitespace. * NB - preserve newlines, they are needed to reproduce * line numbers later for error messages. */ if (process_config_line_depth(options, pw, host, original_host, line, filename, linenum, activep, flags, want_final_pass, depth) != 0) bad_options++; } free(line); fclose(f); if (bad_options > 0) fatal("%s: terminating, %d bad configuration options", filename, bad_options); return 1; } /* Returns 1 if a string option is unset or set to "none" or 0 otherwise. */ int option_clear_or_none(const char *o) { return o == NULL || strcasecmp(o, "none") == 0; } /* * Returns 1 if CanonicalizePermittedCNAMEs have been specified, 0 otherwise. * Allowed to be called on non-final configuration. */ int config_has_permitted_cnames(Options *options) { if (options->num_permitted_cnames == 1 && strcasecmp(options->permitted_cnames[0].source_list, "none") == 0 && strcmp(options->permitted_cnames[0].target_list, "") == 0) return 0; return options->num_permitted_cnames > 0; } /* * Initializes options to special values that indicate that they have not yet * been set. Read_config_file will only set options with this value. Options * are processed in the following order: command line, user config file, * system config file. Last, fill_default_options is called. */ void initialize_options(Options * options) { memset(options, 'X', sizeof(*options)); options->host_arg = NULL; options->forward_agent = -1; options->forward_agent_sock_path = NULL; options->forward_x11 = -1; options->forward_x11_trusted = -1; options->forward_x11_timeout = -1; options->stdio_forward_host = NULL; options->stdio_forward_port = 0; options->clear_forwardings = -1; options->exit_on_forward_failure = -1; options->xauth_location = NULL; options->fwd_opts.gateway_ports = -1; options->fwd_opts.streamlocal_bind_mask = (mode_t)-1; options->fwd_opts.streamlocal_bind_unlink = -1; options->pubkey_authentication = -1; options->gss_authentication = -1; options->gss_deleg_creds = -1; options->password_authentication = -1; options->kbd_interactive_authentication = -1; options->kbd_interactive_devices = NULL; options->hostbased_authentication = -1; options->batch_mode = -1; options->check_host_ip = -1; options->strict_host_key_checking = -1; options->compression = -1; options->tcp_keep_alive = -1; options->port = -1; options->address_family = -1; options->connection_attempts = -1; options->connection_timeout = -1; options->number_of_password_prompts = -1; options->ciphers = NULL; options->macs = NULL; options->kex_algorithms = NULL; options->hostkeyalgorithms = NULL; options->ca_sign_algorithms = NULL; options->num_identity_files = 0; memset(options->identity_keys, 0, sizeof(options->identity_keys)); options->num_certificate_files = 0; memset(options->certificates, 0, sizeof(options->certificates)); options->hostname = NULL; options->host_key_alias = NULL; options->proxy_command = NULL; options->jump_user = NULL; options->jump_host = NULL; options->jump_port = -1; options->jump_extra = NULL; options->user = NULL; options->escape_char = -1; options->num_system_hostfiles = 0; options->num_user_hostfiles = 0; options->local_forwards = NULL; options->num_local_forwards = 0; options->remote_forwards = NULL; options->num_remote_forwards = 0; options->permitted_remote_opens = NULL; options->num_permitted_remote_opens = 0; options->log_facility = SYSLOG_FACILITY_NOT_SET; options->log_level = SYSLOG_LEVEL_NOT_SET; options->num_log_verbose = 0; options->log_verbose = NULL; options->preferred_authentications = NULL; options->bind_address = NULL; options->bind_interface = NULL; options->pkcs11_provider = NULL; options->sk_provider = NULL; options->enable_ssh_keysign = - 1; options->no_host_authentication_for_localhost = - 1; options->identities_only = - 1; options->rekey_limit = - 1; options->rekey_interval = -1; options->verify_host_key_dns = -1; options->server_alive_interval = -1; options->server_alive_count_max = -1; options->send_env = NULL; options->num_send_env = 0; options->setenv = NULL; options->num_setenv = 0; options->control_path = NULL; options->control_master = -1; options->control_persist = -1; options->control_persist_timeout = 0; options->hash_known_hosts = -1; options->tun_open = -1; options->tun_local = -1; options->tun_remote = -1; options->local_command = NULL; options->permit_local_command = -1; options->remote_command = NULL; options->add_keys_to_agent = -1; options->add_keys_to_agent_lifespan = -1; options->identity_agent = NULL; options->visual_host_key = -1; options->ip_qos_interactive = -1; options->ip_qos_bulk = -1; options->request_tty = -1; options->session_type = -1; options->stdin_null = -1; options->fork_after_authentication = -1; options->proxy_use_fdpass = -1; options->ignored_unknown = NULL; options->num_canonical_domains = 0; options->num_permitted_cnames = 0; options->canonicalize_max_dots = -1; options->canonicalize_fallback_local = -1; options->canonicalize_hostname = -1; options->revoked_host_keys = NULL; options->fingerprint_hash = -1; options->update_hostkeys = -1; options->hostbased_accepted_algos = NULL; options->pubkey_accepted_algos = NULL; options->known_hosts_command = NULL; options->required_rsa_size = -1; options->enable_escape_commandline = -1; + options->obscure_keystroke_timing_interval = -1; options->tag = NULL; } /* * A petite version of fill_default_options() that just fills the options * needed for hostname canonicalization to proceed. */ void fill_default_options_for_canonicalization(Options *options) { if (options->canonicalize_max_dots == -1) options->canonicalize_max_dots = 1; if (options->canonicalize_fallback_local == -1) options->canonicalize_fallback_local = 1; if (options->canonicalize_hostname == -1) options->canonicalize_hostname = SSH_CANONICALISE_NO; } /* * Called after processing other sources of option data, this fills those * options for which no value has been specified with their default values. */ int fill_default_options(Options * options) { char *all_cipher, *all_mac, *all_kex, *all_key, *all_sig; char *def_cipher, *def_mac, *def_kex, *def_key, *def_sig; int ret = 0, r; if (options->forward_agent == -1) options->forward_agent = 0; if (options->forward_x11 == -1) options->forward_x11 = 0; if (options->forward_x11_trusted == -1) options->forward_x11_trusted = 0; if (options->forward_x11_timeout == -1) options->forward_x11_timeout = 1200; /* * stdio forwarding (-W) changes the default for these but we defer * setting the values so they can be overridden. */ if (options->exit_on_forward_failure == -1) options->exit_on_forward_failure = options->stdio_forward_host != NULL ? 1 : 0; if (options->clear_forwardings == -1) options->clear_forwardings = options->stdio_forward_host != NULL ? 1 : 0; if (options->clear_forwardings == 1) clear_forwardings(options); if (options->xauth_location == NULL) options->xauth_location = xstrdup(_PATH_XAUTH); if (options->fwd_opts.gateway_ports == -1) options->fwd_opts.gateway_ports = 0; if (options->fwd_opts.streamlocal_bind_mask == (mode_t)-1) options->fwd_opts.streamlocal_bind_mask = 0177; if (options->fwd_opts.streamlocal_bind_unlink == -1) options->fwd_opts.streamlocal_bind_unlink = 0; if (options->pubkey_authentication == -1) options->pubkey_authentication = SSH_PUBKEY_AUTH_ALL; if (options->gss_authentication == -1) options->gss_authentication = 0; if (options->gss_deleg_creds == -1) options->gss_deleg_creds = 0; if (options->password_authentication == -1) options->password_authentication = 1; if (options->kbd_interactive_authentication == -1) options->kbd_interactive_authentication = 1; if (options->hostbased_authentication == -1) options->hostbased_authentication = 0; if (options->batch_mode == -1) options->batch_mode = 0; if (options->check_host_ip == -1) options->check_host_ip = 0; if (options->strict_host_key_checking == -1) options->strict_host_key_checking = SSH_STRICT_HOSTKEY_ASK; if (options->compression == -1) options->compression = 0; if (options->tcp_keep_alive == -1) options->tcp_keep_alive = 1; if (options->port == -1) options->port = 0; /* Filled in ssh_connect. */ if (options->address_family == -1) options->address_family = AF_UNSPEC; if (options->connection_attempts == -1) options->connection_attempts = 1; if (options->number_of_password_prompts == -1) options->number_of_password_prompts = 3; /* options->hostkeyalgorithms, default set in myproposals.h */ if (options->add_keys_to_agent == -1) { options->add_keys_to_agent = 0; options->add_keys_to_agent_lifespan = 0; } if (options->num_identity_files == 0) { add_identity_file(options, "~/", _PATH_SSH_CLIENT_ID_RSA, 0); #ifdef OPENSSL_HAS_ECC add_identity_file(options, "~/", _PATH_SSH_CLIENT_ID_ECDSA, 0); add_identity_file(options, "~/", _PATH_SSH_CLIENT_ID_ECDSA_SK, 0); #endif add_identity_file(options, "~/", _PATH_SSH_CLIENT_ID_ED25519, 0); add_identity_file(options, "~/", _PATH_SSH_CLIENT_ID_ED25519_SK, 0); add_identity_file(options, "~/", _PATH_SSH_CLIENT_ID_XMSS, 0); add_identity_file(options, "~/", _PATH_SSH_CLIENT_ID_DSA, 0); } if (options->escape_char == -1) options->escape_char = '~'; if (options->num_system_hostfiles == 0) { options->system_hostfiles[options->num_system_hostfiles++] = xstrdup(_PATH_SSH_SYSTEM_HOSTFILE); options->system_hostfiles[options->num_system_hostfiles++] = xstrdup(_PATH_SSH_SYSTEM_HOSTFILE2); } if (options->update_hostkeys == -1) { if (options->verify_host_key_dns <= 0 && (options->num_user_hostfiles == 0 || (options->num_user_hostfiles == 1 && strcmp(options-> user_hostfiles[0], _PATH_SSH_USER_HOSTFILE) == 0))) options->update_hostkeys = SSH_UPDATE_HOSTKEYS_YES; else options->update_hostkeys = SSH_UPDATE_HOSTKEYS_NO; } if (options->num_user_hostfiles == 0) { options->user_hostfiles[options->num_user_hostfiles++] = xstrdup(_PATH_SSH_USER_HOSTFILE); options->user_hostfiles[options->num_user_hostfiles++] = xstrdup(_PATH_SSH_USER_HOSTFILE2); } if (options->log_level == SYSLOG_LEVEL_NOT_SET) options->log_level = SYSLOG_LEVEL_INFO; if (options->log_facility == SYSLOG_FACILITY_NOT_SET) options->log_facility = SYSLOG_FACILITY_USER; if (options->no_host_authentication_for_localhost == - 1) options->no_host_authentication_for_localhost = 0; if (options->identities_only == -1) options->identities_only = 0; if (options->enable_ssh_keysign == -1) options->enable_ssh_keysign = 0; if (options->rekey_limit == -1) options->rekey_limit = 0; if (options->rekey_interval == -1) options->rekey_interval = 0; if (options->verify_host_key_dns == -1) options->verify_host_key_dns = 0; if (options->server_alive_interval == -1) options->server_alive_interval = 0; if (options->server_alive_count_max == -1) options->server_alive_count_max = 3; if (options->control_master == -1) options->control_master = 0; if (options->control_persist == -1) { options->control_persist = 0; options->control_persist_timeout = 0; } if (options->hash_known_hosts == -1) options->hash_known_hosts = 0; if (options->tun_open == -1) options->tun_open = SSH_TUNMODE_NO; if (options->tun_local == -1) options->tun_local = SSH_TUNID_ANY; if (options->tun_remote == -1) options->tun_remote = SSH_TUNID_ANY; if (options->permit_local_command == -1) options->permit_local_command = 0; if (options->visual_host_key == -1) options->visual_host_key = 0; if (options->ip_qos_interactive == -1) options->ip_qos_interactive = IPTOS_DSCP_AF21; if (options->ip_qos_bulk == -1) options->ip_qos_bulk = IPTOS_DSCP_CS1; if (options->request_tty == -1) options->request_tty = REQUEST_TTY_AUTO; if (options->session_type == -1) options->session_type = SESSION_TYPE_DEFAULT; if (options->stdin_null == -1) options->stdin_null = 0; if (options->fork_after_authentication == -1) options->fork_after_authentication = 0; if (options->proxy_use_fdpass == -1) options->proxy_use_fdpass = 0; if (options->canonicalize_max_dots == -1) options->canonicalize_max_dots = 1; if (options->canonicalize_fallback_local == -1) options->canonicalize_fallback_local = 1; if (options->canonicalize_hostname == -1) options->canonicalize_hostname = SSH_CANONICALISE_NO; if (options->fingerprint_hash == -1) options->fingerprint_hash = SSH_FP_HASH_DEFAULT; #ifdef ENABLE_SK_INTERNAL if (options->sk_provider == NULL) options->sk_provider = xstrdup("internal"); #else if (options->sk_provider == NULL) options->sk_provider = xstrdup("$SSH_SK_PROVIDER"); #endif if (options->required_rsa_size == -1) options->required_rsa_size = SSH_RSA_MINIMUM_MODULUS_SIZE; if (options->enable_escape_commandline == -1) options->enable_escape_commandline = 0; + if (options->obscure_keystroke_timing_interval == -1) { + options->obscure_keystroke_timing_interval = + SSH_KEYSTROKE_DEFAULT_INTERVAL_MS; + } /* Expand KEX name lists */ all_cipher = cipher_alg_list(',', 0); all_mac = mac_alg_list(','); all_kex = kex_alg_list(','); all_key = sshkey_alg_list(0, 0, 1, ','); all_sig = sshkey_alg_list(0, 1, 1, ','); /* remove unsupported algos from default lists */ def_cipher = match_filter_allowlist(KEX_CLIENT_ENCRYPT, all_cipher); def_mac = match_filter_allowlist(KEX_CLIENT_MAC, all_mac); def_kex = match_filter_allowlist(KEX_CLIENT_KEX, all_kex); def_key = match_filter_allowlist(KEX_DEFAULT_PK_ALG, all_key); def_sig = match_filter_allowlist(SSH_ALLOWED_CA_SIGALGS, all_sig); #define ASSEMBLE(what, defaults, all) \ do { \ if ((r = kex_assemble_names(&options->what, \ defaults, all)) != 0) { \ error_fr(r, "%s", #what); \ goto fail; \ } \ } while (0) ASSEMBLE(ciphers, def_cipher, all_cipher); ASSEMBLE(macs, def_mac, all_mac); ASSEMBLE(kex_algorithms, def_kex, all_kex); ASSEMBLE(hostbased_accepted_algos, def_key, all_key); ASSEMBLE(pubkey_accepted_algos, def_key, all_key); ASSEMBLE(ca_sign_algorithms, def_sig, all_sig); #undef ASSEMBLE #define CLEAR_ON_NONE(v) \ do { \ if (option_clear_or_none(v)) { \ free(v); \ v = NULL; \ } \ } while(0) CLEAR_ON_NONE(options->local_command); CLEAR_ON_NONE(options->remote_command); CLEAR_ON_NONE(options->proxy_command); CLEAR_ON_NONE(options->control_path); CLEAR_ON_NONE(options->revoked_host_keys); CLEAR_ON_NONE(options->pkcs11_provider); CLEAR_ON_NONE(options->sk_provider); CLEAR_ON_NONE(options->known_hosts_command); if (options->jump_host != NULL && strcmp(options->jump_host, "none") == 0 && options->jump_port == 0 && options->jump_user == NULL) { free(options->jump_host); options->jump_host = NULL; } if (options->num_permitted_cnames == 1 && !config_has_permitted_cnames(options)) { /* clean up CanonicalizePermittedCNAMEs=none */ free(options->permitted_cnames[0].source_list); free(options->permitted_cnames[0].target_list); memset(options->permitted_cnames, '\0', sizeof(*options->permitted_cnames)); options->num_permitted_cnames = 0; } /* options->identity_agent distinguishes NULL from 'none' */ /* options->user will be set in the main program if appropriate */ /* options->hostname will be set in the main program if appropriate */ /* options->host_key_alias should not be set by default */ /* options->preferred_authentications will be set in ssh */ /* success */ ret = 0; fail: free(all_cipher); free(all_mac); free(all_kex); free(all_key); free(all_sig); free(def_cipher); free(def_mac); free(def_kex); free(def_key); free(def_sig); return ret; } void free_options(Options *o) { int i; if (o == NULL) return; #define FREE_ARRAY(type, n, a) \ do { \ type _i; \ for (_i = 0; _i < (n); _i++) \ free((a)[_i]); \ } while (0) free(o->forward_agent_sock_path); free(o->xauth_location); FREE_ARRAY(u_int, o->num_log_verbose, o->log_verbose); free(o->log_verbose); free(o->ciphers); free(o->macs); free(o->hostkeyalgorithms); free(o->kex_algorithms); free(o->ca_sign_algorithms); free(o->hostname); free(o->host_key_alias); free(o->proxy_command); free(o->user); FREE_ARRAY(u_int, o->num_system_hostfiles, o->system_hostfiles); FREE_ARRAY(u_int, o->num_user_hostfiles, o->user_hostfiles); free(o->preferred_authentications); free(o->bind_address); free(o->bind_interface); free(o->pkcs11_provider); free(o->sk_provider); for (i = 0; i < o->num_identity_files; i++) { free(o->identity_files[i]); sshkey_free(o->identity_keys[i]); } for (i = 0; i < o->num_certificate_files; i++) { free(o->certificate_files[i]); sshkey_free(o->certificates[i]); } free(o->identity_agent); for (i = 0; i < o->num_local_forwards; i++) { free(o->local_forwards[i].listen_host); free(o->local_forwards[i].listen_path); free(o->local_forwards[i].connect_host); free(o->local_forwards[i].connect_path); } free(o->local_forwards); for (i = 0; i < o->num_remote_forwards; i++) { free(o->remote_forwards[i].listen_host); free(o->remote_forwards[i].listen_path); free(o->remote_forwards[i].connect_host); free(o->remote_forwards[i].connect_path); } free(o->remote_forwards); free(o->stdio_forward_host); FREE_ARRAY(u_int, o->num_send_env, o->send_env); free(o->send_env); FREE_ARRAY(u_int, o->num_setenv, o->setenv); free(o->setenv); free(o->control_path); free(o->local_command); free(o->remote_command); FREE_ARRAY(int, o->num_canonical_domains, o->canonical_domains); for (i = 0; i < o->num_permitted_cnames; i++) { free(o->permitted_cnames[i].source_list); free(o->permitted_cnames[i].target_list); } free(o->revoked_host_keys); free(o->hostbased_accepted_algos); free(o->pubkey_accepted_algos); free(o->jump_user); free(o->jump_host); free(o->jump_extra); free(o->ignored_unknown); explicit_bzero(o, sizeof(*o)); #undef FREE_ARRAY } struct fwdarg { char *arg; int ispath; }; /* * parse_fwd_field * parses the next field in a port forwarding specification. * sets fwd to the parsed field and advances p past the colon * or sets it to NULL at end of string. * returns 0 on success, else non-zero. */ static int parse_fwd_field(char **p, struct fwdarg *fwd) { char *ep, *cp = *p; int ispath = 0; if (*cp == '\0') { *p = NULL; return -1; /* end of string */ } /* * A field escaped with square brackets is used literally. * XXX - allow ']' to be escaped via backslash? */ if (*cp == '[') { /* find matching ']' */ for (ep = cp + 1; *ep != ']' && *ep != '\0'; ep++) { if (*ep == '/') ispath = 1; } /* no matching ']' or not at end of field. */ if (ep[0] != ']' || (ep[1] != ':' && ep[1] != '\0')) return -1; /* NUL terminate the field and advance p past the colon */ *ep++ = '\0'; if (*ep != '\0') *ep++ = '\0'; fwd->arg = cp + 1; fwd->ispath = ispath; *p = ep; return 0; } for (cp = *p; *cp != '\0'; cp++) { switch (*cp) { case '\\': memmove(cp, cp + 1, strlen(cp + 1) + 1); if (*cp == '\0') return -1; break; case '/': ispath = 1; break; case ':': *cp++ = '\0'; goto done; } } done: fwd->arg = *p; fwd->ispath = ispath; *p = cp; return 0; } /* * parse_forward * parses a string containing a port forwarding specification of the form: * dynamicfwd == 0 * [listenhost:]listenport|listenpath:connecthost:connectport|connectpath * listenpath:connectpath * dynamicfwd == 1 * [listenhost:]listenport * returns number of arguments parsed or zero on error */ int parse_forward(struct Forward *fwd, const char *fwdspec, int dynamicfwd, int remotefwd) { struct fwdarg fwdargs[4]; char *p, *cp; int i, err; memset(fwd, 0, sizeof(*fwd)); memset(fwdargs, 0, sizeof(fwdargs)); /* * We expand environment variables before checking if we think they're * paths so that if ${VAR} expands to a fully qualified path it is * treated as a path. */ cp = p = dollar_expand(&err, fwdspec); if (p == NULL || err) return 0; /* skip leading spaces */ while (isspace((u_char)*cp)) cp++; for (i = 0; i < 4; ++i) { if (parse_fwd_field(&cp, &fwdargs[i]) != 0) break; } /* Check for trailing garbage */ if (cp != NULL && *cp != '\0') { i = 0; /* failure */ } switch (i) { case 1: if (fwdargs[0].ispath) { fwd->listen_path = xstrdup(fwdargs[0].arg); fwd->listen_port = PORT_STREAMLOCAL; } else { fwd->listen_host = NULL; fwd->listen_port = a2port(fwdargs[0].arg); } fwd->connect_host = xstrdup("socks"); break; case 2: if (fwdargs[0].ispath && fwdargs[1].ispath) { fwd->listen_path = xstrdup(fwdargs[0].arg); fwd->listen_port = PORT_STREAMLOCAL; fwd->connect_path = xstrdup(fwdargs[1].arg); fwd->connect_port = PORT_STREAMLOCAL; } else if (fwdargs[1].ispath) { fwd->listen_host = NULL; fwd->listen_port = a2port(fwdargs[0].arg); fwd->connect_path = xstrdup(fwdargs[1].arg); fwd->connect_port = PORT_STREAMLOCAL; } else { fwd->listen_host = xstrdup(fwdargs[0].arg); fwd->listen_port = a2port(fwdargs[1].arg); fwd->connect_host = xstrdup("socks"); } break; case 3: if (fwdargs[0].ispath) { fwd->listen_path = xstrdup(fwdargs[0].arg); fwd->listen_port = PORT_STREAMLOCAL; fwd->connect_host = xstrdup(fwdargs[1].arg); fwd->connect_port = a2port(fwdargs[2].arg); } else if (fwdargs[2].ispath) { fwd->listen_host = xstrdup(fwdargs[0].arg); fwd->listen_port = a2port(fwdargs[1].arg); fwd->connect_path = xstrdup(fwdargs[2].arg); fwd->connect_port = PORT_STREAMLOCAL; } else { fwd->listen_host = NULL; fwd->listen_port = a2port(fwdargs[0].arg); fwd->connect_host = xstrdup(fwdargs[1].arg); fwd->connect_port = a2port(fwdargs[2].arg); } break; case 4: fwd->listen_host = xstrdup(fwdargs[0].arg); fwd->listen_port = a2port(fwdargs[1].arg); fwd->connect_host = xstrdup(fwdargs[2].arg); fwd->connect_port = a2port(fwdargs[3].arg); break; default: i = 0; /* failure */ } free(p); if (dynamicfwd) { if (!(i == 1 || i == 2)) goto fail_free; } else { if (!(i == 3 || i == 4)) { if (fwd->connect_path == NULL && fwd->listen_path == NULL) goto fail_free; } if (fwd->connect_port <= 0 && fwd->connect_path == NULL) goto fail_free; } if ((fwd->listen_port < 0 && fwd->listen_path == NULL) || (!remotefwd && fwd->listen_port == 0)) goto fail_free; if (fwd->connect_host != NULL && strlen(fwd->connect_host) >= NI_MAXHOST) goto fail_free; /* * XXX - if connecting to a remote socket, max sun len may not * match this host */ if (fwd->connect_path != NULL && strlen(fwd->connect_path) >= PATH_MAX_SUN) goto fail_free; if (fwd->listen_host != NULL && strlen(fwd->listen_host) >= NI_MAXHOST) goto fail_free; if (fwd->listen_path != NULL && strlen(fwd->listen_path) >= PATH_MAX_SUN) goto fail_free; return (i); fail_free: free(fwd->connect_host); fwd->connect_host = NULL; free(fwd->connect_path); fwd->connect_path = NULL; free(fwd->listen_host); fwd->listen_host = NULL; free(fwd->listen_path); fwd->listen_path = NULL; return (0); } int parse_jump(const char *s, Options *o, int active) { char *orig, *sdup, *cp; char *host = NULL, *user = NULL; int r, ret = -1, port = -1, first; active &= o->proxy_command == NULL && o->jump_host == NULL; orig = sdup = xstrdup(s); /* Remove comment and trailing whitespace */ if ((cp = strchr(orig, '#')) != NULL) *cp = '\0'; rtrim(orig); first = active; do { if (strcasecmp(s, "none") == 0) break; if ((cp = strrchr(sdup, ',')) == NULL) cp = sdup; /* last */ else *cp++ = '\0'; if (first) { /* First argument and configuration is active */ r = parse_ssh_uri(cp, &user, &host, &port); if (r == -1 || (r == 1 && parse_user_host_port(cp, &user, &host, &port) != 0)) goto out; } else { /* Subsequent argument or inactive configuration */ r = parse_ssh_uri(cp, NULL, NULL, NULL); if (r == -1 || (r == 1 && parse_user_host_port(cp, NULL, NULL, NULL) != 0)) goto out; } first = 0; /* only check syntax for subsequent hosts */ } while (cp != sdup); /* success */ if (active) { if (strcasecmp(s, "none") == 0) { o->jump_host = xstrdup("none"); o->jump_port = 0; } else { o->jump_user = user; o->jump_host = host; o->jump_port = port; o->proxy_command = xstrdup("none"); user = host = NULL; if ((cp = strrchr(s, ',')) != NULL && cp != s) { o->jump_extra = xstrdup(s); o->jump_extra[cp - s] = '\0'; } } } ret = 0; out: free(orig); free(user); free(host); return ret; } int parse_ssh_uri(const char *uri, char **userp, char **hostp, int *portp) { char *user = NULL, *host = NULL, *path = NULL; int r, port; r = parse_uri("ssh", uri, &user, &host, &port, &path); if (r == 0 && path != NULL) r = -1; /* path not allowed */ if (r == 0) { if (userp != NULL) { *userp = user; user = NULL; } if (hostp != NULL) { *hostp = host; host = NULL; } if (portp != NULL) *portp = port; } free(user); free(host); free(path); return r; } /* XXX the following is a near-vebatim copy from servconf.c; refactor */ static const char * fmt_multistate_int(int val, const struct multistate *m) { u_int i; for (i = 0; m[i].key != NULL; i++) { if (m[i].value == val) return m[i].key; } return "UNKNOWN"; } static const char * fmt_intarg(OpCodes code, int val) { if (val == -1) return "unset"; switch (code) { case oAddressFamily: return fmt_multistate_int(val, multistate_addressfamily); case oVerifyHostKeyDNS: case oUpdateHostkeys: return fmt_multistate_int(val, multistate_yesnoask); case oStrictHostKeyChecking: return fmt_multistate_int(val, multistate_strict_hostkey); case oControlMaster: return fmt_multistate_int(val, multistate_controlmaster); case oTunnel: return fmt_multistate_int(val, multistate_tunnel); case oRequestTTY: return fmt_multistate_int(val, multistate_requesttty); case oSessionType: return fmt_multistate_int(val, multistate_sessiontype); case oCanonicalizeHostname: return fmt_multistate_int(val, multistate_canonicalizehostname); case oAddKeysToAgent: return fmt_multistate_int(val, multistate_yesnoaskconfirm); case oPubkeyAuthentication: return fmt_multistate_int(val, multistate_pubkey_auth); case oFingerprintHash: return ssh_digest_alg_name(val); default: switch (val) { case 0: return "no"; case 1: return "yes"; default: return "UNKNOWN"; } } } static const char * lookup_opcode_name(OpCodes code) { u_int i; for (i = 0; keywords[i].name != NULL; i++) if (keywords[i].opcode == code) return(keywords[i].name); return "UNKNOWN"; } static void dump_cfg_int(OpCodes code, int val) { + if (code == oObscureKeystrokeTiming) { + if (val == 0) { + printf("%s no\n", lookup_opcode_name(code)); + return; + } else if (val == SSH_KEYSTROKE_DEFAULT_INTERVAL_MS) { + printf("%s yes\n", lookup_opcode_name(code)); + return; + } + /* FALLTHROUGH */ + } printf("%s %d\n", lookup_opcode_name(code), val); } static void dump_cfg_fmtint(OpCodes code, int val) { printf("%s %s\n", lookup_opcode_name(code), fmt_intarg(code, val)); } static void dump_cfg_string(OpCodes code, const char *val) { if (val == NULL) return; printf("%s %s\n", lookup_opcode_name(code), val); } static void dump_cfg_strarray(OpCodes code, u_int count, char **vals) { u_int i; for (i = 0; i < count; i++) printf("%s %s\n", lookup_opcode_name(code), vals[i]); } static void dump_cfg_strarray_oneline(OpCodes code, u_int count, char **vals) { u_int i; printf("%s", lookup_opcode_name(code)); if (count == 0) printf(" none"); for (i = 0; i < count; i++) printf(" %s", vals[i]); printf("\n"); } static void dump_cfg_forwards(OpCodes code, u_int count, const struct Forward *fwds) { const struct Forward *fwd; u_int i; /* oDynamicForward */ for (i = 0; i < count; i++) { fwd = &fwds[i]; if (code == oDynamicForward && fwd->connect_host != NULL && strcmp(fwd->connect_host, "socks") != 0) continue; if (code == oLocalForward && fwd->connect_host != NULL && strcmp(fwd->connect_host, "socks") == 0) continue; printf("%s", lookup_opcode_name(code)); if (fwd->listen_port == PORT_STREAMLOCAL) printf(" %s", fwd->listen_path); else if (fwd->listen_host == NULL) printf(" %d", fwd->listen_port); else { printf(" [%s]:%d", fwd->listen_host, fwd->listen_port); } if (code != oDynamicForward) { if (fwd->connect_port == PORT_STREAMLOCAL) printf(" %s", fwd->connect_path); else if (fwd->connect_host == NULL) printf(" %d", fwd->connect_port); else { printf(" [%s]:%d", fwd->connect_host, fwd->connect_port); } } printf("\n"); } } void dump_client_config(Options *o, const char *host) { int i, r; char buf[8], *all_key; /* * Expand HostKeyAlgorithms name lists. This isn't handled in * fill_default_options() like the other algorithm lists because * the host key algorithms are by default dynamically chosen based * on the host's keys found in known_hosts. */ all_key = sshkey_alg_list(0, 0, 1, ','); if ((r = kex_assemble_names(&o->hostkeyalgorithms, kex_default_pk_alg(), all_key)) != 0) fatal_fr(r, "expand HostKeyAlgorithms"); free(all_key); /* Most interesting options first: user, host, port */ dump_cfg_string(oHost, o->host_arg); dump_cfg_string(oUser, o->user); dump_cfg_string(oHostname, host); dump_cfg_int(oPort, o->port); /* Flag options */ dump_cfg_fmtint(oAddressFamily, o->address_family); dump_cfg_fmtint(oBatchMode, o->batch_mode); dump_cfg_fmtint(oCanonicalizeFallbackLocal, o->canonicalize_fallback_local); dump_cfg_fmtint(oCanonicalizeHostname, o->canonicalize_hostname); dump_cfg_fmtint(oCheckHostIP, o->check_host_ip); dump_cfg_fmtint(oCompression, o->compression); dump_cfg_fmtint(oControlMaster, o->control_master); dump_cfg_fmtint(oEnableSSHKeysign, o->enable_ssh_keysign); dump_cfg_fmtint(oClearAllForwardings, o->clear_forwardings); dump_cfg_fmtint(oExitOnForwardFailure, o->exit_on_forward_failure); dump_cfg_fmtint(oFingerprintHash, o->fingerprint_hash); dump_cfg_fmtint(oForwardX11, o->forward_x11); dump_cfg_fmtint(oForwardX11Trusted, o->forward_x11_trusted); dump_cfg_fmtint(oGatewayPorts, o->fwd_opts.gateway_ports); #ifdef GSSAPI dump_cfg_fmtint(oGssAuthentication, o->gss_authentication); dump_cfg_fmtint(oGssDelegateCreds, o->gss_deleg_creds); #endif /* GSSAPI */ dump_cfg_fmtint(oHashKnownHosts, o->hash_known_hosts); dump_cfg_fmtint(oHostbasedAuthentication, o->hostbased_authentication); dump_cfg_fmtint(oIdentitiesOnly, o->identities_only); dump_cfg_fmtint(oKbdInteractiveAuthentication, o->kbd_interactive_authentication); dump_cfg_fmtint(oNoHostAuthenticationForLocalhost, o->no_host_authentication_for_localhost); dump_cfg_fmtint(oPasswordAuthentication, o->password_authentication); dump_cfg_fmtint(oPermitLocalCommand, o->permit_local_command); dump_cfg_fmtint(oProxyUseFdpass, o->proxy_use_fdpass); dump_cfg_fmtint(oPubkeyAuthentication, o->pubkey_authentication); dump_cfg_fmtint(oRequestTTY, o->request_tty); dump_cfg_fmtint(oSessionType, o->session_type); dump_cfg_fmtint(oStdinNull, o->stdin_null); dump_cfg_fmtint(oForkAfterAuthentication, o->fork_after_authentication); dump_cfg_fmtint(oStreamLocalBindUnlink, o->fwd_opts.streamlocal_bind_unlink); dump_cfg_fmtint(oStrictHostKeyChecking, o->strict_host_key_checking); dump_cfg_fmtint(oTCPKeepAlive, o->tcp_keep_alive); dump_cfg_fmtint(oTunnel, o->tun_open); dump_cfg_fmtint(oVerifyHostKeyDNS, o->verify_host_key_dns); dump_cfg_fmtint(oVisualHostKey, o->visual_host_key); dump_cfg_fmtint(oUpdateHostkeys, o->update_hostkeys); dump_cfg_fmtint(oEnableEscapeCommandline, o->enable_escape_commandline); /* Integer options */ dump_cfg_int(oCanonicalizeMaxDots, o->canonicalize_max_dots); dump_cfg_int(oConnectionAttempts, o->connection_attempts); dump_cfg_int(oForwardX11Timeout, o->forward_x11_timeout); dump_cfg_int(oNumberOfPasswordPrompts, o->number_of_password_prompts); dump_cfg_int(oServerAliveCountMax, o->server_alive_count_max); dump_cfg_int(oServerAliveInterval, o->server_alive_interval); dump_cfg_int(oRequiredRSASize, o->required_rsa_size); + dump_cfg_int(oObscureKeystrokeTiming, + o->obscure_keystroke_timing_interval); /* String options */ dump_cfg_string(oBindAddress, o->bind_address); dump_cfg_string(oBindInterface, o->bind_interface); dump_cfg_string(oCiphers, o->ciphers); dump_cfg_string(oControlPath, o->control_path); dump_cfg_string(oHostKeyAlgorithms, o->hostkeyalgorithms); dump_cfg_string(oHostKeyAlias, o->host_key_alias); dump_cfg_string(oHostbasedAcceptedAlgorithms, o->hostbased_accepted_algos); dump_cfg_string(oIdentityAgent, o->identity_agent); dump_cfg_string(oIgnoreUnknown, o->ignored_unknown); dump_cfg_string(oKbdInteractiveDevices, o->kbd_interactive_devices); dump_cfg_string(oKexAlgorithms, o->kex_algorithms); dump_cfg_string(oCASignatureAlgorithms, o->ca_sign_algorithms); dump_cfg_string(oLocalCommand, o->local_command); dump_cfg_string(oRemoteCommand, o->remote_command); dump_cfg_string(oLogLevel, log_level_name(o->log_level)); dump_cfg_string(oMacs, o->macs); #ifdef ENABLE_PKCS11 dump_cfg_string(oPKCS11Provider, o->pkcs11_provider); #endif dump_cfg_string(oSecurityKeyProvider, o->sk_provider); dump_cfg_string(oPreferredAuthentications, o->preferred_authentications); dump_cfg_string(oPubkeyAcceptedAlgorithms, o->pubkey_accepted_algos); dump_cfg_string(oRevokedHostKeys, o->revoked_host_keys); dump_cfg_string(oXAuthLocation, o->xauth_location); dump_cfg_string(oKnownHostsCommand, o->known_hosts_command); dump_cfg_string(oTag, o->tag); /* Forwards */ dump_cfg_forwards(oDynamicForward, o->num_local_forwards, o->local_forwards); dump_cfg_forwards(oLocalForward, o->num_local_forwards, o->local_forwards); dump_cfg_forwards(oRemoteForward, o->num_remote_forwards, o->remote_forwards); /* String array options */ dump_cfg_strarray(oIdentityFile, o->num_identity_files, o->identity_files); dump_cfg_strarray_oneline(oCanonicalDomains, o->num_canonical_domains, o->canonical_domains); dump_cfg_strarray(oCertificateFile, o->num_certificate_files, o->certificate_files); dump_cfg_strarray_oneline(oGlobalKnownHostsFile, o->num_system_hostfiles, o->system_hostfiles); dump_cfg_strarray_oneline(oUserKnownHostsFile, o->num_user_hostfiles, o->user_hostfiles); dump_cfg_strarray(oSendEnv, o->num_send_env, o->send_env); dump_cfg_strarray(oSetEnv, o->num_setenv, o->setenv); dump_cfg_strarray_oneline(oLogVerbose, o->num_log_verbose, o->log_verbose); /* Special cases */ /* PermitRemoteOpen */ if (o->num_permitted_remote_opens == 0) printf("%s any\n", lookup_opcode_name(oPermitRemoteOpen)); else dump_cfg_strarray_oneline(oPermitRemoteOpen, o->num_permitted_remote_opens, o->permitted_remote_opens); /* AddKeysToAgent */ if (o->add_keys_to_agent_lifespan <= 0) dump_cfg_fmtint(oAddKeysToAgent, o->add_keys_to_agent); else { printf("addkeystoagent%s %d\n", o->add_keys_to_agent == 3 ? " confirm" : "", o->add_keys_to_agent_lifespan); } /* oForwardAgent */ if (o->forward_agent_sock_path == NULL) dump_cfg_fmtint(oForwardAgent, o->forward_agent); else dump_cfg_string(oForwardAgent, o->forward_agent_sock_path); /* oConnectTimeout */ if (o->connection_timeout == -1) printf("connecttimeout none\n"); else dump_cfg_int(oConnectTimeout, o->connection_timeout); /* oTunnelDevice */ printf("tunneldevice"); if (o->tun_local == SSH_TUNID_ANY) printf(" any"); else printf(" %d", o->tun_local); if (o->tun_remote == SSH_TUNID_ANY) printf(":any"); else printf(":%d", o->tun_remote); printf("\n"); /* oCanonicalizePermittedCNAMEs */ printf("canonicalizePermittedcnames"); if (o->num_permitted_cnames == 0) printf(" none"); for (i = 0; i < o->num_permitted_cnames; i++) { printf(" %s:%s", o->permitted_cnames[i].source_list, o->permitted_cnames[i].target_list); } printf("\n"); /* oControlPersist */ if (o->control_persist == 0 || o->control_persist_timeout == 0) dump_cfg_fmtint(oControlPersist, o->control_persist); else dump_cfg_int(oControlPersist, o->control_persist_timeout); /* oEscapeChar */ if (o->escape_char == SSH_ESCAPECHAR_NONE) printf("escapechar none\n"); else { vis(buf, o->escape_char, VIS_WHITE, 0); printf("escapechar %s\n", buf); } /* oIPQoS */ printf("ipqos %s ", iptos2str(o->ip_qos_interactive)); printf("%s\n", iptos2str(o->ip_qos_bulk)); /* oRekeyLimit */ printf("rekeylimit %llu %d\n", (unsigned long long)o->rekey_limit, o->rekey_interval); /* oStreamLocalBindMask */ printf("streamlocalbindmask 0%o\n", o->fwd_opts.streamlocal_bind_mask); /* oLogFacility */ printf("syslogfacility %s\n", log_facility_name(o->log_facility)); /* oProxyCommand / oProxyJump */ if (o->jump_host == NULL) dump_cfg_string(oProxyCommand, o->proxy_command); else { /* Check for numeric addresses */ i = strchr(o->jump_host, ':') != NULL || strspn(o->jump_host, "1234567890.") == strlen(o->jump_host); snprintf(buf, sizeof(buf), "%d", o->jump_port); printf("proxyjump %s%s%s%s%s%s%s%s%s\n", /* optional additional jump spec */ o->jump_extra == NULL ? "" : o->jump_extra, o->jump_extra == NULL ? "" : ",", /* optional user */ o->jump_user == NULL ? "" : o->jump_user, o->jump_user == NULL ? "" : "@", /* opening [ if hostname is numeric */ i ? "[" : "", /* mandatory hostname */ o->jump_host, /* closing ] if hostname is numeric */ i ? "]" : "", /* optional port number */ o->jump_port <= 0 ? "" : ":", o->jump_port <= 0 ? "" : buf); } } diff --git a/readconf.h b/readconf.h index dfe5bab0a3ca..ce261bd63642 100644 --- a/readconf.h +++ b/readconf.h @@ -1,249 +1,255 @@ -/* $OpenBSD: readconf.h,v 1.151 2023/07/17 04:08:31 djm Exp $ */ +/* $OpenBSD: readconf.h,v 1.152 2023/08/28 03:31:16 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * Functions for reading the configuration file. * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". */ #ifndef READCONF_H #define READCONF_H /* Data structure for representing option data. */ #define SSH_MAX_HOSTS_FILES 32 #define MAX_CANON_DOMAINS 32 #define PATH_MAX_SUN (sizeof((struct sockaddr_un *)0)->sun_path) struct allowed_cname { char *source_list; char *target_list; }; typedef struct { char *host_arg; /* Host arg as specified on command line. */ int forward_agent; /* Forward authentication agent. */ char *forward_agent_sock_path; /* Optional path of the agent. */ int forward_x11; /* Forward X11 display. */ int forward_x11_timeout; /* Expiration for Cookies */ int forward_x11_trusted; /* Trust Forward X11 display. */ int exit_on_forward_failure; /* Exit if bind(2) fails for -L/-R */ char *xauth_location; /* Location for xauth program */ struct ForwardOptions fwd_opts; /* forwarding options */ int pubkey_authentication; /* Try ssh2 pubkey authentication. */ int hostbased_authentication; /* ssh2's rhosts_rsa */ int gss_authentication; /* Try GSS authentication */ int gss_deleg_creds; /* Delegate GSS credentials */ int password_authentication; /* Try password * authentication. */ int kbd_interactive_authentication; /* Try keyboard-interactive auth. */ char *kbd_interactive_devices; /* Keyboard-interactive auth devices. */ int batch_mode; /* Batch mode: do not ask for passwords. */ int check_host_ip; /* Also keep track of keys for IP address */ int strict_host_key_checking; /* Strict host key checking. */ int compression; /* Compress packets in both directions. */ int tcp_keep_alive; /* Set SO_KEEPALIVE. */ int ip_qos_interactive; /* IP ToS/DSCP/class for interactive */ int ip_qos_bulk; /* IP ToS/DSCP/class for bulk traffic */ SyslogFacility log_facility; /* Facility for system logging. */ LogLevel log_level; /* Level for logging. */ u_int num_log_verbose; /* Verbose log overrides */ char **log_verbose; int port; /* Port to connect. */ int address_family; int connection_attempts; /* Max attempts (seconds) before * giving up */ int connection_timeout; /* Max time (seconds) before * aborting connection attempt */ int number_of_password_prompts; /* Max number of password * prompts. */ char *ciphers; /* SSH2 ciphers in order of preference. */ char *macs; /* SSH2 macs in order of preference. */ char *hostkeyalgorithms; /* SSH2 server key types in order of preference. */ char *kex_algorithms; /* SSH2 kex methods in order of preference. */ char *ca_sign_algorithms; /* Allowed CA signature algorithms */ char *hostname; /* Real host to connect. */ char *tag; /* Configuration tag name. */ char *host_key_alias; /* hostname alias for .ssh/known_hosts */ char *proxy_command; /* Proxy command for connecting the host. */ char *user; /* User to log in as. */ int escape_char; /* Escape character; -2 = none */ u_int num_system_hostfiles; /* Paths for /etc/ssh/ssh_known_hosts */ char *system_hostfiles[SSH_MAX_HOSTS_FILES]; u_int num_user_hostfiles; /* Path for $HOME/.ssh/known_hosts */ char *user_hostfiles[SSH_MAX_HOSTS_FILES]; char *preferred_authentications; char *bind_address; /* local socket address for connection to sshd */ char *bind_interface; /* local interface for bind address */ char *pkcs11_provider; /* PKCS#11 provider */ char *sk_provider; /* Security key provider */ int verify_host_key_dns; /* Verify host key using DNS */ int num_identity_files; /* Number of files for RSA/DSA identities. */ char *identity_files[SSH_MAX_IDENTITY_FILES]; int identity_file_userprovided[SSH_MAX_IDENTITY_FILES]; struct sshkey *identity_keys[SSH_MAX_IDENTITY_FILES]; int num_certificate_files; /* Number of extra certificates for ssh. */ char *certificate_files[SSH_MAX_CERTIFICATE_FILES]; int certificate_file_userprovided[SSH_MAX_CERTIFICATE_FILES]; struct sshkey *certificates[SSH_MAX_CERTIFICATE_FILES]; int add_keys_to_agent; int add_keys_to_agent_lifespan; char *identity_agent; /* Optional path to ssh-agent socket */ /* Local TCP/IP forward requests. */ int num_local_forwards; struct Forward *local_forwards; /* Remote TCP/IP forward requests. */ int num_remote_forwards; struct Forward *remote_forwards; int clear_forwardings; /* Restrict remote dynamic forwarding */ char **permitted_remote_opens; u_int num_permitted_remote_opens; /* stdio forwarding (-W) host and port */ char *stdio_forward_host; int stdio_forward_port; int enable_ssh_keysign; int64_t rekey_limit; int rekey_interval; int no_host_authentication_for_localhost; int identities_only; int server_alive_interval; int server_alive_count_max; u_int num_send_env; char **send_env; u_int num_setenv; char **setenv; char *control_path; int control_master; int control_persist; /* ControlPersist flag */ int control_persist_timeout; /* ControlPersist timeout (seconds) */ int hash_known_hosts; int tun_open; /* tun(4) */ int tun_local; /* force tun device (optional) */ int tun_remote; /* force tun device (optional) */ char *local_command; int permit_local_command; char *remote_command; int visual_host_key; int request_tty; int session_type; int stdin_null; int fork_after_authentication; int proxy_use_fdpass; int num_canonical_domains; char *canonical_domains[MAX_CANON_DOMAINS]; int canonicalize_hostname; int canonicalize_max_dots; int canonicalize_fallback_local; int num_permitted_cnames; struct allowed_cname permitted_cnames[MAX_CANON_DOMAINS]; char *revoked_host_keys; int fingerprint_hash; int update_hostkeys; /* one of SSH_UPDATE_HOSTKEYS_* */ char *hostbased_accepted_algos; char *pubkey_accepted_algos; char *jump_user; char *jump_host; int jump_port; char *jump_extra; char *known_hosts_command; int required_rsa_size; /* minimum size of RSA keys */ int enable_escape_commandline; /* ~C commandline */ + int obscure_keystroke_timing_interval; char *ignored_unknown; /* Pattern list of unknown tokens to ignore */ } Options; #define SSH_PUBKEY_AUTH_NO 0x00 #define SSH_PUBKEY_AUTH_UNBOUND 0x01 #define SSH_PUBKEY_AUTH_HBOUND 0x02 #define SSH_PUBKEY_AUTH_ALL 0x03 #define SSH_CANONICALISE_NO 0 #define SSH_CANONICALISE_YES 1 #define SSH_CANONICALISE_ALWAYS 2 #define SSHCTL_MASTER_NO 0 #define SSHCTL_MASTER_YES 1 #define SSHCTL_MASTER_AUTO 2 #define SSHCTL_MASTER_ASK 3 #define SSHCTL_MASTER_AUTO_ASK 4 #define REQUEST_TTY_AUTO 0 #define REQUEST_TTY_NO 1 #define REQUEST_TTY_YES 2 #define REQUEST_TTY_FORCE 3 #define SESSION_TYPE_NONE 0 #define SESSION_TYPE_SUBSYSTEM 1 #define SESSION_TYPE_DEFAULT 2 #define SSHCONF_CHECKPERM 1 /* check permissions on config file */ #define SSHCONF_USERCONF 2 /* user provided config file not system */ #define SSHCONF_FINAL 4 /* Final pass over config, after canon. */ #define SSHCONF_NEVERMATCH 8 /* Match/Host never matches; internal only */ #define SSH_UPDATE_HOSTKEYS_NO 0 #define SSH_UPDATE_HOSTKEYS_YES 1 #define SSH_UPDATE_HOSTKEYS_ASK 2 #define SSH_STRICT_HOSTKEY_OFF 0 #define SSH_STRICT_HOSTKEY_NEW 1 #define SSH_STRICT_HOSTKEY_YES 2 #define SSH_STRICT_HOSTKEY_ASK 3 +/* ObscureKeystrokes parameters */ +#define SSH_KEYSTROKE_DEFAULT_INTERVAL_MS 20 +#define SSH_KEYSTROKE_CHAFF_MIN_MS 1024 +#define SSH_KEYSTROKE_CHAFF_RNG_MS 2048 + const char *kex_default_pk_alg(void); char *ssh_connection_hash(const char *thishost, const char *host, const char *portstr, const char *user); void initialize_options(Options *); int fill_default_options(Options *); void fill_default_options_for_canonicalization(Options *); void free_options(Options *o); int process_config_line(Options *, struct passwd *, const char *, const char *, char *, const char *, int, int *, int); int read_config_file(const char *, struct passwd *, const char *, const char *, Options *, int, int *); int parse_forward(struct Forward *, const char *, int, int); int parse_jump(const char *, Options *, int); int parse_ssh_uri(const char *, char **, char **, int *); int default_ssh_port(void); int option_clear_or_none(const char *); int config_has_permitted_cnames(Options *); void dump_client_config(Options *o, const char *host); void add_local_forward(Options *, const struct Forward *); void add_remote_forward(Options *, const struct Forward *); void add_identity_file(Options *, const char *, const char *, int); void add_certificate_file(Options *, const char *, int); #endif /* READCONF_H */ diff --git a/regress/Makefile b/regress/Makefile index 5caf9b8e41dd..c21b0215a8cd 100644 --- a/regress/Makefile +++ b/regress/Makefile @@ -1,286 +1,287 @@ -# $OpenBSD: Makefile,v 1.125 2023/05/17 05:52:01 djm Exp $ +# $OpenBSD: Makefile,v 1.126 2023/09/06 23:36:09 djm Exp $ tests: prep file-tests t-exec unit REGRESS_TARGETS= t1 t2 t3 t4 t5 t6 t7 t8 t9 t10 t11 t12 # File based tests file-tests: $(REGRESS_TARGETS) # Interop tests are not run by default interop interop-tests: t-exec-interop prep: test "x${USE_VALGRIND}" = "x" || mkdir -p $(OBJ)/valgrind-out clean: for F in $(CLEANFILES); do rm -f $(OBJ)$$F; done rm -rf $(OBJ).putty distclean: clean LTESTS= connect \ proxy-connect \ sshfp-connect \ connect-privsep \ connect-uri \ proto-version \ proto-mismatch \ exit-status \ exit-status-signal \ envpass \ transfer \ banner \ rekey \ dhgex \ stderr-data \ stderr-after-eof \ broken-pipe \ try-ciphers \ yes-head \ login-timeout \ agent \ agent-getpeereid \ agent-timeout \ agent-ptrace \ agent-subprocess \ keyscan \ keygen-change \ keygen-comment \ keygen-convert \ keygen-knownhosts \ keygen-moduli \ keygen-sshfp \ key-options \ scp \ scp3 \ scp-uri \ sftp \ sftp-chroot \ sftp-cmds \ sftp-badcmds \ sftp-batch \ sftp-glob \ sftp-perm \ sftp-uri \ reconfigure \ dynamic-forward \ forwarding \ multiplex \ reexec \ brokenkeys \ sshcfgparse \ cfgparse \ cfgmatch \ cfgmatchlisten \ percent \ addrmatch \ localcommand \ forcecommand \ portnum \ keytype \ kextype \ cert-hostkey \ cert-userkey \ host-expand \ keys-command \ forward-control \ integrity \ krl \ multipubkey \ limit-keytype \ hostkey-agent \ hostkey-rotate \ principals-command \ cert-file \ cfginclude \ servcfginclude \ allow-deny-users \ authinfo \ sshsig \ knownhosts \ knownhosts-command \ agent-restrict \ hostbased \ channel-timeout \ - connection-timeout + connection-timeout \ + match-subsystem INTEROP_TESTS= putty-transfer putty-ciphers putty-kex conch-ciphers #INTEROP_TESTS+=ssh-com ssh-com-client ssh-com-keygen ssh-com-sftp EXTRA_TESTS= agent-pkcs11 #EXTRA_TESTS+= cipher-speed USERNAME= ${LOGNAME} CLEANFILES= *.core actual agent-key.* authorized_keys_${USERNAME} \ authorized_keys_${USERNAME}.* \ authorized_principals_${USERNAME} \ banner.in banner.out cert_host_key* cert_user_key* \ copy.1 copy.2 data ed25519-agent ed25519-agent* \ ed25519-agent.pub ed25519 ed25519.pub empty.in \ expect failed-regress.log failed-ssh.log failed-sshd.log \ hkr.* host.ecdsa-sha2-nistp256 host.ecdsa-sha2-nistp384 \ host.ecdsa-sha2-nistp521 host.ssh-dss host.ssh-ed25519 \ host.ssh-rsa host_ca_key* host_krl_* host_revoked_* key.* \ key.dsa-* key.ecdsa-* key.ed25519-512 \ key.ed25519-512.pub key.rsa-* keys-command-args kh.* askpass \ known_hosts known_hosts-cert known_hosts.* krl-* ls.copy \ modpipe netcat no_identity_config \ pidfile putty.rsa2 ready regress.log remote_pid \ revoked-* rsa rsa-agent rsa-agent.pub rsa.pub rsa_ssh2_cr.prv \ rsa_ssh2_crnl.prv scp-ssh-wrapper.exe \ scp-ssh-wrapper.scp setuid-allowed sftp-server.log \ sftp-server.sh sftp.log ssh-log-wrapper.sh ssh.log \ ssh-agent.log ssh-add.log slow-sftp-server.sh \ ssh-rsa_oldfmt knownhosts_command \ ssh_config ssh_config.* ssh_proxy ssh_proxy_bak \ ssh_proxy_* sshd.log sshd_config sshd_config.* \ sshd_config.* sshd_proxy sshd_proxy.* sshd_proxy_bak \ sshd_proxy_orig t10.out t10.out.pub t12.out t12.out.pub \ t2.out t3.out t6.out1 t6.out2 t7.out t7.out.pub \ t8.out t8.out.pub t9.out t9.out.pub \ timestamp testdata user_*key* user_ca* user_key* # Enable all malloc(3) randomisations and checks TEST_ENV= "MALLOC_OPTIONS=CFGJRSUX" TEST_SSH_SSHKEYGEN?=ssh-keygen CPPFLAGS=-I.. t1: ${TEST_SSH_SSHKEYGEN} -if ${.CURDIR}/rsa_ssh2.prv | diff - ${.CURDIR}/rsa_openssh.prv tr '\n' '\r' <${.CURDIR}/rsa_ssh2.prv > ${.OBJDIR}/rsa_ssh2_cr.prv ${TEST_SSH_SSHKEYGEN} -if ${.OBJDIR}/rsa_ssh2_cr.prv | diff - ${.CURDIR}/rsa_openssh.prv awk '{print $$0 "\r"}' ${.CURDIR}/rsa_ssh2.prv > ${.OBJDIR}/rsa_ssh2_crnl.prv ${TEST_SSH_SSHKEYGEN} -if ${.OBJDIR}/rsa_ssh2_crnl.prv | diff - ${.CURDIR}/rsa_openssh.prv t2: cat ${.CURDIR}/rsa_openssh.prv > $(OBJ)/t2.out chmod 600 $(OBJ)/t2.out ${TEST_SSH_SSHKEYGEN} -yf $(OBJ)/t2.out | diff - ${.CURDIR}/rsa_openssh.pub t3: ${TEST_SSH_SSHKEYGEN} -ef ${.CURDIR}/rsa_openssh.pub >$(OBJ)/t3.out ${TEST_SSH_SSHKEYGEN} -if $(OBJ)/t3.out | diff - ${.CURDIR}/rsa_openssh.pub t4: ${TEST_SSH_SSHKEYGEN} -E md5 -lf ${.CURDIR}/rsa_openssh.pub |\ awk '{print $$2}' | diff - ${.CURDIR}/t4.ok t5: ${TEST_SSH_SSHKEYGEN} -Bf ${.CURDIR}/rsa_openssh.pub |\ awk '{print $$2}' | diff - ${.CURDIR}/t5.ok t6: ${TEST_SSH_SSHKEYGEN} -if ${.CURDIR}/dsa_ssh2.prv > $(OBJ)/t6.out1 ${TEST_SSH_SSHKEYGEN} -if ${.CURDIR}/dsa_ssh2.pub > $(OBJ)/t6.out2 chmod 600 $(OBJ)/t6.out1 ${TEST_SSH_SSHKEYGEN} -yf $(OBJ)/t6.out1 | diff - $(OBJ)/t6.out2 $(OBJ)/t7.out: ${TEST_SSH_SSHKEYGEN} -q -t rsa -N '' -f $@ t7: $(OBJ)/t7.out ${TEST_SSH_SSHKEYGEN} -lf $(OBJ)/t7.out > /dev/null ${TEST_SSH_SSHKEYGEN} -Bf $(OBJ)/t7.out > /dev/null $(OBJ)/t8.out: ${TEST_SSH_SSHKEYGEN} -q -t dsa -N '' -f $@ t8: $(OBJ)/t8.out ${TEST_SSH_SSHKEYGEN} -lf $(OBJ)/t8.out > /dev/null ${TEST_SSH_SSHKEYGEN} -Bf $(OBJ)/t8.out > /dev/null $(OBJ)/t9.out: ! ${TEST_SSH_SSH} -Q key-plain | grep ecdsa >/dev/null || \ ${TEST_SSH_SSHKEYGEN} -q -t ecdsa -N '' -f $@ t9: $(OBJ)/t9.out ! ${TEST_SSH_SSH} -Q key-plain | grep ecdsa >/dev/null || \ ${TEST_SSH_SSHKEYGEN} -lf $(OBJ)/t9.out > /dev/null ! ${TEST_SSH_SSH} -Q key-plain | grep ecdsa >/dev/null || \ ${TEST_SSH_SSHKEYGEN} -Bf $(OBJ)/t9.out > /dev/null $(OBJ)/t10.out: ${TEST_SSH_SSHKEYGEN} -q -t ed25519 -N '' -f $@ t10: $(OBJ)/t10.out ${TEST_SSH_SSHKEYGEN} -lf $(OBJ)/t10.out > /dev/null ${TEST_SSH_SSHKEYGEN} -Bf $(OBJ)/t10.out > /dev/null t11: ${TEST_SSH_SSHKEYGEN} -E sha256 -lf ${.CURDIR}/rsa_openssh.pub |\ awk '{print $$2}' | diff - ${.CURDIR}/t11.ok $(OBJ)/t12.out: ${TEST_SSH_SSHKEYGEN} -q -t ed25519 -N '' -C 'test-comment-1234' -f $@ t12: $(OBJ)/t12.out ${TEST_SSH_SSHKEYGEN} -lf $(OBJ)/t12.out.pub | grep test-comment-1234 >/dev/null t-exec: ${LTESTS:=.sh} @if [ "x$?" = "x" ]; then exit 0; fi; \ _started=""; test -z "${LTESTS_FROM}" && _started=1 ;\ for TEST in ""$?; do \ if [ -z "$$_started" ] ; then \ if [ "x$$TEST" = "x${LTESTS_FROM}.sh" ]; then \ _started=1; \ else \ continue; \ fi ; \ fi ; \ skip=no; \ for t in ""$${SKIP_LTESTS}; do \ if [ "x$${t}.sh" = "x$${TEST}" ]; then skip=yes; fi; \ done; \ if [ "x$${skip}" = "xno" ]; then \ echo "run test $${TEST}" ... 1>&2; \ (env SUDO="${SUDO}" TEST_ENV=${TEST_ENV} ${TEST_SHELL} ${.CURDIR}/test-exec.sh ${.OBJDIR} ${.CURDIR}/$${TEST}) || exit $$?; \ else \ echo skip test $${TEST} 1>&2; \ fi; \ done t-exec-interop: ${INTEROP_TESTS:=.sh} @if [ "x$?" = "x" ]; then exit 0; fi; \ for TEST in ""$?; do \ echo "run test $${TEST}" ... 1>&2; \ (env SUDO="${SUDO}" TEST_ENV=${TEST_ENV} ${TEST_SHELL} ${.CURDIR}/test-exec.sh ${.OBJDIR} ${.CURDIR}/$${TEST}) || exit $$?; \ done t-extra: ${EXTRA_TESTS:=.sh} @if [ "x$?" = "x" ]; then exit 0; fi; \ for TEST in ""$?; do \ echo "run test $${TEST}" ... 1>&2; \ (env SUDO="${SUDO}" TEST_ENV=${TEST_ENV} ${TEST_SHELL} ${.CURDIR}/test-exec.sh ${.OBJDIR} ${.CURDIR}/$${TEST}) || exit $$?; \ done # Not run by default interop: ${INTEROP_TARGETS} # Unit tests, built by top-level Makefile unit: set -e ; if test -z "${SKIP_UNIT}" ; then \ V="" ; \ test "x${USE_VALGRIND}" = "x" || \ V=${.CURDIR}/valgrind-unit.sh ; \ $$V ${.OBJDIR}/unittests/sshbuf/test_sshbuf ; \ $$V ${.OBJDIR}/unittests/sshkey/test_sshkey \ -d ${.CURDIR}/unittests/sshkey/testdata ; \ $$V ${.OBJDIR}/unittests/sshsig/test_sshsig \ -d ${.CURDIR}/unittests/sshsig/testdata ; \ $$V ${.OBJDIR}/unittests/authopt/test_authopt \ -d ${.CURDIR}/unittests/authopt/testdata ; \ $$V ${.OBJDIR}/unittests/bitmap/test_bitmap ; \ $$V ${.OBJDIR}/unittests/conversion/test_conversion ; \ $$V ${.OBJDIR}/unittests/kex/test_kex ; \ $$V ${.OBJDIR}/unittests/hostkeys/test_hostkeys \ -d ${.CURDIR}/unittests/hostkeys/testdata ; \ $$V ${.OBJDIR}/unittests/match/test_match ; \ $$V ${.OBJDIR}/unittests/misc/test_misc ; \ if test "x${TEST_SSH_UTF8}" = "xyes" ; then \ $$V ${.OBJDIR}/unittests/utf8/test_utf8 ; \ fi \ fi diff --git a/regress/match-subsystem.sh b/regress/match-subsystem.sh new file mode 100644 index 000000000000..0b691d8e8884 --- /dev/null +++ b/regress/match-subsystem.sh @@ -0,0 +1,90 @@ +# $OpenBSD: match-subsystem.sh,v 1.1 2023/09/06 23:36:09 djm Exp $ +# Placed in the Public Domain. + +tid="sshd_config match subsystem" + +cp $OBJ/sshd_proxy $OBJ/sshd_proxy_bak + +try_subsystem() { + _id=$1 + _subsystem=$2 + _expect=$3 + ${SSHD} -tf $OBJ/sshd_proxy || fatal "$_id: bad config" + ${SSH} -sF $OBJ/ssh_proxy somehost $_subsystem + _exit=$? + trace "$_id subsystem $_subsystem" + if [ $_exit -ne $_expect ] ; then + fail "$_id: subsystem $_subsystem exit $_exit expected $_expect" + fi + return $? +} + +# Simple case: subsystem in main config. +cp $OBJ/sshd_proxy_bak $OBJ/sshd_proxy +cat >> $OBJ/sshd_proxy << _EOF +Subsystem xxx /bin/sh -c "exit 23" +_EOF +try_subsystem "main config" xxx 23 + +# No clobber in main config. +cp $OBJ/sshd_proxy_bak $OBJ/sshd_proxy +cat >> $OBJ/sshd_proxy << _EOF +Subsystem xxx /bin/sh -c "exit 23" +Subsystem xxx /bin/sh -c "exit 24" +_EOF +try_subsystem "main config no clobber" xxx 23 + +# Subsystem in match all block +cp $OBJ/sshd_proxy_bak $OBJ/sshd_proxy +cat >> $OBJ/sshd_proxy << _EOF +Match all +Subsystem xxx /bin/sh -c "exit 21" +_EOF +try_subsystem "match all" xxx 21 + +# No clobber in match all block +cp $OBJ/sshd_proxy_bak $OBJ/sshd_proxy +cat >> $OBJ/sshd_proxy << _EOF +Match all +Subsystem xxx /bin/sh -c "exit 21" +Subsystem xxx /bin/sh -c "exit 24" +_EOF +try_subsystem "match all no clobber" xxx 21 + +# Subsystem in match user block +cp $OBJ/sshd_proxy_bak $OBJ/sshd_proxy +cat >> $OBJ/sshd_proxy << _EOF +Match user * +Subsystem xxx /bin/sh -c "exit 20" +_EOF +try_subsystem "match user" xxx 20 + +# No clobber in match user block +cp $OBJ/sshd_proxy_bak $OBJ/sshd_proxy +cat >> $OBJ/sshd_proxy << _EOF +Match user * +Subsystem xxx /bin/sh -c "exit 20" +Subsystem xxx /bin/sh -c "exit 24" +Match all +Subsystem xxx /bin/sh -c "exit 24" +_EOF +try_subsystem "match user no clobber" xxx 20 + +# Override main with match all +cp $OBJ/sshd_proxy_bak $OBJ/sshd_proxy +cat >> $OBJ/sshd_proxy << _EOF +Subsystem xxx /bin/sh -c "exit 23" +Match all +Subsystem xxx /bin/sh -c "exit 19" +_EOF +try_subsystem "match all override" xxx 19 + +# Override main with match user +cp $OBJ/sshd_proxy_bak $OBJ/sshd_proxy +cat >> $OBJ/sshd_proxy << _EOF +Subsystem xxx /bin/sh -c "exit 23" +Match user * +Subsystem xxx /bin/sh -c "exit 18" +_EOF +try_subsystem "match user override" xxx 18 + diff --git a/regress/scp.sh b/regress/scp.sh index 76c2b2a6bb56..640cf434ff67 100644 --- a/regress/scp.sh +++ b/regress/scp.sh @@ -1,195 +1,205 @@ -# $OpenBSD: scp.sh,v 1.18 2023/01/13 04:47:34 dtucker Exp $ +# $OpenBSD: scp.sh,v 1.19 2023/09/08 05:50:57 djm Exp $ # Placed in the Public Domain. tid="scp" #set -x COPY2=${OBJ}/copy2 DIR=${COPY}.dd DIR2=${COPY}.dd2 COPY3=${OBJ}/copy.glob[123] DIR3=${COPY}.dd.glob[456] DIFFOPT="-rN" # Figure out if diff does not understand "-N" if ! diff -N ${SRC}/scp.sh ${SRC}/scp.sh 2>/dev/null; then DIFFOPT="-r" fi maybe_add_scp_path_to_sshd SRC=`dirname ${SCRIPT}` cp ${SRC}/scp-ssh-wrapper.sh ${OBJ}/scp-ssh-wrapper.scp chmod 755 ${OBJ}/scp-ssh-wrapper.scp export SCP # used in scp-ssh-wrapper.scp scpclean() { rm -rf ${COPY} ${COPY2} ${DIR} ${DIR2} ${COPY3} ${DIR3} mkdir ${DIR} ${DIR2} ${DIR3} chmod 755 ${DIR} ${DIR2} ${DIR3} } +# Create directory structure for recursive copy tests. +forest() { + scpclean + rm -rf ${DIR2} + cp ${DATA} ${DIR}/copy + ln -s ${DIR}/copy ${DIR}/copy-sym + mkdir ${DIR}/subdir + cp ${DATA} ${DIR}/subdir/copy + ln -s ${DIR}/subdir ${DIR}/subdir-sym +} + for mode in scp sftp ; do tag="$tid: $mode mode" if test $mode = scp ; then scpopts="-O -q -S ${OBJ}/scp-ssh-wrapper.scp" else - scpopts="-s -D ${SFTPSERVER}" + scpopts="-qs -D ${SFTPSERVER}" fi + verbose "$tag: simple copy local file to local file" scpclean $SCP $scpopts ${DATA} ${COPY} || fail "copy failed" cmp ${DATA} ${COPY} || fail "corrupted copy" verbose "$tag: simple copy local file to remote file" scpclean $SCP $scpopts ${DATA} somehost:${COPY} || fail "copy failed" cmp ${DATA} ${COPY} || fail "corrupted copy" verbose "$tag: simple copy remote file to local file" scpclean $SCP $scpopts somehost:${DATA} ${COPY} || fail "copy failed" cmp ${DATA} ${COPY} || fail "corrupted copy" verbose "$tag: copy local file to remote file in place" scpclean cp ${DATA} ${COPY} $SCP $scpopts ${COPY} somehost:${COPY} || fail "copy failed" cmp ${DATA} ${COPY} || fail "corrupted copy" verbose "$tag: copy remote file to local file in place" scpclean cp ${DATA} ${COPY} $SCP $scpopts somehost:${COPY} ${COPY} || fail "copy failed" cmp ${DATA} ${COPY} || fail "corrupted copy" verbose "$tag: copy local file to remote file clobber" scpclean cat ${DATA} ${DATA} > ${COPY} $SCP $scpopts ${DATA} somehost:${COPY} || fail "copy failed" ls -l $DATA $COPY cmp ${DATA} ${COPY} || fail "corrupted copy" verbose "$tag: copy remote file to local file clobber" scpclean cat ${DATA} ${DATA} > ${COPY} $SCP $scpopts somehost:${DATA} ${COPY} || fail "copy failed" cmp ${DATA} ${COPY} || fail "corrupted copy" verbose "$tag: simple copy local file to remote dir" scpclean cp ${DATA} ${COPY} $SCP $scpopts ${COPY} somehost:${DIR} || fail "copy failed" cmp ${COPY} ${DIR}/copy || fail "corrupted copy" verbose "$tag: simple copy local file to local dir" scpclean cp ${DATA} ${COPY} $SCP $scpopts ${COPY} ${DIR} || fail "copy failed" cmp ${COPY} ${DIR}/copy || fail "corrupted copy" verbose "$tag: simple copy remote file to local dir" scpclean cp ${DATA} ${COPY} $SCP $scpopts somehost:${COPY} ${DIR} || fail "copy failed" cmp ${COPY} ${DIR}/copy || fail "corrupted copy" verbose "$tag: recursive local dir to remote dir" - scpclean - rm -rf ${DIR2} - cp ${DATA} ${DIR}/copy + forest $SCP $scpopts -r ${DIR} somehost:${DIR2} || fail "copy failed" diff ${DIFFOPT} ${DIR} ${DIR2} || fail "corrupted copy" verbose "$tag: recursive local dir to local dir" - scpclean + forest rm -rf ${DIR2} cp ${DATA} ${DIR}/copy $SCP $scpopts -r ${DIR} ${DIR2} || fail "copy failed" diff ${DIFFOPT} ${DIR} ${DIR2} || fail "corrupted copy" verbose "$tag: recursive remote dir to local dir" - scpclean + forest rm -rf ${DIR2} cp ${DATA} ${DIR}/copy $SCP $scpopts -r somehost:${DIR} ${DIR2} || fail "copy failed" diff ${DIFFOPT} ${DIR} ${DIR2} || fail "corrupted copy" verbose "$tag: unmatched glob file local->remote" scpclean $SCP $scpopts ${DATA} somehost:${COPY3} || fail "copy failed" cmp ${DATA} ${COPY3} || fail "corrupted copy" verbose "$tag: unmatched glob file remote->local" # NB. no clean $SCP $scpopts somehost:${COPY3} ${COPY2} || fail "copy failed" cmp ${DATA} ${COPY2} || fail "corrupted copy" verbose "$tag: unmatched glob dir recursive local->remote" scpclean rm -rf ${DIR3} cp ${DATA} ${DIR}/copy cp ${DATA} ${DIR}/copy.glob[1234] $SCP $scpopts -r ${DIR} somehost:${DIR3} || fail "copy failed" diff ${DIFFOPT} ${DIR} ${DIR3} || fail "corrupted copy" verbose "$tag: unmatched glob dir recursive remote->local" # NB. no clean rm -rf ${DIR2} $SCP $scpopts -r somehost:${DIR3} ${DIR2} || fail "copy failed" diff ${DIFFOPT} ${DIR} ${DIR2} || fail "corrupted copy" verbose "$tag: shell metacharacters" scpclean (cd ${DIR} && \ touch '`touch metachartest`' && \ $SCP $scpopts *metachar* ${DIR2} 2>/dev/null; \ [ ! -f metachartest ] ) || fail "shell metacharacters" if [ ! -z "$SUDO" ]; then verbose "$tag: skipped file after scp -p with failed chown+utimes" scpclean cp -p ${DATA} ${DIR}/copy cp -p ${DATA} ${DIR}/copy2 cp ${DATA} ${DIR2}/copy chmod 660 ${DIR2}/copy $SUDO chown root ${DIR2}/copy $SCP -p $scpopts somehost:${DIR}/\* ${DIR2} >/dev/null 2>&1 $SUDO diff ${DIFFOPT} ${DIR} ${DIR2} || fail "corrupted copy" $SUDO rm ${DIR2}/copy fi for i in 0 1 2 3 4 5 6 7; do verbose "$tag: disallow bad server #$i" SCPTESTMODE=badserver_$i export DIR SCPTESTMODE scpclean $SCP $scpopts somehost:${DATA} ${DIR} >/dev/null 2>/dev/null [ -d {$DIR}/rootpathdir ] && fail "allows dir relative to root dir" [ -d ${DIR}/dotpathdir ] && fail "allows dir creation in non-recursive mode" scpclean $SCP -r $scpopts somehost:${DATA} ${DIR2} >/dev/null 2>/dev/null [ -d ${DIR}/dotpathdir ] && fail "allows dir creation outside of subdir" scpclean $SCP -pr $scpopts somehost:${DATA} ${DIR2} >/dev/null 2>/dev/null [ ! -w ${DIR2} ] && fail "allows target root attribute change" scpclean $SCP $scpopts somehost:${DATA} ${DIR2} >/dev/null 2>/dev/null [ -e ${DIR2}/extrafile ] && fail "allows unauth object creation" rm -f ${DIR2}/extrafile done verbose "$tag: detect non-directory target" scpclean echo a > ${COPY} echo b > ${COPY2} $SCP $scpopts ${DATA} ${COPY} ${COPY2} cmp ${COPY} ${COPY2} >/dev/null && fail "corrupt target" done scpclean rm -f ${OBJ}/scp-ssh-wrapper.scp diff --git a/regress/scp3.sh b/regress/scp3.sh index 383121f4519e..eeb7a9dde475 100644 --- a/regress/scp3.sh +++ b/regress/scp3.sh @@ -1,62 +1,69 @@ -# $OpenBSD: scp3.sh,v 1.4 2023/01/13 04:47:34 dtucker Exp $ +# $OpenBSD: scp3.sh,v 1.5 2023/09/08 06:10:57 djm Exp $ # Placed in the Public Domain. tid="scp3" -set -x - COPY2=${OBJ}/copy2 DIR=${COPY}.dd DIR2=${COPY}.dd2 maybe_add_scp_path_to_sshd SRC=`dirname ${SCRIPT}` cp ${SRC}/scp-ssh-wrapper.sh ${OBJ}/scp-ssh-wrapper.scp chmod 755 ${OBJ}/scp-ssh-wrapper.scp export SCP # used in scp-ssh-wrapper.scp scpclean() { rm -rf ${COPY} ${COPY2} ${DIR} ${DIR2} mkdir ${DIR} ${DIR2} chmod 755 ${DIR} ${DIR2} } +# Create directory structure for recursive copy tests. +forest() { + scpclean + rm -rf ${DIR2} + cp ${DATA} ${DIR}/copy + ln -s ${DIR}/copy ${DIR}/copy-sym + mkdir ${DIR}/subdir + cp ${DATA} ${DIR}/subdir/copy + ln -s ${DIR}/subdir ${DIR}/subdir-sym +} + for mode in scp sftp ; do scpopts="-F${OBJ}/ssh_proxy -S ${SSH} -q" tag="$tid: $mode mode" if test $mode = scp ; then scpopts="$scpopts -O" else scpopts="-s -D ${SFTPSERVER}" fi verbose "$tag: simple copy remote file to remote file" scpclean $SCP $scpopts -3 hostA:${DATA} hostB:${COPY} || fail "copy failed" cmp ${DATA} ${COPY} || fail "corrupted copy" verbose "$tag: simple copy remote file to remote dir" scpclean cp ${DATA} ${COPY} $SCP $scpopts -3 hostA:${COPY} hostB:${DIR} || fail "copy failed" cmp ${COPY} ${DIR}/copy || fail "corrupted copy" verbose "$tag: recursive remote dir to remote dir" - scpclean - rm -rf ${DIR2} - cp ${DATA} ${DIR}/copy + forest $SCP $scpopts -3r hostA:${DIR} hostB:${DIR2} || fail "copy failed" diff -r ${DIR} ${DIR2} || fail "corrupted copy" diff -r ${DIR2} ${DIR} || fail "corrupted copy" verbose "$tag: detect non-directory target" scpclean echo a > ${COPY} echo b > ${COPY2} $SCP $scpopts -3 hostA:${DATA} hostA:${COPY} hostB:${COPY2} cmp ${COPY} ${COPY2} >/dev/null && fail "corrupt target" done scpclean rm -f ${OBJ}/scp-ssh-wrapper.exe diff --git a/scp.c b/scp.c index 5edb4f07dba6..eaa407cb1de8 100644 --- a/scp.c +++ b/scp.c @@ -1,2267 +1,2267 @@ -/* $OpenBSD: scp.c,v 1.257 2023/07/14 05:31:44 djm Exp $ */ +/* $OpenBSD: scp.c,v 1.259 2023/09/10 23:12:32 djm Exp $ */ /* * scp - secure remote copy. This is basically patched BSD rcp which * uses ssh to do the data transfer (instead of using rcmd). * * NOTE: This version should NOT be suid root. (This uses ssh to * do the transfer and ssh has the necessary privileges.) * * 1995 Timo Rinne , Tatu Ylonen * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". */ /* * Copyright (c) 1999 Theo de Raadt. All rights reserved. * Copyright (c) 1999 Aaron Campbell. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * Parts from: * * Copyright (c) 1983, 1990, 1992, 1993, 1995 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include "includes.h" #include #ifdef HAVE_SYS_STAT_H # include #endif #ifdef HAVE_POLL_H #include #else # ifdef HAVE_SYS_POLL_H # include # endif #endif #ifdef HAVE_SYS_TIME_H # include #endif #include #include #include #include #include #include #ifdef HAVE_FNMATCH_H #include #endif #ifdef USE_SYSTEM_GLOB # include #else # include "openbsd-compat/glob.h" #endif #ifdef HAVE_LIBGEN_H #include #endif #include #ifdef HAVE_UTIL_H # include #endif #include #include #include #include #ifdef HAVE_STDINT_H # include #endif #include #include #include #include #include #if defined(HAVE_STRNVIS) && defined(HAVE_VIS_H) && !defined(BROKEN_STRNVIS) #include #endif #include "xmalloc.h" #include "ssh.h" #include "atomicio.h" #include "pathnames.h" #include "log.h" #include "misc.h" #include "progressmeter.h" #include "utf8.h" #include "sftp.h" #include "sftp-common.h" #include "sftp-client.h" extern char *__progname; #define COPY_BUFLEN 16384 int do_cmd(char *, char *, char *, int, int, char *, int *, int *, pid_t *); int do_cmd2(char *, char *, int, char *, int, int); /* Struct for addargs */ arglist args; arglist remote_remote_args; /* Bandwidth limit */ long long limit_kbps = 0; struct bwlimit bwlimit; /* Name of current file being transferred. */ char *curfile; /* This is set to non-zero to enable verbose mode. */ int verbose_mode = 0; LogLevel log_level = SYSLOG_LEVEL_INFO; /* This is set to zero if the progressmeter is not desired. */ int showprogress = 1; /* * This is set to non-zero if remote-remote copy should be piped * through this process. */ int throughlocal = 1; /* Non-standard port to use for the ssh connection or -1. */ int sshport = -1; /* This is the program to execute for the secured connection. ("ssh" or -S) */ char *ssh_program = _PATH_SSH_PROGRAM; /* This is used to store the pid of ssh_program */ pid_t do_cmd_pid = -1; pid_t do_cmd_pid2 = -1; /* SFTP copy parameters */ size_t sftp_copy_buflen; size_t sftp_nrequests; /* Needed for sftp */ volatile sig_atomic_t interrupted = 0; -int remote_glob(struct sftp_conn *, const char *, int, +int sftp_glob(struct sftp_conn *, const char *, int, int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */ static void killchild(int signo) { if (do_cmd_pid > 1) { kill(do_cmd_pid, signo ? signo : SIGTERM); (void)waitpid(do_cmd_pid, NULL, 0); } if (do_cmd_pid2 > 1) { kill(do_cmd_pid2, signo ? signo : SIGTERM); (void)waitpid(do_cmd_pid2, NULL, 0); } if (signo) _exit(1); exit(1); } static void suspone(int pid, int signo) { int status; if (pid > 1) { kill(pid, signo); while (waitpid(pid, &status, WUNTRACED) == -1 && errno == EINTR) ; } } static void suspchild(int signo) { suspone(do_cmd_pid, signo); suspone(do_cmd_pid2, signo); kill(getpid(), SIGSTOP); } static int do_local_cmd(arglist *a) { u_int i; int status; pid_t pid; if (a->num == 0) fatal("do_local_cmd: no arguments"); if (verbose_mode) { fprintf(stderr, "Executing:"); for (i = 0; i < a->num; i++) fmprintf(stderr, " %s", a->list[i]); fprintf(stderr, "\n"); } if ((pid = fork()) == -1) fatal("do_local_cmd: fork: %s", strerror(errno)); if (pid == 0) { execvp(a->list[0], a->list); perror(a->list[0]); exit(1); } do_cmd_pid = pid; ssh_signal(SIGTERM, killchild); ssh_signal(SIGINT, killchild); ssh_signal(SIGHUP, killchild); while (waitpid(pid, &status, 0) == -1) if (errno != EINTR) fatal("do_local_cmd: waitpid: %s", strerror(errno)); do_cmd_pid = -1; if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) return (-1); return (0); } /* * This function executes the given command as the specified user on the * given host. This returns < 0 if execution fails, and >= 0 otherwise. This * assigns the input and output file descriptors on success. */ int do_cmd(char *program, char *host, char *remuser, int port, int subsystem, char *cmd, int *fdin, int *fdout, pid_t *pid) { #ifdef USE_PIPES int pin[2], pout[2]; #else int sv[2]; #endif if (verbose_mode) fmprintf(stderr, "Executing: program %s host %s, user %s, command %s\n", program, host, remuser ? remuser : "(unspecified)", cmd); if (port == -1) port = sshport; #ifdef USE_PIPES if (pipe(pin) == -1 || pipe(pout) == -1) fatal("pipe: %s", strerror(errno)); #else /* Create a socket pair for communicating with ssh. */ if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == -1) fatal("socketpair: %s", strerror(errno)); #endif ssh_signal(SIGTSTP, suspchild); ssh_signal(SIGTTIN, suspchild); ssh_signal(SIGTTOU, suspchild); /* Fork a child to execute the command on the remote host using ssh. */ *pid = fork(); switch (*pid) { case -1: fatal("fork: %s", strerror(errno)); case 0: /* Child. */ #ifdef USE_PIPES if (dup2(pin[0], STDIN_FILENO) == -1 || dup2(pout[1], STDOUT_FILENO) == -1) { error("dup2: %s", strerror(errno)); _exit(1); } close(pin[0]); close(pin[1]); close(pout[0]); close(pout[1]); #else if (dup2(sv[0], STDIN_FILENO) == -1 || dup2(sv[0], STDOUT_FILENO) == -1) { error("dup2: %s", strerror(errno)); _exit(1); } close(sv[0]); close(sv[1]); #endif replacearg(&args, 0, "%s", program); if (port != -1) { addargs(&args, "-p"); addargs(&args, "%d", port); } if (remuser != NULL) { addargs(&args, "-l"); addargs(&args, "%s", remuser); } if (subsystem) addargs(&args, "-s"); addargs(&args, "--"); addargs(&args, "%s", host); addargs(&args, "%s", cmd); execvp(program, args.list); perror(program); _exit(1); default: /* Parent. Close the other side, and return the local side. */ #ifdef USE_PIPES close(pin[0]); close(pout[1]); *fdout = pin[1]; *fdin = pout[0]; #else close(sv[0]); *fdin = sv[1]; *fdout = sv[1]; #endif ssh_signal(SIGTERM, killchild); ssh_signal(SIGINT, killchild); ssh_signal(SIGHUP, killchild); return 0; } } /* * This function executes a command similar to do_cmd(), but expects the * input and output descriptors to be setup by a previous call to do_cmd(). * This way the input and output of two commands can be connected. */ int do_cmd2(char *host, char *remuser, int port, char *cmd, int fdin, int fdout) { int status; pid_t pid; if (verbose_mode) fmprintf(stderr, "Executing: 2nd program %s host %s, user %s, command %s\n", ssh_program, host, remuser ? remuser : "(unspecified)", cmd); if (port == -1) port = sshport; /* Fork a child to execute the command on the remote host using ssh. */ pid = fork(); if (pid == 0) { if (dup2(fdin, 0) == -1) perror("dup2"); if (dup2(fdout, 1) == -1) perror("dup2"); replacearg(&args, 0, "%s", ssh_program); if (port != -1) { addargs(&args, "-p"); addargs(&args, "%d", port); } if (remuser != NULL) { addargs(&args, "-l"); addargs(&args, "%s", remuser); } addargs(&args, "-oBatchMode=yes"); addargs(&args, "--"); addargs(&args, "%s", host); addargs(&args, "%s", cmd); execvp(ssh_program, args.list); perror(ssh_program); exit(1); } else if (pid == -1) { fatal("fork: %s", strerror(errno)); } while (waitpid(pid, &status, 0) == -1) if (errno != EINTR) fatal("do_cmd2: waitpid: %s", strerror(errno)); return 0; } typedef struct { size_t cnt; char *buf; } BUF; BUF *allocbuf(BUF *, int, int); void lostconn(int); int okname(char *); void run_err(const char *,...) __attribute__((__format__ (printf, 1, 2))) __attribute__((__nonnull__ (1))); int note_err(const char *,...) __attribute__((__format__ (printf, 1, 2))); void verifydir(char *); struct passwd *pwd; uid_t userid; int errs, remin, remout, remin2, remout2; int Tflag, pflag, iamremote, iamrecursive, targetshouldbedirectory; #define CMDNEEDS 64 char cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */ enum scp_mode_e { MODE_SCP, MODE_SFTP }; int response(void); void rsource(char *, struct stat *); void sink(int, char *[], const char *); void source(int, char *[]); void tolocal(int, char *[], enum scp_mode_e, char *sftp_direct); void toremote(int, char *[], enum scp_mode_e, char *sftp_direct); void usage(void); void source_sftp(int, char *, char *, struct sftp_conn *); void sink_sftp(int, char *, const char *, struct sftp_conn *); void throughlocal_sftp(struct sftp_conn *, struct sftp_conn *, char *, char *); int main(int argc, char **argv) { int ch, fflag, tflag, status, r, n; char **newargv, *argv0; const char *errstr; extern char *optarg; extern int optind; enum scp_mode_e mode = MODE_SFTP; char *sftp_direct = NULL; long long llv; /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ sanitise_stdfd(); msetlocale(); /* Copy argv, because we modify it */ argv0 = argv[0]; newargv = xcalloc(MAXIMUM(argc + 1, 1), sizeof(*newargv)); for (n = 0; n < argc; n++) newargv[n] = xstrdup(argv[n]); argv = newargv; __progname = ssh_get_progname(argv[0]); log_init(argv0, log_level, SYSLOG_FACILITY_USER, 2); memset(&args, '\0', sizeof(args)); memset(&remote_remote_args, '\0', sizeof(remote_remote_args)); args.list = remote_remote_args.list = NULL; addargs(&args, "%s", ssh_program); addargs(&args, "-x"); addargs(&args, "-oPermitLocalCommand=no"); addargs(&args, "-oClearAllForwardings=yes"); addargs(&args, "-oRemoteCommand=none"); addargs(&args, "-oRequestTTY=no"); fflag = Tflag = tflag = 0; while ((ch = getopt(argc, argv, "12346ABCTdfOpqRrstvD:F:J:M:P:S:c:i:l:o:X:")) != -1) { switch (ch) { /* User-visible flags. */ case '1': fatal("SSH protocol v.1 is no longer supported"); break; case '2': /* Ignored */ break; case 'A': case '4': case '6': case 'C': addargs(&args, "-%c", ch); addargs(&remote_remote_args, "-%c", ch); break; case 'D': sftp_direct = optarg; break; case '3': throughlocal = 1; break; case 'R': throughlocal = 0; break; case 'o': case 'c': case 'i': case 'F': case 'J': addargs(&remote_remote_args, "-%c", ch); addargs(&remote_remote_args, "%s", optarg); addargs(&args, "-%c", ch); addargs(&args, "%s", optarg); break; case 'O': mode = MODE_SCP; break; case 's': mode = MODE_SFTP; break; case 'P': sshport = a2port(optarg); if (sshport <= 0) fatal("bad port \"%s\"\n", optarg); break; case 'B': addargs(&remote_remote_args, "-oBatchmode=yes"); addargs(&args, "-oBatchmode=yes"); break; case 'l': limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024, &errstr); if (errstr != NULL) usage(); limit_kbps *= 1024; /* kbps */ bandwidth_limit_init(&bwlimit, limit_kbps, COPY_BUFLEN); break; case 'p': pflag = 1; break; case 'r': iamrecursive = 1; break; case 'S': ssh_program = xstrdup(optarg); break; case 'v': addargs(&args, "-v"); addargs(&remote_remote_args, "-v"); if (verbose_mode == 0) log_level = SYSLOG_LEVEL_DEBUG1; else if (log_level < SYSLOG_LEVEL_DEBUG3) log_level++; verbose_mode = 1; break; case 'q': addargs(&args, "-q"); addargs(&remote_remote_args, "-q"); showprogress = 0; break; case 'X': /* Please keep in sync with sftp.c -X */ if (strncmp(optarg, "buffer=", 7) == 0) { r = scan_scaled(optarg + 7, &llv); if (r == 0 && (llv <= 0 || llv > 256 * 1024)) { r = -1; errno = EINVAL; } if (r == -1) { fatal("Invalid buffer size \"%s\": %s", optarg + 7, strerror(errno)); } sftp_copy_buflen = (size_t)llv; } else if (strncmp(optarg, "nrequests=", 10) == 0) { llv = strtonum(optarg + 10, 1, 256 * 1024, &errstr); if (errstr != NULL) { fatal("Invalid number of requests " "\"%s\": %s", optarg + 10, errstr); } sftp_nrequests = (size_t)llv; } else { fatal("Invalid -X option"); } break; /* Server options. */ case 'd': targetshouldbedirectory = 1; break; case 'f': /* "from" */ iamremote = 1; fflag = 1; break; case 't': /* "to" */ iamremote = 1; tflag = 1; #ifdef HAVE_CYGWIN setmode(0, O_BINARY); #endif break; case 'T': Tflag = 1; break; default: usage(); } } argc -= optind; argv += optind; log_init(argv0, log_level, SYSLOG_FACILITY_USER, 2); /* Do this last because we want the user to be able to override it */ addargs(&args, "-oForwardAgent=no"); if (iamremote) mode = MODE_SCP; if ((pwd = getpwuid(userid = getuid())) == NULL) fatal("unknown user %u", (u_int) userid); if (!isatty(STDOUT_FILENO)) showprogress = 0; if (pflag) { /* Cannot pledge: -p allows setuid/setgid files... */ } else { if (pledge("stdio rpath wpath cpath fattr tty proc exec", NULL) == -1) { perror("pledge"); exit(1); } } remin = STDIN_FILENO; remout = STDOUT_FILENO; if (fflag) { /* Follow "protocol", send data. */ (void) response(); source(argc, argv); exit(errs != 0); } if (tflag) { /* Receive data. */ sink(argc, argv, NULL); exit(errs != 0); } if (argc < 2) usage(); if (argc > 2) targetshouldbedirectory = 1; remin = remout = -1; do_cmd_pid = -1; /* Command to be executed on remote system using "ssh". */ (void) snprintf(cmd, sizeof cmd, "scp%s%s%s%s", verbose_mode ? " -v" : "", iamrecursive ? " -r" : "", pflag ? " -p" : "", targetshouldbedirectory ? " -d" : ""); (void) ssh_signal(SIGPIPE, lostconn); if (colon(argv[argc - 1])) /* Dest is remote host. */ toremote(argc, argv, mode, sftp_direct); else { if (targetshouldbedirectory) verifydir(argv[argc - 1]); tolocal(argc, argv, mode, sftp_direct); /* Dest is local host. */ } /* * Finally check the exit status of the ssh process, if one was forked * and no error has occurred yet */ if (do_cmd_pid != -1 && (mode == MODE_SFTP || errs == 0)) { if (remin != -1) (void) close(remin); if (remout != -1) (void) close(remout); if (waitpid(do_cmd_pid, &status, 0) == -1) errs = 1; else { if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) errs = 1; } } exit(errs != 0); } /* Callback from atomicio6 to update progress meter and limit bandwidth */ static int scpio(void *_cnt, size_t s) { off_t *cnt = (off_t *)_cnt; *cnt += s; refresh_progress_meter(0); if (limit_kbps > 0) bandwidth_limit(&bwlimit, s); return 0; } static int do_times(int fd, int verb, const struct stat *sb) { /* strlen(2^64) == 20; strlen(10^6) == 7 */ char buf[(20 + 7 + 2) * 2 + 2]; (void)snprintf(buf, sizeof(buf), "T%llu 0 %llu 0\n", (unsigned long long) (sb->st_mtime < 0 ? 0 : sb->st_mtime), (unsigned long long) (sb->st_atime < 0 ? 0 : sb->st_atime)); if (verb) { fprintf(stderr, "File mtime %lld atime %lld\n", (long long)sb->st_mtime, (long long)sb->st_atime); fprintf(stderr, "Sending file timestamps: %s", buf); } (void) atomicio(vwrite, fd, buf, strlen(buf)); return (response()); } static int parse_scp_uri(const char *uri, char **userp, char **hostp, int *portp, char **pathp) { int r; r = parse_uri("scp", uri, userp, hostp, portp, pathp); if (r == 0 && *pathp == NULL) *pathp = xstrdup("."); return r; } /* Appends a string to an array; returns 0 on success, -1 on alloc failure */ static int append(char *cp, char ***ap, size_t *np) { char **tmp; if ((tmp = reallocarray(*ap, *np + 1, sizeof(*tmp))) == NULL) return -1; tmp[(*np)] = cp; (*np)++; *ap = tmp; return 0; } /* * Finds the start and end of the first brace pair in the pattern. * returns 0 on success or -1 for invalid patterns. */ static int find_brace(const char *pattern, int *startp, int *endp) { int i; int in_bracket, brace_level; *startp = *endp = -1; in_bracket = brace_level = 0; for (i = 0; i < INT_MAX && *endp < 0 && pattern[i] != '\0'; i++) { switch (pattern[i]) { case '\\': /* skip next character */ if (pattern[i + 1] != '\0') i++; break; case '[': in_bracket = 1; break; case ']': in_bracket = 0; break; case '{': if (in_bracket) break; if (pattern[i + 1] == '}') { /* Protect a single {}, for find(1), like csh */ i++; /* skip */ break; } if (*startp == -1) *startp = i; brace_level++; break; case '}': if (in_bracket) break; if (*startp < 0) { /* Unbalanced brace */ return -1; } if (--brace_level <= 0) *endp = i; break; } } /* unbalanced brackets/braces */ if (*endp < 0 && (*startp >= 0 || in_bracket)) return -1; return 0; } /* * Assembles and records a successfully-expanded pattern, returns -1 on * alloc failure. */ static int emit_expansion(const char *pattern, int brace_start, int brace_end, int sel_start, int sel_end, char ***patternsp, size_t *npatternsp) { char *cp; size_t pattern_len; int o = 0, tail_len; if ((pattern_len = strlen(pattern)) == 0 || pattern_len >= INT_MAX) return -1; tail_len = strlen(pattern + brace_end + 1); if ((cp = malloc(brace_start + (sel_end - sel_start) + tail_len + 1)) == NULL) return -1; /* Pattern before initial brace */ if (brace_start > 0) { memcpy(cp, pattern, brace_start); o = brace_start; } /* Current braced selection */ if (sel_end - sel_start > 0) { memcpy(cp + o, pattern + sel_start, sel_end - sel_start); o += sel_end - sel_start; } /* Remainder of pattern after closing brace */ if (tail_len > 0) { memcpy(cp + o, pattern + brace_end + 1, tail_len); o += tail_len; } cp[o] = '\0'; if (append(cp, patternsp, npatternsp) != 0) { free(cp); return -1; } return 0; } /* * Expand the first encountered brace in pattern, appending the expanded * patterns it yielded to the *patternsp array. * * Returns 0 on success or -1 on allocation failure. * * Signals whether expansion was performed via *expanded and whether * pattern was invalid via *invalid. */ static int brace_expand_one(const char *pattern, char ***patternsp, size_t *npatternsp, int *expanded, int *invalid) { int i; int in_bracket, brace_start, brace_end, brace_level; int sel_start, sel_end; *invalid = *expanded = 0; if (find_brace(pattern, &brace_start, &brace_end) != 0) { *invalid = 1; return 0; } else if (brace_start == -1) return 0; in_bracket = brace_level = 0; for (i = sel_start = brace_start + 1; i < brace_end; i++) { switch (pattern[i]) { case '{': if (in_bracket) break; brace_level++; break; case '}': if (in_bracket) break; brace_level--; break; case '[': in_bracket = 1; break; case ']': in_bracket = 0; break; case '\\': if (i < brace_end - 1) i++; /* skip */ break; } if (pattern[i] == ',' || i == brace_end - 1) { if (in_bracket || brace_level > 0) continue; /* End of a selection, emit an expanded pattern */ /* Adjust end index for last selection */ sel_end = (i == brace_end - 1) ? brace_end : i; if (emit_expansion(pattern, brace_start, brace_end, sel_start, sel_end, patternsp, npatternsp) != 0) return -1; /* move on to the next selection */ sel_start = i + 1; continue; } } if (in_bracket || brace_level > 0) { *invalid = 1; return 0; } /* success */ *expanded = 1; return 0; } /* Expand braces from pattern. Returns 0 on success, -1 on failure */ static int brace_expand(const char *pattern, char ***patternsp, size_t *npatternsp) { char *cp, *cp2, **active = NULL, **done = NULL; size_t i, nactive = 0, ndone = 0; int ret = -1, invalid = 0, expanded = 0; *patternsp = NULL; *npatternsp = 0; /* Start the worklist with the original pattern */ if ((cp = strdup(pattern)) == NULL) return -1; if (append(cp, &active, &nactive) != 0) { free(cp); return -1; } while (nactive > 0) { cp = active[nactive - 1]; nactive--; if (brace_expand_one(cp, &active, &nactive, &expanded, &invalid) == -1) { free(cp); goto fail; } if (invalid) fatal_f("invalid brace pattern \"%s\"", cp); if (expanded) { /* * Current entry expanded to new entries on the * active list; discard the progenitor pattern. */ free(cp); continue; } /* * Pattern did not expand; append the finename component to * the completed list */ if ((cp2 = strrchr(cp, '/')) != NULL) *cp2++ = '\0'; else cp2 = cp; if (append(xstrdup(cp2), &done, &ndone) != 0) { free(cp); goto fail; } free(cp); } /* success */ *patternsp = done; *npatternsp = ndone; done = NULL; ndone = 0; ret = 0; fail: for (i = 0; i < nactive; i++) free(active[i]); free(active); for (i = 0; i < ndone; i++) free(done[i]); free(done); return ret; } static struct sftp_conn * do_sftp_connect(char *host, char *user, int port, char *sftp_direct, int *reminp, int *remoutp, int *pidp) { if (sftp_direct == NULL) { if (do_cmd(ssh_program, host, user, port, 1, "sftp", reminp, remoutp, pidp) < 0) return NULL; } else { freeargs(&args); addargs(&args, "sftp-server"); if (do_cmd(sftp_direct, host, NULL, -1, 0, "sftp", reminp, remoutp, pidp) < 0) return NULL; } - return do_init(*reminp, *remoutp, + return sftp_init(*reminp, *remoutp, sftp_copy_buflen, sftp_nrequests, limit_kbps); } void toremote(int argc, char **argv, enum scp_mode_e mode, char *sftp_direct) { char *suser = NULL, *host = NULL, *src = NULL; char *bp, *tuser, *thost, *targ; int sport = -1, tport = -1; struct sftp_conn *conn = NULL, *conn2 = NULL; arglist alist; int i, r, status; struct stat sb; u_int j; memset(&alist, '\0', sizeof(alist)); alist.list = NULL; /* Parse target */ r = parse_scp_uri(argv[argc - 1], &tuser, &thost, &tport, &targ); if (r == -1) { fmprintf(stderr, "%s: invalid uri\n", argv[argc - 1]); ++errs; goto out; } if (r != 0) { if (parse_user_host_path(argv[argc - 1], &tuser, &thost, &targ) == -1) { fmprintf(stderr, "%s: invalid target\n", argv[argc - 1]); ++errs; goto out; } } /* Parse source files */ for (i = 0; i < argc - 1; i++) { free(suser); free(host); free(src); r = parse_scp_uri(argv[i], &suser, &host, &sport, &src); if (r == -1) { fmprintf(stderr, "%s: invalid uri\n", argv[i]); ++errs; continue; } if (r != 0) { parse_user_host_path(argv[i], &suser, &host, &src); } if (suser != NULL && !okname(suser)) { ++errs; continue; } if (host && throughlocal) { /* extended remote to remote */ if (mode == MODE_SFTP) { if (remin == -1) { /* Connect to dest now */ conn = do_sftp_connect(thost, tuser, tport, sftp_direct, &remin, &remout, &do_cmd_pid); if (conn == NULL) { fatal("Unable to open " "destination connection"); } debug3_f("origin in %d out %d pid %ld", remin, remout, (long)do_cmd_pid); } /* * XXX remember suser/host/sport and only * reconnect if they change between arguments. * would save reconnections for cases like * scp -3 hosta:/foo hosta:/bar hostb: */ /* Connect to origin now */ conn2 = do_sftp_connect(host, suser, sport, sftp_direct, &remin2, &remout2, &do_cmd_pid2); if (conn2 == NULL) { fatal("Unable to open " "source connection"); } debug3_f("destination in %d out %d pid %ld", remin2, remout2, (long)do_cmd_pid2); throughlocal_sftp(conn2, conn, src, targ); (void) close(remin2); (void) close(remout2); remin2 = remout2 = -1; if (waitpid(do_cmd_pid2, &status, 0) == -1) ++errs; else if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) ++errs; do_cmd_pid2 = -1; continue; } else { xasprintf(&bp, "%s -f %s%s", cmd, *src == '-' ? "-- " : "", src); if (do_cmd(ssh_program, host, suser, sport, 0, bp, &remin, &remout, &do_cmd_pid) < 0) exit(1); free(bp); xasprintf(&bp, "%s -t %s%s", cmd, *targ == '-' ? "-- " : "", targ); if (do_cmd2(thost, tuser, tport, bp, remin, remout) < 0) exit(1); free(bp); (void) close(remin); (void) close(remout); remin = remout = -1; } } else if (host) { /* standard remote to remote */ /* * Second remote user is passed to first remote side * via scp command-line. Ensure it contains no obvious * shell characters. */ if (tuser != NULL && !okname(tuser)) { ++errs; continue; } if (tport != -1 && tport != SSH_DEFAULT_PORT) { /* This would require the remote support URIs */ fatal("target port not supported with two " "remote hosts and the -R option"); } freeargs(&alist); addargs(&alist, "%s", ssh_program); addargs(&alist, "-x"); addargs(&alist, "-oClearAllForwardings=yes"); addargs(&alist, "-n"); for (j = 0; j < remote_remote_args.num; j++) { addargs(&alist, "%s", remote_remote_args.list[j]); } if (sport != -1) { addargs(&alist, "-p"); addargs(&alist, "%d", sport); } if (suser) { addargs(&alist, "-l"); addargs(&alist, "%s", suser); } addargs(&alist, "--"); addargs(&alist, "%s", host); addargs(&alist, "%s", cmd); addargs(&alist, "%s", src); addargs(&alist, "%s%s%s:%s", tuser ? tuser : "", tuser ? "@" : "", thost, targ); if (do_local_cmd(&alist) != 0) errs = 1; } else { /* local to remote */ if (mode == MODE_SFTP) { /* no need to glob: already done by shell */ if (stat(argv[i], &sb) != 0) { fatal("stat local \"%s\": %s", argv[i], strerror(errno)); } if (remin == -1) { /* Connect to remote now */ conn = do_sftp_connect(thost, tuser, tport, sftp_direct, &remin, &remout, &do_cmd_pid); if (conn == NULL) { fatal("Unable to open sftp " "connection"); } } /* The protocol */ source_sftp(1, argv[i], targ, conn); continue; } /* SCP */ if (remin == -1) { xasprintf(&bp, "%s -t %s%s", cmd, *targ == '-' ? "-- " : "", targ); if (do_cmd(ssh_program, thost, tuser, tport, 0, bp, &remin, &remout, &do_cmd_pid) < 0) exit(1); if (response() < 0) exit(1); free(bp); } source(1, argv + i); } } out: if (mode == MODE_SFTP) free(conn); free(tuser); free(thost); free(targ); free(suser); free(host); free(src); } void tolocal(int argc, char **argv, enum scp_mode_e mode, char *sftp_direct) { char *bp, *host = NULL, *src = NULL, *suser = NULL; arglist alist; struct sftp_conn *conn = NULL; int i, r, sport = -1; memset(&alist, '\0', sizeof(alist)); alist.list = NULL; for (i = 0; i < argc - 1; i++) { free(suser); free(host); free(src); r = parse_scp_uri(argv[i], &suser, &host, &sport, &src); if (r == -1) { fmprintf(stderr, "%s: invalid uri\n", argv[i]); ++errs; continue; } if (r != 0) parse_user_host_path(argv[i], &suser, &host, &src); if (suser != NULL && !okname(suser)) { ++errs; continue; } if (!host) { /* Local to local. */ freeargs(&alist); addargs(&alist, "%s", _PATH_CP); if (iamrecursive) addargs(&alist, "-r"); if (pflag) addargs(&alist, "-p"); addargs(&alist, "--"); addargs(&alist, "%s", argv[i]); addargs(&alist, "%s", argv[argc-1]); if (do_local_cmd(&alist)) ++errs; continue; } /* Remote to local. */ if (mode == MODE_SFTP) { conn = do_sftp_connect(host, suser, sport, sftp_direct, &remin, &remout, &do_cmd_pid); if (conn == NULL) { error("sftp connection failed"); ++errs; continue; } /* The protocol */ sink_sftp(1, argv[argc - 1], src, conn); free(conn); (void) close(remin); (void) close(remout); remin = remout = -1; continue; } /* SCP */ xasprintf(&bp, "%s -f %s%s", cmd, *src == '-' ? "-- " : "", src); if (do_cmd(ssh_program, host, suser, sport, 0, bp, &remin, &remout, &do_cmd_pid) < 0) { free(bp); ++errs; continue; } free(bp); sink(1, argv + argc - 1, src); (void) close(remin); remin = remout = -1; } free(suser); free(host); free(src); } /* Prepare remote path, handling ~ by assuming cwd is the homedir */ static char * prepare_remote_path(struct sftp_conn *conn, const char *path) { size_t nslash; /* Handle ~ prefixed paths */ if (*path == '\0' || strcmp(path, "~") == 0) return xstrdup("."); if (*path != '~') return xstrdup(path); if (strncmp(path, "~/", 2) == 0) { if ((nslash = strspn(path + 2, "/")) == strlen(path + 2)) return xstrdup("."); return xstrdup(path + 2 + nslash); } - if (can_expand_path(conn)) - return do_expand_path(conn, path); + if (sftp_can_expand_path(conn)) + return sftp_expand_path(conn, path); /* No protocol extension */ error("server expand-path extension is required " "for ~user paths in SFTP mode"); return NULL; } void source_sftp(int argc, char *src, char *targ, struct sftp_conn *conn) { char *target = NULL, *filename = NULL, *abs_dst = NULL; int src_is_dir, target_is_dir; Attrib a; struct stat st; memset(&a, '\0', sizeof(a)); if (stat(src, &st) != 0) fatal("stat local \"%s\": %s", src, strerror(errno)); src_is_dir = S_ISDIR(st.st_mode); if ((filename = basename(src)) == NULL) fatal("basename \"%s\": %s", src, strerror(errno)); /* * No need to glob here - the local shell already took care of * the expansions */ if ((target = prepare_remote_path(conn, targ)) == NULL) cleanup_exit(255); - target_is_dir = remote_is_dir(conn, target); + target_is_dir = sftp_remote_is_dir(conn, target); if (targetshouldbedirectory && !target_is_dir) { debug("target directory \"%s\" does not exist", target); a.flags = SSH2_FILEXFER_ATTR_PERMISSIONS; a.perm = st.st_mode | 0700; /* ensure writable */ - if (do_mkdir(conn, target, &a, 1) != 0) + if (sftp_mkdir(conn, target, &a, 1) != 0) cleanup_exit(255); /* error already logged */ target_is_dir = 1; } if (target_is_dir) - abs_dst = path_append(target, filename); + abs_dst = sftp_path_append(target, filename); else { abs_dst = target; target = NULL; } debug3_f("copying local %s to remote %s", src, abs_dst); if (src_is_dir && iamrecursive) { - if (upload_dir(conn, src, abs_dst, pflag, + if (sftp_upload_dir(conn, src, abs_dst, pflag, SFTP_PROGRESS_ONLY, 0, 0, 1, 1) != 0) { error("failed to upload directory %s to %s", src, targ); errs = 1; } - } else if (do_upload(conn, src, abs_dst, pflag, 0, 0, 1) != 0) { + } else if (sftp_upload(conn, src, abs_dst, pflag, 0, 0, 1) != 0) { error("failed to upload file %s to %s", src, targ); errs = 1; } free(abs_dst); free(target); } void source(int argc, char **argv) { struct stat stb; static BUF buffer; BUF *bp; off_t i, statbytes; size_t amt, nr; int fd = -1, haderr, indx; char *last, *name, buf[PATH_MAX + 128], encname[PATH_MAX]; int len; for (indx = 0; indx < argc; ++indx) { name = argv[indx]; statbytes = 0; len = strlen(name); while (len > 1 && name[len-1] == '/') name[--len] = '\0'; if ((fd = open(name, O_RDONLY|O_NONBLOCK)) == -1) goto syserr; if (strchr(name, '\n') != NULL) { strnvis(encname, name, sizeof(encname), VIS_NL); name = encname; } if (fstat(fd, &stb) == -1) { syserr: run_err("%s: %s", name, strerror(errno)); goto next; } if (stb.st_size < 0) { run_err("%s: %s", name, "Negative file size"); goto next; } unset_nonblock(fd); switch (stb.st_mode & S_IFMT) { case S_IFREG: break; case S_IFDIR: if (iamrecursive) { rsource(name, &stb); goto next; } /* FALLTHROUGH */ default: run_err("%s: not a regular file", name); goto next; } if ((last = strrchr(name, '/')) == NULL) last = name; else ++last; curfile = last; if (pflag) { if (do_times(remout, verbose_mode, &stb) < 0) goto next; } #define FILEMODEMASK (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO) snprintf(buf, sizeof buf, "C%04o %lld %s\n", (u_int) (stb.st_mode & FILEMODEMASK), (long long)stb.st_size, last); if (verbose_mode) fmprintf(stderr, "Sending file modes: %s", buf); (void) atomicio(vwrite, remout, buf, strlen(buf)); if (response() < 0) goto next; if ((bp = allocbuf(&buffer, fd, COPY_BUFLEN)) == NULL) { next: if (fd != -1) { (void) close(fd); fd = -1; } continue; } if (showprogress) start_progress_meter(curfile, stb.st_size, &statbytes); set_nonblock(remout); for (haderr = i = 0; i < stb.st_size; i += bp->cnt) { amt = bp->cnt; if (i + (off_t)amt > stb.st_size) amt = stb.st_size - i; if (!haderr) { if ((nr = atomicio(read, fd, bp->buf, amt)) != amt) { haderr = errno; memset(bp->buf + nr, 0, amt - nr); } } /* Keep writing after error to retain sync */ if (haderr) { (void)atomicio(vwrite, remout, bp->buf, amt); memset(bp->buf, 0, amt); continue; } if (atomicio6(vwrite, remout, bp->buf, amt, scpio, &statbytes) != amt) haderr = errno; } unset_nonblock(remout); if (fd != -1) { if (close(fd) == -1 && !haderr) haderr = errno; fd = -1; } if (!haderr) (void) atomicio(vwrite, remout, "", 1); else run_err("%s: %s", name, strerror(haderr)); (void) response(); if (showprogress) stop_progress_meter(); } } void rsource(char *name, struct stat *statp) { DIR *dirp; struct dirent *dp; char *last, *vect[1], path[PATH_MAX]; if (!(dirp = opendir(name))) { run_err("%s: %s", name, strerror(errno)); return; } last = strrchr(name, '/'); if (last == NULL) last = name; else last++; if (pflag) { if (do_times(remout, verbose_mode, statp) < 0) { closedir(dirp); return; } } (void) snprintf(path, sizeof path, "D%04o %d %.1024s\n", (u_int) (statp->st_mode & FILEMODEMASK), 0, last); if (verbose_mode) fmprintf(stderr, "Entering directory: %s", path); (void) atomicio(vwrite, remout, path, strlen(path)); if (response() < 0) { closedir(dirp); return; } while ((dp = readdir(dirp)) != NULL) { if (dp->d_ino == 0) continue; if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) continue; if (strlen(name) + 1 + strlen(dp->d_name) >= sizeof(path) - 1) { run_err("%s/%s: name too long", name, dp->d_name); continue; } (void) snprintf(path, sizeof path, "%s/%s", name, dp->d_name); vect[0] = path; source(1, vect); } (void) closedir(dirp); (void) atomicio(vwrite, remout, "E\n", 2); (void) response(); } void sink_sftp(int argc, char *dst, const char *src, struct sftp_conn *conn) { char *abs_src = NULL; char *abs_dst = NULL; glob_t g; char *filename, *tmp = NULL; int i, r, err = 0, dst_is_dir; struct stat st; memset(&g, 0, sizeof(g)); /* * Here, we need remote glob as SFTP can not depend on remote shell * expansions */ if ((abs_src = prepare_remote_path(conn, src)) == NULL) { err = -1; goto out; } debug3_f("copying remote %s to local %s", abs_src, dst); - if ((r = remote_glob(conn, abs_src, GLOB_NOCHECK|GLOB_MARK, + if ((r = sftp_glob(conn, abs_src, GLOB_NOCHECK|GLOB_MARK, NULL, &g)) != 0) { if (r == GLOB_NOSPACE) error("%s: too many glob matches", src); else error("%s: %s", src, strerror(ENOENT)); err = -1; goto out; } /* Did we actually get any matches back from the glob? */ if (g.gl_matchc == 0 && g.gl_pathc == 1 && g.gl_pathv[0] != 0) { /* * If nothing matched but a path returned, then it's probably * a GLOB_NOCHECK result. Check whether the unglobbed path * exists so we can give a nice error message early. */ - if (do_stat(conn, g.gl_pathv[0], 1) == NULL) { + if (sftp_stat(conn, g.gl_pathv[0], 1, NULL) != 0) { error("%s: %s", src, strerror(ENOENT)); err = -1; goto out; } } if ((r = stat(dst, &st)) != 0) debug2_f("stat local \"%s\": %s", dst, strerror(errno)); dst_is_dir = r == 0 && S_ISDIR(st.st_mode); if (g.gl_matchc > 1 && !dst_is_dir) { if (r == 0) { error("Multiple files match pattern, but destination " "\"%s\" is not a directory", dst); err = -1; goto out; } debug2_f("creating destination \"%s\"", dst); if (mkdir(dst, 0777) != 0) { error("local mkdir \"%s\": %s", dst, strerror(errno)); err = -1; goto out; } dst_is_dir = 1; } for (i = 0; g.gl_pathv[i] && !interrupted; i++) { tmp = xstrdup(g.gl_pathv[i]); if ((filename = basename(tmp)) == NULL) { error("basename %s: %s", tmp, strerror(errno)); err = -1; goto out; } if (dst_is_dir) - abs_dst = path_append(dst, filename); + abs_dst = sftp_path_append(dst, filename); else abs_dst = xstrdup(dst); debug("Fetching %s to %s\n", g.gl_pathv[i], abs_dst); - if (globpath_is_dir(g.gl_pathv[i]) && iamrecursive) { - if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL, - pflag, SFTP_PROGRESS_ONLY, 0, 0, 1, 1) == -1) + if (sftp_globpath_is_dir(g.gl_pathv[i]) && iamrecursive) { + if (sftp_download_dir(conn, g.gl_pathv[i], abs_dst, + NULL, pflag, SFTP_PROGRESS_ONLY, 0, 0, 1, 1) == -1) err = -1; } else { - if (do_download(conn, g.gl_pathv[i], abs_dst, NULL, + if (sftp_download(conn, g.gl_pathv[i], abs_dst, NULL, pflag, 0, 0, 1) == -1) err = -1; } free(abs_dst); abs_dst = NULL; free(tmp); tmp = NULL; } out: free(abs_src); free(tmp); globfree(&g); if (err == -1) errs = 1; } #define TYPE_OVERFLOW(type, val) \ ((sizeof(type) == 4 && (val) > INT32_MAX) || \ (sizeof(type) == 8 && (val) > INT64_MAX) || \ (sizeof(type) != 4 && sizeof(type) != 8)) void sink(int argc, char **argv, const char *src) { static BUF buffer; struct stat stb; BUF *bp; off_t i; size_t j, count; int amt, exists, first, ofd; mode_t mode, omode, mask; off_t size, statbytes; unsigned long long ull; int setimes, targisdir, wrerr; char ch, *cp, *np, *targ, *why, *vect[1], buf[2048], visbuf[2048]; char **patterns = NULL; size_t n, npatterns = 0; struct timeval tv[2]; #define atime tv[0] #define mtime tv[1] #define SCREWUP(str) { why = str; goto screwup; } if (TYPE_OVERFLOW(time_t, 0) || TYPE_OVERFLOW(off_t, 0)) SCREWUP("Unexpected off_t/time_t size"); setimes = targisdir = 0; mask = umask(0); if (!pflag) (void) umask(mask); if (argc != 1) { run_err("ambiguous target"); exit(1); } targ = *argv; if (targetshouldbedirectory) verifydir(targ); (void) atomicio(vwrite, remout, "", 1); if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode)) targisdir = 1; if (src != NULL && !iamrecursive && !Tflag) { /* * Prepare to try to restrict incoming filenames to match * the requested destination file glob. */ if (brace_expand(src, &patterns, &npatterns) != 0) fatal_f("could not expand pattern"); } for (first = 1;; first = 0) { cp = buf; if (atomicio(read, remin, cp, 1) != 1) goto done; if (*cp++ == '\n') SCREWUP("unexpected "); do { if (atomicio(read, remin, &ch, sizeof(ch)) != sizeof(ch)) SCREWUP("lost connection"); *cp++ = ch; } while (cp < &buf[sizeof(buf) - 1] && ch != '\n'); *cp = 0; if (verbose_mode) fmprintf(stderr, "Sink: %s", buf); if (buf[0] == '\01' || buf[0] == '\02') { if (iamremote == 0) { (void) snmprintf(visbuf, sizeof(visbuf), NULL, "%s", buf + 1); (void) atomicio(vwrite, STDERR_FILENO, visbuf, strlen(visbuf)); } if (buf[0] == '\02') exit(1); ++errs; continue; } if (buf[0] == 'E') { (void) atomicio(vwrite, remout, "", 1); goto done; } if (ch == '\n') *--cp = 0; cp = buf; if (*cp == 'T') { setimes++; cp++; if (!isdigit((unsigned char)*cp)) SCREWUP("mtime.sec not present"); ull = strtoull(cp, &cp, 10); if (!cp || *cp++ != ' ') SCREWUP("mtime.sec not delimited"); if (TYPE_OVERFLOW(time_t, ull)) setimes = 0; /* out of range */ mtime.tv_sec = ull; mtime.tv_usec = strtol(cp, &cp, 10); if (!cp || *cp++ != ' ' || mtime.tv_usec < 0 || mtime.tv_usec > 999999) SCREWUP("mtime.usec not delimited"); if (!isdigit((unsigned char)*cp)) SCREWUP("atime.sec not present"); ull = strtoull(cp, &cp, 10); if (!cp || *cp++ != ' ') SCREWUP("atime.sec not delimited"); if (TYPE_OVERFLOW(time_t, ull)) setimes = 0; /* out of range */ atime.tv_sec = ull; atime.tv_usec = strtol(cp, &cp, 10); if (!cp || *cp++ != '\0' || atime.tv_usec < 0 || atime.tv_usec > 999999) SCREWUP("atime.usec not delimited"); (void) atomicio(vwrite, remout, "", 1); continue; } if (*cp != 'C' && *cp != 'D') { /* * Check for the case "rcp remote:foo\* local:bar". * In this case, the line "No match." can be returned * by the shell before the rcp command on the remote is * executed so the ^Aerror_message convention isn't * followed. */ if (first) { run_err("%s", cp); exit(1); } SCREWUP("expected control record"); } mode = 0; for (++cp; cp < buf + 5; cp++) { if (*cp < '0' || *cp > '7') SCREWUP("bad mode"); mode = (mode << 3) | (*cp - '0'); } if (!pflag) mode &= ~mask; if (*cp++ != ' ') SCREWUP("mode not delimited"); if (!isdigit((unsigned char)*cp)) SCREWUP("size not present"); ull = strtoull(cp, &cp, 10); if (!cp || *cp++ != ' ') SCREWUP("size not delimited"); if (TYPE_OVERFLOW(off_t, ull)) SCREWUP("size out of range"); size = (off_t)ull; if (*cp == '\0' || strchr(cp, '/') != NULL || strcmp(cp, ".") == 0 || strcmp(cp, "..") == 0) { run_err("error: unexpected filename: %s", cp); exit(1); } if (npatterns > 0) { for (n = 0; n < npatterns; n++) { if (strcmp(patterns[n], cp) == 0 || fnmatch(patterns[n], cp, 0) == 0) break; } if (n >= npatterns) SCREWUP("filename does not match request"); } if (targisdir) { static char *namebuf; static size_t cursize; size_t need; need = strlen(targ) + strlen(cp) + 250; if (need > cursize) { free(namebuf); namebuf = xmalloc(need); cursize = need; } (void) snprintf(namebuf, need, "%s%s%s", targ, strcmp(targ, "/") ? "/" : "", cp); np = namebuf; } else np = targ; curfile = cp; exists = stat(np, &stb) == 0; if (buf[0] == 'D') { int mod_flag = pflag; if (!iamrecursive) SCREWUP("received directory without -r"); if (exists) { if (!S_ISDIR(stb.st_mode)) { errno = ENOTDIR; goto bad; } if (pflag) (void) chmod(np, mode); } else { /* Handle copying from a read-only directory */ mod_flag = 1; if (mkdir(np, mode | S_IRWXU) == -1) goto bad; } vect[0] = xstrdup(np); sink(1, vect, src); if (setimes) { setimes = 0; (void) utimes(vect[0], tv); } if (mod_flag) (void) chmod(vect[0], mode); free(vect[0]); continue; } omode = mode; mode |= S_IWUSR; if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) == -1) { bad: run_err("%s: %s", np, strerror(errno)); continue; } (void) atomicio(vwrite, remout, "", 1); if ((bp = allocbuf(&buffer, ofd, COPY_BUFLEN)) == NULL) { (void) close(ofd); continue; } cp = bp->buf; wrerr = 0; /* * NB. do not use run_err() unless immediately followed by * exit() below as it may send a spurious reply that might * desyncronise us from the peer. Use note_err() instead. */ statbytes = 0; if (showprogress) start_progress_meter(curfile, size, &statbytes); set_nonblock(remin); for (count = i = 0; i < size; i += bp->cnt) { amt = bp->cnt; if (i + amt > size) amt = size - i; count += amt; do { j = atomicio6(read, remin, cp, amt, scpio, &statbytes); if (j == 0) { run_err("%s", j != EPIPE ? strerror(errno) : "dropped connection"); exit(1); } amt -= j; cp += j; } while (amt > 0); if (count == bp->cnt) { /* Keep reading so we stay sync'd up. */ if (!wrerr) { if (atomicio(vwrite, ofd, bp->buf, count) != count) { note_err("%s: %s", np, strerror(errno)); wrerr = 1; } } count = 0; cp = bp->buf; } } unset_nonblock(remin); if (count != 0 && !wrerr && atomicio(vwrite, ofd, bp->buf, count) != count) { note_err("%s: %s", np, strerror(errno)); wrerr = 1; } if (!wrerr && (!exists || S_ISREG(stb.st_mode)) && ftruncate(ofd, size) != 0) note_err("%s: truncate: %s", np, strerror(errno)); if (pflag) { if (exists || omode != mode) #ifdef HAVE_FCHMOD if (fchmod(ofd, omode)) { #else /* HAVE_FCHMOD */ if (chmod(np, omode)) { #endif /* HAVE_FCHMOD */ note_err("%s: set mode: %s", np, strerror(errno)); } } else { if (!exists && omode != mode) #ifdef HAVE_FCHMOD if (fchmod(ofd, omode & ~mask)) { #else /* HAVE_FCHMOD */ if (chmod(np, omode & ~mask)) { #endif /* HAVE_FCHMOD */ note_err("%s: set mode: %s", np, strerror(errno)); } } if (close(ofd) == -1) note_err("%s: close: %s", np, strerror(errno)); (void) response(); if (showprogress) stop_progress_meter(); if (setimes && !wrerr) { setimes = 0; if (utimes(np, tv) == -1) { note_err("%s: set times: %s", np, strerror(errno)); } } /* If no error was noted then signal success for this file */ if (note_err(NULL) == 0) (void) atomicio(vwrite, remout, "", 1); } done: for (n = 0; n < npatterns; n++) free(patterns[n]); free(patterns); return; screwup: for (n = 0; n < npatterns; n++) free(patterns[n]); free(patterns); run_err("protocol error: %s", why); exit(1); } void throughlocal_sftp(struct sftp_conn *from, struct sftp_conn *to, char *src, char *targ) { char *target = NULL, *filename = NULL, *abs_dst = NULL; char *abs_src = NULL, *tmp = NULL; glob_t g; int i, r, targetisdir, err = 0; if ((filename = basename(src)) == NULL) fatal("basename %s: %s", src, strerror(errno)); if ((abs_src = prepare_remote_path(from, src)) == NULL || (target = prepare_remote_path(to, targ)) == NULL) cleanup_exit(255); memset(&g, 0, sizeof(g)); - targetisdir = remote_is_dir(to, target); + targetisdir = sftp_remote_is_dir(to, target); if (!targetisdir && targetshouldbedirectory) { error("%s: destination is not a directory", targ); err = -1; goto out; } debug3_f("copying remote %s to remote %s", abs_src, target); - if ((r = remote_glob(from, abs_src, GLOB_NOCHECK|GLOB_MARK, + if ((r = sftp_glob(from, abs_src, GLOB_NOCHECK|GLOB_MARK, NULL, &g)) != 0) { if (r == GLOB_NOSPACE) error("%s: too many glob matches", src); else error("%s: %s", src, strerror(ENOENT)); err = -1; goto out; } /* Did we actually get any matches back from the glob? */ if (g.gl_matchc == 0 && g.gl_pathc == 1 && g.gl_pathv[0] != 0) { /* * If nothing matched but a path returned, then it's probably * a GLOB_NOCHECK result. Check whether the unglobbed path * exists so we can give a nice error message early. */ - if (do_stat(from, g.gl_pathv[0], 1) == NULL) { + if (sftp_stat(from, g.gl_pathv[0], 1, NULL) != 0) { error("%s: %s", src, strerror(ENOENT)); err = -1; goto out; } } for (i = 0; g.gl_pathv[i] && !interrupted; i++) { tmp = xstrdup(g.gl_pathv[i]); if ((filename = basename(tmp)) == NULL) { error("basename %s: %s", tmp, strerror(errno)); err = -1; goto out; } if (targetisdir) - abs_dst = path_append(target, filename); + abs_dst = sftp_path_append(target, filename); else abs_dst = xstrdup(target); debug("Fetching %s to %s\n", g.gl_pathv[i], abs_dst); - if (globpath_is_dir(g.gl_pathv[i]) && iamrecursive) { - if (crossload_dir(from, to, g.gl_pathv[i], abs_dst, + if (sftp_globpath_is_dir(g.gl_pathv[i]) && iamrecursive) { + if (sftp_crossload_dir(from, to, g.gl_pathv[i], abs_dst, NULL, pflag, SFTP_PROGRESS_ONLY, 1) == -1) err = -1; } else { - if (do_crossload(from, to, g.gl_pathv[i], abs_dst, NULL, - pflag) == -1) + if (sftp_crossload(from, to, g.gl_pathv[i], abs_dst, + NULL, pflag) == -1) err = -1; } free(abs_dst); abs_dst = NULL; free(tmp); tmp = NULL; } out: free(abs_src); free(abs_dst); free(target); free(tmp); globfree(&g); if (err == -1) errs = 1; } int response(void) { char ch, *cp, resp, rbuf[2048], visbuf[2048]; if (atomicio(read, remin, &resp, sizeof(resp)) != sizeof(resp)) lostconn(0); cp = rbuf; switch (resp) { case 0: /* ok */ return (0); default: *cp++ = resp; /* FALLTHROUGH */ case 1: /* error, followed by error msg */ case 2: /* fatal error, "" */ do { if (atomicio(read, remin, &ch, sizeof(ch)) != sizeof(ch)) lostconn(0); *cp++ = ch; } while (cp < &rbuf[sizeof(rbuf) - 1] && ch != '\n'); if (!iamremote) { cp[-1] = '\0'; (void) snmprintf(visbuf, sizeof(visbuf), NULL, "%s\n", rbuf); (void) atomicio(vwrite, STDERR_FILENO, visbuf, strlen(visbuf)); } ++errs; if (resp == 1) return (-1); exit(1); } /* NOTREACHED */ } void usage(void) { (void) fprintf(stderr, "usage: scp [-346ABCOpqRrsTv] [-c cipher] [-D sftp_server_path] [-F ssh_config]\n" " [-i identity_file] [-J destination] [-l limit] [-o ssh_option]\n" " [-P port] [-S program] [-X sftp_option] source ... target\n"); exit(1); } void run_err(const char *fmt,...) { static FILE *fp; va_list ap; ++errs; if (fp != NULL || (remout != -1 && (fp = fdopen(remout, "w")))) { (void) fprintf(fp, "%c", 0x01); (void) fprintf(fp, "scp: "); va_start(ap, fmt); (void) vfprintf(fp, fmt, ap); va_end(ap); (void) fprintf(fp, "\n"); (void) fflush(fp); } if (!iamremote) { va_start(ap, fmt); vfmprintf(stderr, fmt, ap); va_end(ap); fprintf(stderr, "\n"); } } /* * Notes a sink error for sending at the end of a file transfer. Returns 0 if * no error has been noted or -1 otherwise. Use note_err(NULL) to flush * any active error at the end of the transfer. */ int note_err(const char *fmt, ...) { static char *emsg; va_list ap; /* Replay any previously-noted error */ if (fmt == NULL) { if (emsg == NULL) return 0; run_err("%s", emsg); free(emsg); emsg = NULL; return -1; } errs++; /* Prefer first-noted error */ if (emsg != NULL) return -1; va_start(ap, fmt); vasnmprintf(&emsg, INT_MAX, NULL, fmt, ap); va_end(ap); return -1; } void verifydir(char *cp) { struct stat stb; if (!stat(cp, &stb)) { if (S_ISDIR(stb.st_mode)) return; errno = ENOTDIR; } run_err("%s: %s", cp, strerror(errno)); killchild(0); } int okname(char *cp0) { int c; char *cp; cp = cp0; do { c = (int)*cp; if (c & 0200) goto bad; if (!isalpha(c) && !isdigit((unsigned char)c)) { switch (c) { case '\'': case '"': case '`': case ' ': case '#': goto bad; default: break; } } } while (*++cp); return (1); bad: fmprintf(stderr, "%s: invalid user name\n", cp0); return (0); } BUF * allocbuf(BUF *bp, int fd, int blksize) { size_t size; #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE struct stat stb; if (fstat(fd, &stb) == -1) { run_err("fstat: %s", strerror(errno)); return (0); } size = ROUNDUP(stb.st_blksize, blksize); if (size == 0) size = blksize; #else /* HAVE_STRUCT_STAT_ST_BLKSIZE */ size = blksize; #endif /* HAVE_STRUCT_STAT_ST_BLKSIZE */ if (bp->cnt >= size) return (bp); bp->buf = xrecallocarray(bp->buf, bp->cnt, size, 1); bp->cnt = size; return (bp); } void lostconn(int signo) { if (!iamremote) (void)write(STDERR_FILENO, "lost connection\n", 16); if (signo) _exit(1); else exit(1); } void cleanup_exit(int i) { if (remin > 0) close(remin); if (remout > 0) close(remout); if (remin2 > 0) close(remin2); if (remout2 > 0) close(remout2); if (do_cmd_pid > 0) (void)waitpid(do_cmd_pid, NULL, 0); if (do_cmd_pid2 > 0) (void)waitpid(do_cmd_pid2, NULL, 0); exit(i); } diff --git a/servconf.c b/servconf.c index 45a2f2c2781c..49f7f7322e7e 100644 --- a/servconf.c +++ b/servconf.c @@ -1,3176 +1,3235 @@ -/* $OpenBSD: servconf.c,v 1.396 2023/07/17 05:26:38 djm Exp $ */ +/* $OpenBSD: servconf.c,v 1.402 2023/09/08 06:34:24 djm Exp $ */ /* * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". */ #include "includes.h" #include #include #include #ifdef __OpenBSD__ #include #endif #include #include #include #ifdef HAVE_NET_ROUTE_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_UTIL_H #include #endif #ifdef USE_SYSTEM_GLOB # include #else # include "openbsd-compat/glob.h" #endif #include "openbsd-compat/sys-queue.h" #include "xmalloc.h" #include "ssh.h" #include "log.h" #include "sshbuf.h" #include "misc.h" #include "servconf.h" #include "pathnames.h" #include "cipher.h" #include "sshkey.h" #include "kex.h" #include "mac.h" #include "match.h" #include "channels.h" #include "groupaccess.h" #include "canohost.h" #include "packet.h" #include "ssherr.h" #include "hostfile.h" #include "auth.h" #include "myproposal.h" #include "digest.h" static void add_listen_addr(ServerOptions *, const char *, const char *, int); static void add_one_listen_addr(ServerOptions *, const char *, const char *, int); static void parse_server_config_depth(ServerOptions *options, const char *filename, struct sshbuf *conf, struct include_list *includes, struct connection_info *connectinfo, int flags, int *activep, int depth); /* Use of privilege separation or not */ extern int use_privsep; extern struct sshbuf *cfg; /* Initializes the server options to their default values. */ void initialize_server_options(ServerOptions *options) { memset(options, 0, sizeof(*options)); /* Portable-specific options */ options->use_pam = -1; /* Standard Options */ options->num_ports = 0; options->ports_from_cmdline = 0; options->queued_listen_addrs = NULL; options->num_queued_listens = 0; options->listen_addrs = NULL; options->num_listen_addrs = 0; options->address_family = -1; options->routing_domain = NULL; options->num_host_key_files = 0; options->num_host_cert_files = 0; options->host_key_agent = NULL; options->pid_file = NULL; options->login_grace_time = -1; options->permit_root_login = PERMIT_NOT_SET; options->ignore_rhosts = -1; options->ignore_user_known_hosts = -1; options->print_motd = -1; options->print_lastlog = -1; options->x11_forwarding = -1; options->x11_display_offset = -1; options->x11_use_localhost = -1; options->permit_tty = -1; options->permit_user_rc = -1; options->xauth_location = NULL; options->strict_modes = -1; options->tcp_keep_alive = -1; options->log_facility = SYSLOG_FACILITY_NOT_SET; options->log_level = SYSLOG_LEVEL_NOT_SET; options->num_log_verbose = 0; options->log_verbose = NULL; options->hostbased_authentication = -1; options->hostbased_uses_name_from_packet_only = -1; options->hostbased_accepted_algos = NULL; options->hostkeyalgorithms = NULL; options->pubkey_authentication = -1; options->pubkey_auth_options = -1; options->pubkey_accepted_algos = NULL; options->kerberos_authentication = -1; options->kerberos_or_local_passwd = -1; options->kerberos_ticket_cleanup = -1; options->kerberos_get_afs_token = -1; options->gss_authentication=-1; options->gss_cleanup_creds = -1; options->gss_strict_acceptor = -1; options->password_authentication = -1; options->kbd_interactive_authentication = -1; options->permit_empty_passwd = -1; options->permit_user_env = -1; options->permit_user_env_allowlist = NULL; options->compression = -1; options->rekey_limit = -1; options->rekey_interval = -1; options->allow_tcp_forwarding = -1; options->allow_streamlocal_forwarding = -1; options->allow_agent_forwarding = -1; options->num_allow_users = 0; options->num_deny_users = 0; options->num_allow_groups = 0; options->num_deny_groups = 0; options->ciphers = NULL; options->macs = NULL; options->kex_algorithms = NULL; options->ca_sign_algorithms = NULL; options->fwd_opts.gateway_ports = -1; options->fwd_opts.streamlocal_bind_mask = (mode_t)-1; options->fwd_opts.streamlocal_bind_unlink = -1; options->num_subsystems = 0; options->max_startups_begin = -1; options->max_startups_rate = -1; options->max_startups = -1; options->per_source_max_startups = -1; options->per_source_masklen_ipv4 = -1; options->per_source_masklen_ipv6 = -1; options->max_authtries = -1; options->max_sessions = -1; options->banner = NULL; options->use_dns = -1; options->client_alive_interval = -1; options->client_alive_count_max = -1; options->num_authkeys_files = 0; options->num_accept_env = 0; options->num_setenv = 0; options->permit_tun = -1; options->permitted_opens = NULL; options->permitted_listens = NULL; options->adm_forced_command = NULL; options->chroot_directory = NULL; options->authorized_keys_command = NULL; options->authorized_keys_command_user = NULL; options->revoked_keys_file = NULL; options->sk_provider = NULL; options->trusted_user_ca_keys = NULL; options->authorized_principals_file = NULL; options->authorized_principals_command = NULL; options->authorized_principals_command_user = NULL; options->ip_qos_interactive = -1; options->ip_qos_bulk = -1; options->version_addendum = NULL; options->fingerprint_hash = -1; options->disable_forwarding = -1; options->expose_userauth_info = -1; options->required_rsa_size = -1; options->channel_timeouts = NULL; options->num_channel_timeouts = 0; options->unused_connection_timeout = -1; } /* Returns 1 if a string option is unset or set to "none" or 0 otherwise. */ static int option_clear_or_none(const char *o) { return o == NULL || strcasecmp(o, "none") == 0; } static void assemble_algorithms(ServerOptions *o) { char *all_cipher, *all_mac, *all_kex, *all_key, *all_sig; char *def_cipher, *def_mac, *def_kex, *def_key, *def_sig; int r; all_cipher = cipher_alg_list(',', 0); all_mac = mac_alg_list(','); all_kex = kex_alg_list(','); all_key = sshkey_alg_list(0, 0, 1, ','); all_sig = sshkey_alg_list(0, 1, 1, ','); /* remove unsupported algos from default lists */ def_cipher = match_filter_allowlist(KEX_SERVER_ENCRYPT, all_cipher); def_mac = match_filter_allowlist(KEX_SERVER_MAC, all_mac); def_kex = match_filter_allowlist(KEX_SERVER_KEX, all_kex); def_key = match_filter_allowlist(KEX_DEFAULT_PK_ALG, all_key); def_sig = match_filter_allowlist(SSH_ALLOWED_CA_SIGALGS, all_sig); #define ASSEMBLE(what, defaults, all) \ do { \ if ((r = kex_assemble_names(&o->what, defaults, all)) != 0) \ fatal_fr(r, "%s", #what); \ } while (0) ASSEMBLE(ciphers, def_cipher, all_cipher); ASSEMBLE(macs, def_mac, all_mac); ASSEMBLE(kex_algorithms, def_kex, all_kex); ASSEMBLE(hostkeyalgorithms, def_key, all_key); ASSEMBLE(hostbased_accepted_algos, def_key, all_key); ASSEMBLE(pubkey_accepted_algos, def_key, all_key); ASSEMBLE(ca_sign_algorithms, def_sig, all_sig); #undef ASSEMBLE free(all_cipher); free(all_mac); free(all_kex); free(all_key); free(all_sig); free(def_cipher); free(def_mac); free(def_kex); free(def_key); free(def_sig); } void servconf_add_hostkey(const char *file, const int line, ServerOptions *options, const char *path, int userprovided) { char *apath = derelativise_path(path); opt_array_append2(file, line, "HostKey", &options->host_key_files, &options->host_key_file_userprovided, &options->num_host_key_files, apath, userprovided); free(apath); } void servconf_add_hostcert(const char *file, const int line, ServerOptions *options, const char *path) { char *apath = derelativise_path(path); opt_array_append(file, line, "HostCertificate", &options->host_cert_files, &options->num_host_cert_files, apath); free(apath); } void fill_default_server_options(ServerOptions *options) { u_int i; /* Portable-specific options */ if (options->use_pam == -1) options->use_pam = 0; /* Standard Options */ if (options->num_host_key_files == 0) { /* fill default hostkeys for protocols */ servconf_add_hostkey("[default]", 0, options, _PATH_HOST_RSA_KEY_FILE, 0); #ifdef OPENSSL_HAS_ECC servconf_add_hostkey("[default]", 0, options, _PATH_HOST_ECDSA_KEY_FILE, 0); #endif servconf_add_hostkey("[default]", 0, options, _PATH_HOST_ED25519_KEY_FILE, 0); #ifdef WITH_XMSS servconf_add_hostkey("[default]", 0, options, _PATH_HOST_XMSS_KEY_FILE, 0); #endif /* WITH_XMSS */ } /* No certificates by default */ if (options->num_ports == 0) options->ports[options->num_ports++] = SSH_DEFAULT_PORT; if (options->address_family == -1) options->address_family = AF_UNSPEC; if (options->listen_addrs == NULL) add_listen_addr(options, NULL, NULL, 0); if (options->pid_file == NULL) options->pid_file = xstrdup(_PATH_SSH_DAEMON_PID_FILE); if (options->moduli_file == NULL) options->moduli_file = xstrdup(_PATH_DH_MODULI); if (options->login_grace_time == -1) options->login_grace_time = 120; if (options->permit_root_login == PERMIT_NOT_SET) options->permit_root_login = PERMIT_NO_PASSWD; if (options->ignore_rhosts == -1) options->ignore_rhosts = 1; if (options->ignore_user_known_hosts == -1) options->ignore_user_known_hosts = 0; if (options->print_motd == -1) options->print_motd = 1; if (options->print_lastlog == -1) options->print_lastlog = 1; if (options->x11_forwarding == -1) options->x11_forwarding = 0; if (options->x11_display_offset == -1) options->x11_display_offset = 10; if (options->x11_use_localhost == -1) options->x11_use_localhost = 1; if (options->xauth_location == NULL) options->xauth_location = xstrdup(_PATH_XAUTH); if (options->permit_tty == -1) options->permit_tty = 1; if (options->permit_user_rc == -1) options->permit_user_rc = 1; if (options->strict_modes == -1) options->strict_modes = 1; if (options->tcp_keep_alive == -1) options->tcp_keep_alive = 1; if (options->log_facility == SYSLOG_FACILITY_NOT_SET) options->log_facility = SYSLOG_FACILITY_AUTH; if (options->log_level == SYSLOG_LEVEL_NOT_SET) options->log_level = SYSLOG_LEVEL_INFO; if (options->hostbased_authentication == -1) options->hostbased_authentication = 0; if (options->hostbased_uses_name_from_packet_only == -1) options->hostbased_uses_name_from_packet_only = 0; if (options->pubkey_authentication == -1) options->pubkey_authentication = 1; if (options->pubkey_auth_options == -1) options->pubkey_auth_options = 0; if (options->kerberos_authentication == -1) options->kerberos_authentication = 0; if (options->kerberos_or_local_passwd == -1) options->kerberos_or_local_passwd = 1; if (options->kerberos_ticket_cleanup == -1) options->kerberos_ticket_cleanup = 1; if (options->kerberos_get_afs_token == -1) options->kerberos_get_afs_token = 0; if (options->gss_authentication == -1) options->gss_authentication = 0; if (options->gss_cleanup_creds == -1) options->gss_cleanup_creds = 1; if (options->gss_strict_acceptor == -1) options->gss_strict_acceptor = 1; if (options->password_authentication == -1) options->password_authentication = 1; if (options->kbd_interactive_authentication == -1) options->kbd_interactive_authentication = 1; if (options->permit_empty_passwd == -1) options->permit_empty_passwd = 0; if (options->permit_user_env == -1) { options->permit_user_env = 0; options->permit_user_env_allowlist = NULL; } if (options->compression == -1) #ifdef WITH_ZLIB options->compression = COMP_DELAYED; #else options->compression = COMP_NONE; #endif if (options->rekey_limit == -1) options->rekey_limit = 0; if (options->rekey_interval == -1) options->rekey_interval = 0; if (options->allow_tcp_forwarding == -1) options->allow_tcp_forwarding = FORWARD_ALLOW; if (options->allow_streamlocal_forwarding == -1) options->allow_streamlocal_forwarding = FORWARD_ALLOW; if (options->allow_agent_forwarding == -1) options->allow_agent_forwarding = 1; if (options->fwd_opts.gateway_ports == -1) options->fwd_opts.gateway_ports = 0; if (options->max_startups == -1) options->max_startups = 100; if (options->max_startups_rate == -1) options->max_startups_rate = 30; /* 30% */ if (options->max_startups_begin == -1) options->max_startups_begin = 10; if (options->per_source_max_startups == -1) options->per_source_max_startups = INT_MAX; if (options->per_source_masklen_ipv4 == -1) options->per_source_masklen_ipv4 = 32; if (options->per_source_masklen_ipv6 == -1) options->per_source_masklen_ipv6 = 128; if (options->max_authtries == -1) options->max_authtries = DEFAULT_AUTH_FAIL_MAX; if (options->max_sessions == -1) options->max_sessions = DEFAULT_SESSIONS_MAX; if (options->use_dns == -1) options->use_dns = 0; if (options->client_alive_interval == -1) options->client_alive_interval = 0; if (options->client_alive_count_max == -1) options->client_alive_count_max = 3; if (options->num_authkeys_files == 0) { opt_array_append("[default]", 0, "AuthorizedKeysFiles", &options->authorized_keys_files, &options->num_authkeys_files, _PATH_SSH_USER_PERMITTED_KEYS); opt_array_append("[default]", 0, "AuthorizedKeysFiles", &options->authorized_keys_files, &options->num_authkeys_files, _PATH_SSH_USER_PERMITTED_KEYS2); } if (options->permit_tun == -1) options->permit_tun = SSH_TUNMODE_NO; if (options->ip_qos_interactive == -1) options->ip_qos_interactive = IPTOS_DSCP_AF21; if (options->ip_qos_bulk == -1) options->ip_qos_bulk = IPTOS_DSCP_CS1; if (options->version_addendum == NULL) options->version_addendum = xstrdup(""); if (options->fwd_opts.streamlocal_bind_mask == (mode_t)-1) options->fwd_opts.streamlocal_bind_mask = 0177; if (options->fwd_opts.streamlocal_bind_unlink == -1) options->fwd_opts.streamlocal_bind_unlink = 0; if (options->fingerprint_hash == -1) options->fingerprint_hash = SSH_FP_HASH_DEFAULT; if (options->disable_forwarding == -1) options->disable_forwarding = 0; if (options->expose_userauth_info == -1) options->expose_userauth_info = 0; if (options->sk_provider == NULL) options->sk_provider = xstrdup("internal"); if (options->required_rsa_size == -1) options->required_rsa_size = SSH_RSA_MINIMUM_MODULUS_SIZE; if (options->unused_connection_timeout == -1) options->unused_connection_timeout = 0; assemble_algorithms(options); /* Turn privilege separation and sandboxing on by default */ if (use_privsep == -1) use_privsep = PRIVSEP_ON; #define CLEAR_ON_NONE(v) \ do { \ if (option_clear_or_none(v)) { \ free(v); \ v = NULL; \ } \ } while(0) #define CLEAR_ON_NONE_ARRAY(v, nv, none) \ do { \ if (options->nv == 1 && \ strcasecmp(options->v[0], none) == 0) { \ free(options->v[0]); \ free(options->v); \ options->v = NULL; \ options->nv = 0; \ } \ } while (0) CLEAR_ON_NONE(options->pid_file); CLEAR_ON_NONE(options->xauth_location); CLEAR_ON_NONE(options->banner); CLEAR_ON_NONE(options->trusted_user_ca_keys); CLEAR_ON_NONE(options->revoked_keys_file); CLEAR_ON_NONE(options->sk_provider); CLEAR_ON_NONE(options->authorized_principals_file); CLEAR_ON_NONE(options->adm_forced_command); CLEAR_ON_NONE(options->chroot_directory); CLEAR_ON_NONE(options->routing_domain); CLEAR_ON_NONE(options->host_key_agent); for (i = 0; i < options->num_host_key_files; i++) CLEAR_ON_NONE(options->host_key_files[i]); for (i = 0; i < options->num_host_cert_files; i++) CLEAR_ON_NONE(options->host_cert_files[i]); CLEAR_ON_NONE_ARRAY(channel_timeouts, num_channel_timeouts, "none"); CLEAR_ON_NONE_ARRAY(auth_methods, num_auth_methods, "any"); #undef CLEAR_ON_NONE #undef CLEAR_ON_NONE_ARRAY } /* Keyword tokens. */ typedef enum { sBadOption, /* == unknown option */ /* Portable-specific options */ sUsePAM, /* Standard Options */ sPort, sHostKeyFile, sLoginGraceTime, sPermitRootLogin, sLogFacility, sLogLevel, sLogVerbose, sKerberosAuthentication, sKerberosOrLocalPasswd, sKerberosTicketCleanup, sKerberosGetAFSToken, sPasswordAuthentication, sKbdInteractiveAuthentication, sListenAddress, sAddressFamily, sPrintMotd, sPrintLastLog, sIgnoreRhosts, sX11Forwarding, sX11DisplayOffset, sX11UseLocalhost, sPermitTTY, sStrictModes, sEmptyPasswd, sTCPKeepAlive, sPermitUserEnvironment, sAllowTcpForwarding, sCompression, sRekeyLimit, sAllowUsers, sDenyUsers, sAllowGroups, sDenyGroups, sIgnoreUserKnownHosts, sCiphers, sMacs, sPidFile, sModuliFile, sGatewayPorts, sPubkeyAuthentication, sPubkeyAcceptedAlgorithms, sXAuthLocation, sSubsystem, sMaxStartups, sMaxAuthTries, sMaxSessions, sBanner, sUseDNS, sHostbasedAuthentication, sHostbasedUsesNameFromPacketOnly, sHostbasedAcceptedAlgorithms, sHostKeyAlgorithms, sPerSourceMaxStartups, sPerSourceNetBlockSize, sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile, sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor, sAcceptEnv, sSetEnv, sPermitTunnel, sMatch, sPermitOpen, sPermitListen, sForceCommand, sChrootDirectory, sUsePrivilegeSeparation, sAllowAgentForwarding, sHostCertificate, sInclude, sRevokedKeys, sTrustedUserCAKeys, sAuthorizedPrincipalsFile, sAuthorizedPrincipalsCommand, sAuthorizedPrincipalsCommandUser, sKexAlgorithms, sCASignatureAlgorithms, sIPQoS, sVersionAddendum, sAuthorizedKeysCommand, sAuthorizedKeysCommandUser, sAuthenticationMethods, sHostKeyAgent, sPermitUserRC, sStreamLocalBindMask, sStreamLocalBindUnlink, sAllowStreamLocalForwarding, sFingerprintHash, sDisableForwarding, sExposeAuthInfo, sRDomain, sPubkeyAuthOptions, sSecurityKeyProvider, sRequiredRSASize, sChannelTimeout, sUnusedConnectionTimeout, sDeprecated, sIgnore, sUnsupported } ServerOpCodes; #define SSHCFG_GLOBAL 0x01 /* allowed in main section of config */ #define SSHCFG_MATCH 0x02 /* allowed inside a Match section */ #define SSHCFG_ALL (SSHCFG_GLOBAL|SSHCFG_MATCH) #define SSHCFG_NEVERMATCH 0x04 /* Match never matches; internal only */ #define SSHCFG_MATCH_ONLY 0x08 /* Match only in conditional blocks; internal only */ /* Textual representation of the tokens. */ static struct { const char *name; ServerOpCodes opcode; u_int flags; } keywords[] = { /* Portable-specific options */ #ifdef USE_PAM { "usepam", sUsePAM, SSHCFG_GLOBAL }, #else { "usepam", sUnsupported, SSHCFG_GLOBAL }, #endif { "pamauthenticationviakbdint", sDeprecated, SSHCFG_GLOBAL }, /* Standard Options */ { "port", sPort, SSHCFG_GLOBAL }, { "hostkey", sHostKeyFile, SSHCFG_GLOBAL }, { "hostdsakey", sHostKeyFile, SSHCFG_GLOBAL }, /* alias */ { "hostkeyagent", sHostKeyAgent, SSHCFG_GLOBAL }, { "pidfile", sPidFile, SSHCFG_GLOBAL }, { "modulifile", sModuliFile, SSHCFG_GLOBAL }, { "serverkeybits", sDeprecated, SSHCFG_GLOBAL }, { "logingracetime", sLoginGraceTime, SSHCFG_GLOBAL }, { "keyregenerationinterval", sDeprecated, SSHCFG_GLOBAL }, { "permitrootlogin", sPermitRootLogin, SSHCFG_ALL }, { "syslogfacility", sLogFacility, SSHCFG_GLOBAL }, { "loglevel", sLogLevel, SSHCFG_ALL }, { "logverbose", sLogVerbose, SSHCFG_ALL }, { "rhostsauthentication", sDeprecated, SSHCFG_GLOBAL }, { "rhostsrsaauthentication", sDeprecated, SSHCFG_ALL }, { "hostbasedauthentication", sHostbasedAuthentication, SSHCFG_ALL }, { "hostbasedusesnamefrompacketonly", sHostbasedUsesNameFromPacketOnly, SSHCFG_ALL }, { "hostbasedacceptedalgorithms", sHostbasedAcceptedAlgorithms, SSHCFG_ALL }, { "hostbasedacceptedkeytypes", sHostbasedAcceptedAlgorithms, SSHCFG_ALL }, /* obsolete */ { "hostkeyalgorithms", sHostKeyAlgorithms, SSHCFG_GLOBAL }, { "rsaauthentication", sDeprecated, SSHCFG_ALL }, { "pubkeyauthentication", sPubkeyAuthentication, SSHCFG_ALL }, { "pubkeyacceptedalgorithms", sPubkeyAcceptedAlgorithms, SSHCFG_ALL }, { "pubkeyacceptedkeytypes", sPubkeyAcceptedAlgorithms, SSHCFG_ALL }, /* obsolete */ { "pubkeyauthoptions", sPubkeyAuthOptions, SSHCFG_ALL }, { "dsaauthentication", sPubkeyAuthentication, SSHCFG_GLOBAL }, /* alias */ #ifdef KRB5 { "kerberosauthentication", sKerberosAuthentication, SSHCFG_ALL }, { "kerberosorlocalpasswd", sKerberosOrLocalPasswd, SSHCFG_GLOBAL }, { "kerberosticketcleanup", sKerberosTicketCleanup, SSHCFG_GLOBAL }, #ifdef USE_AFS { "kerberosgetafstoken", sKerberosGetAFSToken, SSHCFG_GLOBAL }, #else { "kerberosgetafstoken", sUnsupported, SSHCFG_GLOBAL }, #endif #else { "kerberosauthentication", sUnsupported, SSHCFG_ALL }, { "kerberosorlocalpasswd", sUnsupported, SSHCFG_GLOBAL }, { "kerberosticketcleanup", sUnsupported, SSHCFG_GLOBAL }, { "kerberosgetafstoken", sUnsupported, SSHCFG_GLOBAL }, #endif { "kerberostgtpassing", sUnsupported, SSHCFG_GLOBAL }, { "afstokenpassing", sUnsupported, SSHCFG_GLOBAL }, #ifdef GSSAPI { "gssapiauthentication", sGssAuthentication, SSHCFG_ALL }, { "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_GLOBAL }, { "gssapistrictacceptorcheck", sGssStrictAcceptor, SSHCFG_GLOBAL }, #else { "gssapiauthentication", sUnsupported, SSHCFG_ALL }, { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL }, { "gssapistrictacceptorcheck", sUnsupported, SSHCFG_GLOBAL }, #endif { "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL }, { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL }, { "challengeresponseauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL }, /* alias */ { "skeyauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL }, /* alias */ { "checkmail", sDeprecated, SSHCFG_GLOBAL }, { "listenaddress", sListenAddress, SSHCFG_GLOBAL }, { "addressfamily", sAddressFamily, SSHCFG_GLOBAL }, { "printmotd", sPrintMotd, SSHCFG_GLOBAL }, #ifdef DISABLE_LASTLOG { "printlastlog", sUnsupported, SSHCFG_GLOBAL }, #else { "printlastlog", sPrintLastLog, SSHCFG_GLOBAL }, #endif { "ignorerhosts", sIgnoreRhosts, SSHCFG_ALL }, { "ignoreuserknownhosts", sIgnoreUserKnownHosts, SSHCFG_GLOBAL }, { "x11forwarding", sX11Forwarding, SSHCFG_ALL }, { "x11displayoffset", sX11DisplayOffset, SSHCFG_ALL }, { "x11uselocalhost", sX11UseLocalhost, SSHCFG_ALL }, { "xauthlocation", sXAuthLocation, SSHCFG_GLOBAL }, { "strictmodes", sStrictModes, SSHCFG_GLOBAL }, { "permitemptypasswords", sEmptyPasswd, SSHCFG_ALL }, { "permituserenvironment", sPermitUserEnvironment, SSHCFG_GLOBAL }, { "uselogin", sDeprecated, SSHCFG_GLOBAL }, { "compression", sCompression, SSHCFG_GLOBAL }, { "rekeylimit", sRekeyLimit, SSHCFG_ALL }, { "tcpkeepalive", sTCPKeepAlive, SSHCFG_GLOBAL }, { "keepalive", sTCPKeepAlive, SSHCFG_GLOBAL }, /* obsolete alias */ { "allowtcpforwarding", sAllowTcpForwarding, SSHCFG_ALL }, { "allowagentforwarding", sAllowAgentForwarding, SSHCFG_ALL }, { "allowusers", sAllowUsers, SSHCFG_ALL }, { "denyusers", sDenyUsers, SSHCFG_ALL }, { "allowgroups", sAllowGroups, SSHCFG_ALL }, { "denygroups", sDenyGroups, SSHCFG_ALL }, { "ciphers", sCiphers, SSHCFG_GLOBAL }, { "macs", sMacs, SSHCFG_GLOBAL }, { "protocol", sIgnore, SSHCFG_GLOBAL }, { "gatewayports", sGatewayPorts, SSHCFG_ALL }, - { "subsystem", sSubsystem, SSHCFG_GLOBAL }, + { "subsystem", sSubsystem, SSHCFG_ALL }, { "maxstartups", sMaxStartups, SSHCFG_GLOBAL }, { "persourcemaxstartups", sPerSourceMaxStartups, SSHCFG_GLOBAL }, { "persourcenetblocksize", sPerSourceNetBlockSize, SSHCFG_GLOBAL }, { "maxauthtries", sMaxAuthTries, SSHCFG_ALL }, { "maxsessions", sMaxSessions, SSHCFG_ALL }, { "banner", sBanner, SSHCFG_ALL }, { "usedns", sUseDNS, SSHCFG_GLOBAL }, { "verifyreversemapping", sDeprecated, SSHCFG_GLOBAL }, { "reversemappingcheck", sDeprecated, SSHCFG_GLOBAL }, { "clientaliveinterval", sClientAliveInterval, SSHCFG_ALL }, { "clientalivecountmax", sClientAliveCountMax, SSHCFG_ALL }, { "authorizedkeysfile", sAuthorizedKeysFile, SSHCFG_ALL }, { "authorizedkeysfile2", sDeprecated, SSHCFG_ALL }, { "useprivilegeseparation", sDeprecated, SSHCFG_GLOBAL}, { "acceptenv", sAcceptEnv, SSHCFG_ALL }, { "setenv", sSetEnv, SSHCFG_ALL }, { "permittunnel", sPermitTunnel, SSHCFG_ALL }, { "permittty", sPermitTTY, SSHCFG_ALL }, { "permituserrc", sPermitUserRC, SSHCFG_ALL }, { "match", sMatch, SSHCFG_ALL }, { "permitopen", sPermitOpen, SSHCFG_ALL }, { "permitlisten", sPermitListen, SSHCFG_ALL }, { "forcecommand", sForceCommand, SSHCFG_ALL }, { "chrootdirectory", sChrootDirectory, SSHCFG_ALL }, { "hostcertificate", sHostCertificate, SSHCFG_GLOBAL }, { "revokedkeys", sRevokedKeys, SSHCFG_ALL }, { "trustedusercakeys", sTrustedUserCAKeys, SSHCFG_ALL }, { "authorizedprincipalsfile", sAuthorizedPrincipalsFile, SSHCFG_ALL }, { "kexalgorithms", sKexAlgorithms, SSHCFG_GLOBAL }, { "include", sInclude, SSHCFG_ALL }, { "ipqos", sIPQoS, SSHCFG_ALL }, { "authorizedkeyscommand", sAuthorizedKeysCommand, SSHCFG_ALL }, { "authorizedkeyscommanduser", sAuthorizedKeysCommandUser, SSHCFG_ALL }, { "authorizedprincipalscommand", sAuthorizedPrincipalsCommand, SSHCFG_ALL }, { "authorizedprincipalscommanduser", sAuthorizedPrincipalsCommandUser, SSHCFG_ALL }, { "versionaddendum", sVersionAddendum, SSHCFG_GLOBAL }, { "authenticationmethods", sAuthenticationMethods, SSHCFG_ALL }, { "streamlocalbindmask", sStreamLocalBindMask, SSHCFG_ALL }, { "streamlocalbindunlink", sStreamLocalBindUnlink, SSHCFG_ALL }, { "allowstreamlocalforwarding", sAllowStreamLocalForwarding, SSHCFG_ALL }, { "fingerprinthash", sFingerprintHash, SSHCFG_GLOBAL }, { "disableforwarding", sDisableForwarding, SSHCFG_ALL }, { "exposeauthinfo", sExposeAuthInfo, SSHCFG_ALL }, { "rdomain", sRDomain, SSHCFG_ALL }, { "casignaturealgorithms", sCASignatureAlgorithms, SSHCFG_ALL }, { "securitykeyprovider", sSecurityKeyProvider, SSHCFG_GLOBAL }, { "requiredrsasize", sRequiredRSASize, SSHCFG_ALL }, { "channeltimeout", sChannelTimeout, SSHCFG_ALL }, { "unusedconnectiontimeout", sUnusedConnectionTimeout, SSHCFG_ALL }, { NULL, sBadOption, 0 } }; static struct { int val; char *text; } tunmode_desc[] = { { SSH_TUNMODE_NO, "no" }, { SSH_TUNMODE_POINTOPOINT, "point-to-point" }, { SSH_TUNMODE_ETHERNET, "ethernet" }, { SSH_TUNMODE_YES, "yes" }, { -1, NULL } }; /* Returns an opcode name from its number */ static const char * lookup_opcode_name(ServerOpCodes code) { u_int i; for (i = 0; keywords[i].name != NULL; i++) if (keywords[i].opcode == code) return(keywords[i].name); return "UNKNOWN"; } /* * Returns the number of the token pointed to by cp or sBadOption. */ static ServerOpCodes parse_token(const char *cp, const char *filename, int linenum, u_int *flags) { u_int i; for (i = 0; keywords[i].name; i++) if (strcasecmp(cp, keywords[i].name) == 0) { *flags = keywords[i].flags; return keywords[i].opcode; } error("%s: line %d: Bad configuration option: %s", filename, linenum, cp); return sBadOption; } char * derelativise_path(const char *path) { char *expanded, *ret, cwd[PATH_MAX]; if (strcasecmp(path, "none") == 0) return xstrdup("none"); expanded = tilde_expand_filename(path, getuid()); if (path_absolute(expanded)) return expanded; if (getcwd(cwd, sizeof(cwd)) == NULL) fatal_f("getcwd: %s", strerror(errno)); xasprintf(&ret, "%s/%s", cwd, expanded); free(expanded); return ret; } static void add_listen_addr(ServerOptions *options, const char *addr, const char *rdomain, int port) { u_int i; if (port > 0) add_one_listen_addr(options, addr, rdomain, port); else { for (i = 0; i < options->num_ports; i++) { add_one_listen_addr(options, addr, rdomain, options->ports[i]); } } } static void add_one_listen_addr(ServerOptions *options, const char *addr, const char *rdomain, int port) { struct addrinfo hints, *ai, *aitop; char strport[NI_MAXSERV]; int gaierr; u_int i; /* Find listen_addrs entry for this rdomain */ for (i = 0; i < options->num_listen_addrs; i++) { if (rdomain == NULL && options->listen_addrs[i].rdomain == NULL) break; if (rdomain == NULL || options->listen_addrs[i].rdomain == NULL) continue; if (strcmp(rdomain, options->listen_addrs[i].rdomain) == 0) break; } if (i >= options->num_listen_addrs) { /* No entry for this rdomain; allocate one */ if (i >= INT_MAX) fatal_f("too many listen addresses"); options->listen_addrs = xrecallocarray(options->listen_addrs, options->num_listen_addrs, options->num_listen_addrs + 1, sizeof(*options->listen_addrs)); i = options->num_listen_addrs++; if (rdomain != NULL) options->listen_addrs[i].rdomain = xstrdup(rdomain); } /* options->listen_addrs[i] points to the addresses for this rdomain */ memset(&hints, 0, sizeof(hints)); hints.ai_family = options->address_family; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = (addr == NULL) ? AI_PASSIVE : 0; snprintf(strport, sizeof strport, "%d", port); if ((gaierr = getaddrinfo(addr, strport, &hints, &aitop)) != 0) fatal("bad addr or host: %s (%s)", addr ? addr : "", ssh_gai_strerror(gaierr)); for (ai = aitop; ai->ai_next; ai = ai->ai_next) ; ai->ai_next = options->listen_addrs[i].addrs; options->listen_addrs[i].addrs = aitop; } /* Returns nonzero if the routing domain name is valid */ static int valid_rdomain(const char *name) { #if defined(HAVE_SYS_VALID_RDOMAIN) return sys_valid_rdomain(name); #elif defined(__OpenBSD__) const char *errstr; long long num; struct rt_tableinfo info; int mib[6]; size_t miblen = sizeof(mib); if (name == NULL) return 1; num = strtonum(name, 0, 255, &errstr); if (errstr != NULL) return 0; /* Check whether the table actually exists */ memset(mib, 0, sizeof(mib)); mib[0] = CTL_NET; mib[1] = PF_ROUTE; mib[4] = NET_RT_TABLE; mib[5] = (int)num; if (sysctl(mib, 6, &info, &miblen, NULL, 0) == -1) return 0; return 1; #else /* defined(__OpenBSD__) */ error("Routing domains are not supported on this platform"); return 0; #endif } /* * Queue a ListenAddress to be processed once we have all of the Ports * and AddressFamily options. */ static void queue_listen_addr(ServerOptions *options, const char *addr, const char *rdomain, int port) { struct queued_listenaddr *qla; options->queued_listen_addrs = xrecallocarray( options->queued_listen_addrs, options->num_queued_listens, options->num_queued_listens + 1, sizeof(*options->queued_listen_addrs)); qla = &options->queued_listen_addrs[options->num_queued_listens++]; qla->addr = xstrdup(addr); qla->port = port; qla->rdomain = rdomain == NULL ? NULL : xstrdup(rdomain); } /* * Process queued (text) ListenAddress entries. */ static void process_queued_listen_addrs(ServerOptions *options) { u_int i; struct queued_listenaddr *qla; if (options->num_ports == 0) options->ports[options->num_ports++] = SSH_DEFAULT_PORT; if (options->address_family == -1) options->address_family = AF_UNSPEC; for (i = 0; i < options->num_queued_listens; i++) { qla = &options->queued_listen_addrs[i]; add_listen_addr(options, qla->addr, qla->rdomain, qla->port); free(qla->addr); free(qla->rdomain); } free(options->queued_listen_addrs); options->queued_listen_addrs = NULL; options->num_queued_listens = 0; } /* * Inform channels layer of permitopen options for a single forwarding * direction (local/remote). */ static void process_permitopen_list(struct ssh *ssh, ServerOpCodes opcode, char **opens, u_int num_opens) { u_int i; int port; char *host, *arg, *oarg; int where = opcode == sPermitOpen ? FORWARD_LOCAL : FORWARD_REMOTE; const char *what = lookup_opcode_name(opcode); channel_clear_permission(ssh, FORWARD_ADM, where); if (num_opens == 0) return; /* permit any */ /* handle keywords: "any" / "none" */ if (num_opens == 1 && strcmp(opens[0], "any") == 0) return; if (num_opens == 1 && strcmp(opens[0], "none") == 0) { channel_disable_admin(ssh, where); return; } /* Otherwise treat it as a list of permitted host:port */ for (i = 0; i < num_opens; i++) { oarg = arg = xstrdup(opens[i]); host = hpdelim(&arg); if (host == NULL) fatal_f("missing host in %s", what); host = cleanhostname(host); if (arg == NULL || ((port = permitopen_port(arg)) < 0)) fatal_f("bad port number in %s", what); /* Send it to channels layer */ channel_add_permission(ssh, FORWARD_ADM, where, host, port); free(oarg); } } /* * Inform channels layer of permitopen options from configuration. */ void process_permitopen(struct ssh *ssh, ServerOptions *options) { process_permitopen_list(ssh, sPermitOpen, options->permitted_opens, options->num_permitted_opens); process_permitopen_list(ssh, sPermitListen, options->permitted_listens, options->num_permitted_listens); } /* Parse a ChannelTimeout clause "pattern=interval" */ static int parse_timeout(const char *s, char **typep, int *secsp) { char *cp, *sdup; int secs; if (typep != NULL) *typep = NULL; if (secsp != NULL) *secsp = 0; if (s == NULL) return -1; sdup = xstrdup(s); if ((cp = strchr(sdup, '=')) == NULL || cp == sdup) { free(sdup); return -1; } *cp++ = '\0'; if ((secs = convtime(cp)) < 0) { free(sdup); return -1; } /* success */ if (typep != NULL) *typep = xstrdup(sdup); if (secsp != NULL) *secsp = secs; free(sdup); return 0; } void process_channel_timeouts(struct ssh *ssh, ServerOptions *options) { int secs; u_int i; char *type; debug3_f("setting %u timeouts", options->num_channel_timeouts); channel_clear_timeouts(ssh); for (i = 0; i < options->num_channel_timeouts; i++) { if (parse_timeout(options->channel_timeouts[i], &type, &secs) != 0) { fatal_f("internal error: bad timeout %s", options->channel_timeouts[i]); } channel_add_timeout(ssh, type, secs); free(type); } } struct connection_info * get_connection_info(struct ssh *ssh, int populate, int use_dns) { static struct connection_info ci; if (ssh == NULL || !populate) return &ci; ci.host = auth_get_canonical_hostname(ssh, use_dns); ci.address = ssh_remote_ipaddr(ssh); ci.laddress = ssh_local_ipaddr(ssh); ci.lport = ssh_local_port(ssh); ci.rdomain = ssh_packet_rdomain_in(ssh); return &ci; } /* * The strategy for the Match blocks is that the config file is parsed twice. * * The first time is at startup. activep is initialized to 1 and the * directives in the global context are processed and acted on. Hitting a * Match directive unsets activep and the directives inside the block are * checked for syntax only. * * The second time is after a connection has been established but before * authentication. activep is initialized to 2 and global config directives * are ignored since they have already been processed. If the criteria in a * Match block is met, activep is set and the subsequent directives * processed and actioned until EOF or another Match block unsets it. Any * options set are copied into the main server config. * * Potential additions/improvements: * - Add Match support for pre-kex directives, eg. Ciphers. * * - Add a Tag directive (idea from David Leonard) ala pf, eg: * Match Address 192.168.0.* * Tag trusted * Match Group wheel * Tag trusted * Match Tag trusted * AllowTcpForwarding yes * GatewayPorts clientspecified * [...] * * - Add a PermittedChannelRequests directive * Match Group shell * PermittedChannelRequests session,forwarded-tcpip */ static int match_cfg_line_group(const char *grps, int line, const char *user) { int result = 0; struct passwd *pw; if (user == NULL) goto out; if ((pw = getpwnam(user)) == NULL) { debug("Can't match group at line %d because user %.100s does " "not exist", line, user); } else if (ga_init(pw->pw_name, pw->pw_gid) == 0) { debug("Can't Match group because user %.100s not in any group " "at line %d", user, line); } else if (ga_match_pattern_list(grps) != 1) { debug("user %.100s does not match group list %.100s at line %d", user, grps, line); } else { debug("user %.100s matched group list %.100s at line %d", user, grps, line); result = 1; } out: ga_free(); return result; } static void match_test_missing_fatal(const char *criteria, const char *attrib) { fatal("'Match %s' in configuration but '%s' not in connection " "test specification.", criteria, attrib); } /* * All of the attributes on a single Match line are ANDed together, so we need * to check every attribute and set the result to zero if any attribute does * not match. */ static int match_cfg_line(char **condition, int line, struct connection_info *ci) { int result = 1, attributes = 0, port; char *arg, *attrib, *cp = *condition; if (ci == NULL) debug3("checking syntax for 'Match %s'", cp); else debug3("checking match for '%s' user %s host %s addr %s " "laddr %s lport %d", cp, ci->user ? ci->user : "(null)", ci->host ? ci->host : "(null)", ci->address ? ci->address : "(null)", ci->laddress ? ci->laddress : "(null)", ci->lport); while ((attrib = strdelim(&cp)) && *attrib != '\0') { /* Terminate on comment */ if (*attrib == '#') { cp = NULL; /* mark all arguments consumed */ break; } arg = NULL; attributes++; /* Criterion "all" has no argument and must appear alone */ if (strcasecmp(attrib, "all") == 0) { if (attributes > 1 || ((arg = strdelim(&cp)) != NULL && *arg != '\0' && *arg != '#')) { error("'all' cannot be combined with other " "Match attributes"); return -1; } if (arg != NULL && *arg == '#') cp = NULL; /* mark all arguments consumed */ *condition = cp; return 1; } /* All other criteria require an argument */ if ((arg = strdelim(&cp)) == NULL || *arg == '\0' || *arg == '#') { error("Missing Match criteria for %s", attrib); return -1; } if (strcasecmp(attrib, "user") == 0) { if (ci == NULL || (ci->test && ci->user == NULL)) { result = 0; continue; } if (ci->user == NULL) match_test_missing_fatal("User", "user"); if (match_usergroup_pattern_list(ci->user, arg) != 1) result = 0; else debug("user %.100s matched 'User %.100s' at " "line %d", ci->user, arg, line); } else if (strcasecmp(attrib, "group") == 0) { if (ci == NULL || (ci->test && ci->user == NULL)) { result = 0; continue; } if (ci->user == NULL) match_test_missing_fatal("Group", "user"); switch (match_cfg_line_group(arg, line, ci->user)) { case -1: return -1; case 0: result = 0; } } else if (strcasecmp(attrib, "host") == 0) { if (ci == NULL || (ci->test && ci->host == NULL)) { result = 0; continue; } if (ci->host == NULL) match_test_missing_fatal("Host", "host"); if (match_hostname(ci->host, arg) != 1) result = 0; else debug("connection from %.100s matched 'Host " "%.100s' at line %d", ci->host, arg, line); } else if (strcasecmp(attrib, "address") == 0) { if (ci == NULL || (ci->test && ci->address == NULL)) { if (addr_match_list(NULL, arg) != 0) fatal("Invalid Match address argument " "'%s' at line %d", arg, line); result = 0; continue; } if (ci->address == NULL) match_test_missing_fatal("Address", "addr"); switch (addr_match_list(ci->address, arg)) { case 1: debug("connection from %.100s matched 'Address " "%.100s' at line %d", ci->address, arg, line); break; case 0: case -1: result = 0; break; case -2: return -1; } } else if (strcasecmp(attrib, "localaddress") == 0){ if (ci == NULL || (ci->test && ci->laddress == NULL)) { if (addr_match_list(NULL, arg) != 0) fatal("Invalid Match localaddress " "argument '%s' at line %d", arg, line); result = 0; continue; } if (ci->laddress == NULL) match_test_missing_fatal("LocalAddress", "laddr"); switch (addr_match_list(ci->laddress, arg)) { case 1: debug("connection from %.100s matched " "'LocalAddress %.100s' at line %d", ci->laddress, arg, line); break; case 0: case -1: result = 0; break; case -2: return -1; } } else if (strcasecmp(attrib, "localport") == 0) { if ((port = a2port(arg)) == -1) { error("Invalid LocalPort '%s' on Match line", arg); return -1; } if (ci == NULL || (ci->test && ci->lport == -1)) { result = 0; continue; } if (ci->lport == 0) match_test_missing_fatal("LocalPort", "lport"); /* TODO support port lists */ if (port == ci->lport) debug("connection from %.100s matched " "'LocalPort %d' at line %d", ci->laddress, port, line); else result = 0; } else if (strcasecmp(attrib, "rdomain") == 0) { if (ci == NULL || (ci->test && ci->rdomain == NULL)) { result = 0; continue; } if (ci->rdomain == NULL) match_test_missing_fatal("RDomain", "rdomain"); if (match_pattern_list(ci->rdomain, arg, 0) != 1) result = 0; else debug("user %.100s matched 'RDomain %.100s' at " "line %d", ci->rdomain, arg, line); } else { error("Unsupported Match attribute %s", attrib); return -1; } } if (attributes == 0) { error("One or more attributes required for Match"); return -1; } if (ci != NULL) debug3("match %sfound", result ? "" : "not "); *condition = cp; return result; } #define WHITESPACE " \t\r\n" /* Multistate option parsing */ struct multistate { char *key; int value; }; static const struct multistate multistate_flag[] = { { "yes", 1 }, { "no", 0 }, { NULL, -1 } }; static const struct multistate multistate_ignore_rhosts[] = { { "yes", IGNORE_RHOSTS_YES }, { "no", IGNORE_RHOSTS_NO }, { "shosts-only", IGNORE_RHOSTS_SHOSTS }, { NULL, -1 } }; static const struct multistate multistate_addressfamily[] = { { "inet", AF_INET }, { "inet6", AF_INET6 }, { "any", AF_UNSPEC }, { NULL, -1 } }; static const struct multistate multistate_permitrootlogin[] = { { "without-password", PERMIT_NO_PASSWD }, { "prohibit-password", PERMIT_NO_PASSWD }, { "forced-commands-only", PERMIT_FORCED_ONLY }, { "yes", PERMIT_YES }, { "no", PERMIT_NO }, { NULL, -1 } }; static const struct multistate multistate_compression[] = { #ifdef WITH_ZLIB { "yes", COMP_DELAYED }, { "delayed", COMP_DELAYED }, #endif { "no", COMP_NONE }, { NULL, -1 } }; static const struct multistate multistate_gatewayports[] = { { "clientspecified", 2 }, { "yes", 1 }, { "no", 0 }, { NULL, -1 } }; static const struct multistate multistate_tcpfwd[] = { { "yes", FORWARD_ALLOW }, { "all", FORWARD_ALLOW }, { "no", FORWARD_DENY }, { "remote", FORWARD_REMOTE }, { "local", FORWARD_LOCAL }, { NULL, -1 } }; static int process_server_config_line_depth(ServerOptions *options, char *line, const char *filename, int linenum, int *activep, struct connection_info *connectinfo, int *inc_flags, int depth, struct include_list *includes) { char *str, ***chararrayptr, **charptr, *arg, *arg2, *p, *keyword; int cmdline = 0, *intptr, value, value2, n, port, oactive, r, found; int ca_only = 0; SyslogFacility *log_facility_ptr; LogLevel *log_level_ptr; ServerOpCodes opcode; u_int i, *uintptr, uvalue, flags = 0; size_t len; long long val64; const struct multistate *multistate_ptr; const char *errstr; struct include_item *item; glob_t gbuf; char **oav = NULL, **av; int oac = 0, ac; int ret = -1; /* Strip trailing whitespace. Allow \f (form feed) at EOL only */ if ((len = strlen(line)) == 0) return 0; for (len--; len > 0; len--) { if (strchr(WHITESPACE "\f", line[len]) == NULL) break; line[len] = '\0'; } str = line; if ((keyword = strdelim(&str)) == NULL) return 0; /* Ignore leading whitespace */ if (*keyword == '\0') keyword = strdelim(&str); if (!keyword || !*keyword || *keyword == '#') return 0; if (str == NULL || *str == '\0') { error("%s line %d: no argument after keyword \"%s\"", filename, linenum, keyword); return -1; } intptr = NULL; charptr = NULL; opcode = parse_token(keyword, filename, linenum, &flags); if (argv_split(str, &oac, &oav, 1) != 0) { error("%s line %d: invalid quotes", filename, linenum); return -1; } ac = oac; av = oav; if (activep == NULL) { /* We are processing a command line directive */ cmdline = 1; activep = &cmdline; } if (*activep && opcode != sMatch && opcode != sInclude) debug3("%s:%d setting %s %s", filename, linenum, keyword, str); if (*activep == 0 && !(flags & SSHCFG_MATCH)) { if (connectinfo == NULL) { fatal("%s line %d: Directive '%s' is not allowed " "within a Match block", filename, linenum, keyword); } else { /* this is a directive we have already processed */ ret = 0; goto out; } } switch (opcode) { /* Portable-specific options */ case sUsePAM: intptr = &options->use_pam; goto parse_flag; /* Standard Options */ case sBadOption: goto out; case sPort: /* ignore ports from configfile if cmdline specifies ports */ if (options->ports_from_cmdline) { argv_consume(&ac); break; } if (options->num_ports >= MAX_PORTS) fatal("%s line %d: too many ports.", filename, linenum); arg = argv_next(&ac, &av); if (!arg || *arg == '\0') fatal("%s line %d: missing port number.", filename, linenum); options->ports[options->num_ports++] = a2port(arg); if (options->ports[options->num_ports-1] <= 0) fatal("%s line %d: Badly formatted port number.", filename, linenum); break; case sLoginGraceTime: intptr = &options->login_grace_time; parse_time: arg = argv_next(&ac, &av); if (!arg || *arg == '\0') fatal("%s line %d: missing time value.", filename, linenum); if ((value = convtime(arg)) == -1) fatal("%s line %d: invalid time value.", filename, linenum); if (*activep && *intptr == -1) *intptr = value; break; case sListenAddress: arg = argv_next(&ac, &av); if (arg == NULL || *arg == '\0') fatal("%s line %d: missing address", filename, linenum); /* check for bare IPv6 address: no "[]" and 2 or more ":" */ if (strchr(arg, '[') == NULL && (p = strchr(arg, ':')) != NULL && strchr(p+1, ':') != NULL) { port = 0; p = arg; } else { arg2 = NULL; p = hpdelim(&arg); if (p == NULL) fatal("%s line %d: bad address:port usage", filename, linenum); p = cleanhostname(p); if (arg == NULL) port = 0; else if ((port = a2port(arg)) <= 0) fatal("%s line %d: bad port number", filename, linenum); } /* Optional routing table */ arg2 = NULL; if ((arg = argv_next(&ac, &av)) != NULL) { if (strcmp(arg, "rdomain") != 0 || (arg2 = argv_next(&ac, &av)) == NULL) fatal("%s line %d: bad ListenAddress syntax", filename, linenum); if (!valid_rdomain(arg2)) fatal("%s line %d: bad routing domain", filename, linenum); } queue_listen_addr(options, p, arg2, port); break; case sAddressFamily: intptr = &options->address_family; multistate_ptr = multistate_addressfamily; parse_multistate: arg = argv_next(&ac, &av); if (!arg || *arg == '\0') fatal("%s line %d: missing argument.", filename, linenum); value = -1; for (i = 0; multistate_ptr[i].key != NULL; i++) { if (strcasecmp(arg, multistate_ptr[i].key) == 0) { value = multistate_ptr[i].value; break; } } if (value == -1) fatal("%s line %d: unsupported option \"%s\".", filename, linenum, arg); if (*activep && *intptr == -1) *intptr = value; break; case sHostKeyFile: arg = argv_next(&ac, &av); if (!arg || *arg == '\0') fatal("%s line %d: missing file name.", filename, linenum); if (*activep) { servconf_add_hostkey(filename, linenum, options, arg, 1); } break; case sHostKeyAgent: charptr = &options->host_key_agent; arg = argv_next(&ac, &av); if (!arg || *arg == '\0') fatal("%s line %d: missing socket name.", filename, linenum); if (*activep && *charptr == NULL) *charptr = !strcmp(arg, SSH_AUTHSOCKET_ENV_NAME) ? xstrdup(arg) : derelativise_path(arg); break; case sHostCertificate: arg = argv_next(&ac, &av); if (!arg || *arg == '\0') fatal("%s line %d: missing file name.", filename, linenum); if (*activep) servconf_add_hostcert(filename, linenum, options, arg); break; case sPidFile: charptr = &options->pid_file; parse_filename: arg = argv_next(&ac, &av); if (!arg || *arg == '\0') fatal("%s line %d: missing file name.", filename, linenum); if (*activep && *charptr == NULL) { *charptr = derelativise_path(arg); /* increase optional counter */ if (intptr != NULL) *intptr = *intptr + 1; } break; case sModuliFile: charptr = &options->moduli_file; goto parse_filename; case sPermitRootLogin: intptr = &options->permit_root_login; multistate_ptr = multistate_permitrootlogin; goto parse_multistate; case sIgnoreRhosts: intptr = &options->ignore_rhosts; multistate_ptr = multistate_ignore_rhosts; goto parse_multistate; case sIgnoreUserKnownHosts: intptr = &options->ignore_user_known_hosts; parse_flag: multistate_ptr = multistate_flag; goto parse_multistate; case sHostbasedAuthentication: intptr = &options->hostbased_authentication; goto parse_flag; case sHostbasedUsesNameFromPacketOnly: intptr = &options->hostbased_uses_name_from_packet_only; goto parse_flag; case sHostbasedAcceptedAlgorithms: charptr = &options->hostbased_accepted_algos; ca_only = 0; parse_pubkey_algos: arg = argv_next(&ac, &av); if (!arg || *arg == '\0') fatal("%s line %d: Missing argument.", filename, linenum); if (*arg != '-' && !sshkey_names_valid2(*arg == '+' || *arg == '^' ? arg + 1 : arg, 1, ca_only)) fatal("%s line %d: Bad key types '%s'.", filename, linenum, arg ? arg : ""); if (*activep && *charptr == NULL) *charptr = xstrdup(arg); break; case sHostKeyAlgorithms: charptr = &options->hostkeyalgorithms; ca_only = 0; goto parse_pubkey_algos; case sCASignatureAlgorithms: charptr = &options->ca_sign_algorithms; ca_only = 1; goto parse_pubkey_algos; case sPubkeyAuthentication: intptr = &options->pubkey_authentication; ca_only = 0; goto parse_flag; case sPubkeyAcceptedAlgorithms: charptr = &options->pubkey_accepted_algos; ca_only = 0; goto parse_pubkey_algos; case sPubkeyAuthOptions: intptr = &options->pubkey_auth_options; value = 0; while ((arg = argv_next(&ac, &av)) != NULL) { if (strcasecmp(arg, "none") == 0) continue; if (strcasecmp(arg, "touch-required") == 0) value |= PUBKEYAUTH_TOUCH_REQUIRED; else if (strcasecmp(arg, "verify-required") == 0) value |= PUBKEYAUTH_VERIFY_REQUIRED; else { error("%s line %d: unsupported %s option %s", filename, linenum, keyword, arg); goto out; } } if (*activep && *intptr == -1) *intptr = value; break; case sKerberosAuthentication: intptr = &options->kerberos_authentication; goto parse_flag; case sKerberosOrLocalPasswd: intptr = &options->kerberos_or_local_passwd; goto parse_flag; case sKerberosTicketCleanup: intptr = &options->kerberos_ticket_cleanup; goto parse_flag; case sKerberosGetAFSToken: intptr = &options->kerberos_get_afs_token; goto parse_flag; case sGssAuthentication: intptr = &options->gss_authentication; goto parse_flag; case sGssCleanupCreds: intptr = &options->gss_cleanup_creds; goto parse_flag; case sGssStrictAcceptor: intptr = &options->gss_strict_acceptor; goto parse_flag; case sPasswordAuthentication: intptr = &options->password_authentication; goto parse_flag; case sKbdInteractiveAuthentication: intptr = &options->kbd_interactive_authentication; goto parse_flag; case sPrintMotd: intptr = &options->print_motd; goto parse_flag; case sPrintLastLog: intptr = &options->print_lastlog; goto parse_flag; case sX11Forwarding: intptr = &options->x11_forwarding; goto parse_flag; case sX11DisplayOffset: intptr = &options->x11_display_offset; parse_int: arg = argv_next(&ac, &av); if ((errstr = atoi_err(arg, &value)) != NULL) fatal("%s line %d: %s integer value %s.", filename, linenum, keyword, errstr); if (*activep && *intptr == -1) *intptr = value; break; case sX11UseLocalhost: intptr = &options->x11_use_localhost; goto parse_flag; case sXAuthLocation: charptr = &options->xauth_location; goto parse_filename; case sPermitTTY: intptr = &options->permit_tty; goto parse_flag; case sPermitUserRC: intptr = &options->permit_user_rc; goto parse_flag; case sStrictModes: intptr = &options->strict_modes; goto parse_flag; case sTCPKeepAlive: intptr = &options->tcp_keep_alive; goto parse_flag; case sEmptyPasswd: intptr = &options->permit_empty_passwd; goto parse_flag; case sPermitUserEnvironment: intptr = &options->permit_user_env; charptr = &options->permit_user_env_allowlist; arg = argv_next(&ac, &av); if (!arg || *arg == '\0') fatal("%s line %d: %s missing argument.", filename, linenum, keyword); value = 0; p = NULL; if (strcmp(arg, "yes") == 0) value = 1; else if (strcmp(arg, "no") == 0) value = 0; else { /* Pattern-list specified */ value = 1; p = xstrdup(arg); } if (*activep && *intptr == -1) { *intptr = value; *charptr = p; p = NULL; } free(p); break; case sCompression: intptr = &options->compression; multistate_ptr = multistate_compression; goto parse_multistate; case sRekeyLimit: arg = argv_next(&ac, &av); if (!arg || *arg == '\0') fatal("%s line %d: %s missing argument.", filename, linenum, keyword); if (strcmp(arg, "default") == 0) { val64 = 0; } else { if (scan_scaled(arg, &val64) == -1) fatal("%.200s line %d: Bad %s number '%s': %s", filename, linenum, keyword, arg, strerror(errno)); if (val64 != 0 && val64 < 16) fatal("%.200s line %d: %s too small", filename, linenum, keyword); } if (*activep && options->rekey_limit == -1) options->rekey_limit = val64; if (ac != 0) { /* optional rekey interval present */ if (strcmp(av[0], "none") == 0) { (void)argv_next(&ac, &av); /* discard */ break; } intptr = &options->rekey_interval; goto parse_time; } break; case sGatewayPorts: intptr = &options->fwd_opts.gateway_ports; multistate_ptr = multistate_gatewayports; goto parse_multistate; case sUseDNS: intptr = &options->use_dns; goto parse_flag; case sLogFacility: log_facility_ptr = &options->log_facility; arg = argv_next(&ac, &av); value = log_facility_number(arg); if (value == SYSLOG_FACILITY_NOT_SET) fatal("%.200s line %d: unsupported log facility '%s'", filename, linenum, arg ? arg : ""); if (*log_facility_ptr == -1) *log_facility_ptr = (SyslogFacility) value; break; case sLogLevel: log_level_ptr = &options->log_level; arg = argv_next(&ac, &av); value = log_level_number(arg); if (value == SYSLOG_LEVEL_NOT_SET) fatal("%.200s line %d: unsupported log level '%s'", filename, linenum, arg ? arg : ""); if (*activep && *log_level_ptr == -1) *log_level_ptr = (LogLevel) value; break; case sLogVerbose: found = options->num_log_verbose == 0; i = 0; while ((arg = argv_next(&ac, &av)) != NULL) { if (*arg == '\0') { error("%s line %d: keyword %s empty argument", filename, linenum, keyword); goto out; } /* Allow "none" only in first position */ if (strcasecmp(arg, "none") == 0) { if (i > 0 || ac > 0) { error("%s line %d: keyword %s \"none\" " "argument must appear alone.", filename, linenum, keyword); goto out; } } i++; if (!found || !*activep) continue; opt_array_append(filename, linenum, keyword, &options->log_verbose, &options->num_log_verbose, arg); } break; case sAllowTcpForwarding: intptr = &options->allow_tcp_forwarding; multistate_ptr = multistate_tcpfwd; goto parse_multistate; case sAllowStreamLocalForwarding: intptr = &options->allow_streamlocal_forwarding; multistate_ptr = multistate_tcpfwd; goto parse_multistate; case sAllowAgentForwarding: intptr = &options->allow_agent_forwarding; goto parse_flag; case sDisableForwarding: intptr = &options->disable_forwarding; goto parse_flag; case sAllowUsers: chararrayptr = &options->allow_users; uintptr = &options->num_allow_users; parse_allowdenyusers: while ((arg = argv_next(&ac, &av)) != NULL) { if (*arg == '\0' || match_user(NULL, NULL, NULL, arg) == -1) fatal("%s line %d: invalid %s pattern: \"%s\"", filename, linenum, keyword, arg); if (!*activep) continue; opt_array_append(filename, linenum, keyword, chararrayptr, uintptr, arg); } break; case sDenyUsers: chararrayptr = &options->deny_users; uintptr = &options->num_deny_users; goto parse_allowdenyusers; case sAllowGroups: chararrayptr = &options->allow_groups; uintptr = &options->num_allow_groups; parse_allowdenygroups: while ((arg = argv_next(&ac, &av)) != NULL) { if (*arg == '\0') fatal("%s line %d: empty %s pattern", filename, linenum, keyword); if (!*activep) continue; opt_array_append(filename, linenum, keyword, chararrayptr, uintptr, arg); } break; case sDenyGroups: chararrayptr = &options->deny_groups; uintptr = &options->num_deny_groups; goto parse_allowdenygroups; case sCiphers: arg = argv_next(&ac, &av); if (!arg || *arg == '\0') fatal("%s line %d: %s missing argument.", filename, linenum, keyword); if (*arg != '-' && !ciphers_valid(*arg == '+' || *arg == '^' ? arg + 1 : arg)) fatal("%s line %d: Bad SSH2 cipher spec '%s'.", filename, linenum, arg ? arg : ""); if (options->ciphers == NULL) options->ciphers = xstrdup(arg); break; case sMacs: arg = argv_next(&ac, &av); if (!arg || *arg == '\0') fatal("%s line %d: %s missing argument.", filename, linenum, keyword); if (*arg != '-' && !mac_valid(*arg == '+' || *arg == '^' ? arg + 1 : arg)) fatal("%s line %d: Bad SSH2 mac spec '%s'.", filename, linenum, arg ? arg : ""); if (options->macs == NULL) options->macs = xstrdup(arg); break; case sKexAlgorithms: arg = argv_next(&ac, &av); if (!arg || *arg == '\0') fatal("%s line %d: %s missing argument.", filename, linenum, keyword); if (*arg != '-' && !kex_names_valid(*arg == '+' || *arg == '^' ? arg + 1 : arg)) fatal("%s line %d: Bad SSH2 KexAlgorithms '%s'.", filename, linenum, arg ? arg : ""); if (options->kex_algorithms == NULL) options->kex_algorithms = xstrdup(arg); break; case sSubsystem: - if (options->num_subsystems >= MAX_SUBSYSTEMS) { - fatal("%s line %d: too many subsystems defined.", - filename, linenum); - } arg = argv_next(&ac, &av); if (!arg || *arg == '\0') fatal("%s line %d: %s missing argument.", filename, linenum, keyword); if (!*activep) { - arg = argv_next(&ac, &av); + argv_consume(&ac); + break; + } + found = 0; + for (i = 0; i < options->num_subsystems; i++) { + if (strcmp(arg, options->subsystem_name[i]) == 0) { + found = 1; + break; + } + } + if (found) { + debug("%s line %d: Subsystem '%s' already defined.", + filename, linenum, arg); + argv_consume(&ac); break; } - for (i = 0; i < options->num_subsystems; i++) - if (strcmp(arg, options->subsystem_name[i]) == 0) - fatal("%s line %d: Subsystem '%s' " - "already defined.", filename, linenum, arg); + options->subsystem_name = xrecallocarray( + options->subsystem_name, options->num_subsystems, + options->num_subsystems + 1, + sizeof(*options->subsystem_name)); + options->subsystem_command = xrecallocarray( + options->subsystem_command, options->num_subsystems, + options->num_subsystems + 1, + sizeof(*options->subsystem_command)); + options->subsystem_args = xrecallocarray( + options->subsystem_args, options->num_subsystems, + options->num_subsystems + 1, + sizeof(*options->subsystem_args)); options->subsystem_name[options->num_subsystems] = xstrdup(arg); arg = argv_next(&ac, &av); - if (!arg || *arg == '\0') + if (!arg || *arg == '\0') { fatal("%s line %d: Missing subsystem command.", filename, linenum); - options->subsystem_command[options->num_subsystems] = xstrdup(arg); - - /* Collect arguments (separate to executable) */ - p = xstrdup(arg); - len = strlen(p) + 1; - while ((arg = argv_next(&ac, &av)) != NULL) { - len += 1 + strlen(arg); - p = xreallocarray(p, 1, len); - strlcat(p, " ", len); - strlcat(p, arg, len); } - options->subsystem_args[options->num_subsystems] = p; + options->subsystem_command[options->num_subsystems] = + xstrdup(arg); + /* Collect arguments (separate to executable) */ + arg = argv_assemble(1, &arg); /* quote command correctly */ + arg2 = argv_assemble(ac, av); /* rest of command */ + xasprintf(&options->subsystem_args[options->num_subsystems], + "%s %s", arg, arg2); + free(arg2); + argv_consume(&ac); options->num_subsystems++; break; case sMaxStartups: arg = argv_next(&ac, &av); if (!arg || *arg == '\0') fatal("%s line %d: %s missing argument.", filename, linenum, keyword); if ((n = sscanf(arg, "%d:%d:%d", &options->max_startups_begin, &options->max_startups_rate, &options->max_startups)) == 3) { if (options->max_startups_begin > options->max_startups || options->max_startups_rate > 100 || options->max_startups_rate < 1) fatal("%s line %d: Invalid %s spec.", filename, linenum, keyword); } else if (n != 1) fatal("%s line %d: Invalid %s spec.", filename, linenum, keyword); else options->max_startups = options->max_startups_begin; if (options->max_startups <= 0 || options->max_startups_begin <= 0) fatal("%s line %d: Invalid %s spec.", filename, linenum, keyword); break; case sPerSourceNetBlockSize: arg = argv_next(&ac, &av); if (!arg || *arg == '\0') fatal("%s line %d: %s missing argument.", filename, linenum, keyword); switch (n = sscanf(arg, "%d:%d", &value, &value2)) { case 2: if (value2 < 0 || value2 > 128) n = -1; /* FALLTHROUGH */ case 1: if (value < 0 || value > 32) n = -1; } if (n != 1 && n != 2) fatal("%s line %d: Invalid %s spec.", filename, linenum, keyword); if (*activep) { options->per_source_masklen_ipv4 = value; options->per_source_masklen_ipv6 = value2; } break; case sPerSourceMaxStartups: arg = argv_next(&ac, &av); if (!arg || *arg == '\0') fatal("%s line %d: %s missing argument.", filename, linenum, keyword); if (strcmp(arg, "none") == 0) { /* no limit */ value = INT_MAX; } else { if ((errstr = atoi_err(arg, &value)) != NULL) fatal("%s line %d: %s integer value %s.", filename, linenum, keyword, errstr); } - if (*activep) + if (*activep && options->per_source_max_startups == -1) options->per_source_max_startups = value; break; case sMaxAuthTries: intptr = &options->max_authtries; goto parse_int; case sMaxSessions: intptr = &options->max_sessions; goto parse_int; case sBanner: charptr = &options->banner; goto parse_filename; /* * These options can contain %X options expanded at * connect time, so that you can specify paths like: * * AuthorizedKeysFile /etc/ssh_keys/%u */ case sAuthorizedKeysFile: uvalue = options->num_authkeys_files; while ((arg = argv_next(&ac, &av)) != NULL) { if (*arg == '\0') { error("%s line %d: keyword %s empty argument", filename, linenum, keyword); goto out; } arg2 = tilde_expand_filename(arg, getuid()); if (*activep && uvalue == 0) { opt_array_append(filename, linenum, keyword, &options->authorized_keys_files, &options->num_authkeys_files, arg2); } free(arg2); } break; case sAuthorizedPrincipalsFile: charptr = &options->authorized_principals_file; arg = argv_next(&ac, &av); if (!arg || *arg == '\0') fatal("%s line %d: %s missing argument.", filename, linenum, keyword); if (*activep && *charptr == NULL) { *charptr = tilde_expand_filename(arg, getuid()); /* increase optional counter */ if (intptr != NULL) *intptr = *intptr + 1; } break; case sClientAliveInterval: intptr = &options->client_alive_interval; goto parse_time; case sClientAliveCountMax: intptr = &options->client_alive_count_max; goto parse_int; case sAcceptEnv: while ((arg = argv_next(&ac, &av)) != NULL) { if (*arg == '\0' || strchr(arg, '=') != NULL) fatal("%s line %d: Invalid environment name.", filename, linenum); if (!*activep) continue; opt_array_append(filename, linenum, keyword, &options->accept_env, &options->num_accept_env, arg); } break; case sSetEnv: uvalue = options->num_setenv; while ((arg = argv_next(&ac, &av)) != NULL) { if (*arg == '\0' || strchr(arg, '=') == NULL) fatal("%s line %d: Invalid environment.", filename, linenum); if (!*activep || uvalue != 0) continue; if (lookup_setenv_in_list(arg, options->setenv, options->num_setenv) != NULL) { debug2("%s line %d: ignoring duplicate env " "name \"%.64s\"", filename, linenum, arg); continue; } opt_array_append(filename, linenum, keyword, &options->setenv, &options->num_setenv, arg); } break; case sPermitTunnel: intptr = &options->permit_tun; arg = argv_next(&ac, &av); if (!arg || *arg == '\0') fatal("%s line %d: %s missing argument.", filename, linenum, keyword); value = -1; for (i = 0; tunmode_desc[i].val != -1; i++) if (strcmp(tunmode_desc[i].text, arg) == 0) { value = tunmode_desc[i].val; break; } if (value == -1) fatal("%s line %d: bad %s argument %s", filename, linenum, keyword, arg); if (*activep && *intptr == -1) *intptr = value; break; case sInclude: if (cmdline) { fatal("Include directive not supported as a " "command-line option"); } value = 0; while ((arg2 = argv_next(&ac, &av)) != NULL) { if (*arg2 == '\0') { error("%s line %d: keyword %s empty argument", filename, linenum, keyword); goto out; } value++; found = 0; if (*arg2 != '/' && *arg2 != '~') { xasprintf(&arg, "%s/%s", SSHDIR, arg2); } else arg = xstrdup(arg2); /* * Don't let included files clobber the containing * file's Match state. */ oactive = *activep; /* consult cache of include files */ TAILQ_FOREACH(item, includes, entry) { if (strcmp(item->selector, arg) != 0) continue; if (item->filename != NULL) { parse_server_config_depth(options, item->filename, item->contents, includes, connectinfo, (*inc_flags & SSHCFG_MATCH_ONLY ? SSHCFG_MATCH_ONLY : (oactive ? 0 : SSHCFG_NEVERMATCH)), activep, depth + 1); } found = 1; *activep = oactive; } if (found != 0) { free(arg); continue; } /* requested glob was not in cache */ debug2("%s line %d: new include %s", filename, linenum, arg); if ((r = glob(arg, 0, NULL, &gbuf)) != 0) { if (r != GLOB_NOMATCH) { fatal("%s line %d: include \"%s\" glob " "failed", filename, linenum, arg); } /* * If no entry matched then record a * placeholder to skip later glob calls. */ debug2("%s line %d: no match for %s", filename, linenum, arg); item = xcalloc(1, sizeof(*item)); item->selector = strdup(arg); TAILQ_INSERT_TAIL(includes, item, entry); } if (gbuf.gl_pathc > INT_MAX) fatal_f("too many glob results"); for (n = 0; n < (int)gbuf.gl_pathc; n++) { debug2("%s line %d: including %s", filename, linenum, gbuf.gl_pathv[n]); item = xcalloc(1, sizeof(*item)); item->selector = strdup(arg); item->filename = strdup(gbuf.gl_pathv[n]); if ((item->contents = sshbuf_new()) == NULL) fatal_f("sshbuf_new failed"); load_server_config(item->filename, item->contents); parse_server_config_depth(options, item->filename, item->contents, includes, connectinfo, (*inc_flags & SSHCFG_MATCH_ONLY ? SSHCFG_MATCH_ONLY : (oactive ? 0 : SSHCFG_NEVERMATCH)), activep, depth + 1); *activep = oactive; TAILQ_INSERT_TAIL(includes, item, entry); } globfree(&gbuf); free(arg); } if (value == 0) { fatal("%s line %d: %s missing filename argument", filename, linenum, keyword); } break; case sMatch: if (cmdline) fatal("Match directive not supported as a command-line " "option"); value = match_cfg_line(&str, linenum, (*inc_flags & SSHCFG_NEVERMATCH ? NULL : connectinfo)); if (value < 0) fatal("%s line %d: Bad Match condition", filename, linenum); *activep = (*inc_flags & SSHCFG_NEVERMATCH) ? 0 : value; /* * The MATCH_ONLY flag is applicable only until the first * match block. */ *inc_flags &= ~SSHCFG_MATCH_ONLY; /* * If match_cfg_line() didn't consume all its arguments then * arrange for the extra arguments check below to fail. */ if (str == NULL || *str == '\0') argv_consume(&ac); break; case sPermitListen: case sPermitOpen: if (opcode == sPermitListen) { uintptr = &options->num_permitted_listens; chararrayptr = &options->permitted_listens; } else { uintptr = &options->num_permitted_opens; chararrayptr = &options->permitted_opens; } arg = argv_next(&ac, &av); if (!arg || *arg == '\0') fatal("%s line %d: %s missing argument.", filename, linenum, keyword); uvalue = *uintptr; /* modified later */ if (strcmp(arg, "any") == 0 || strcmp(arg, "none") == 0) { if (*activep && uvalue == 0) { *uintptr = 1; *chararrayptr = xcalloc(1, sizeof(**chararrayptr)); (*chararrayptr)[0] = xstrdup(arg); } break; } for (; arg != NULL && *arg != '\0'; arg = argv_next(&ac, &av)) { if (opcode == sPermitListen && strchr(arg, ':') == NULL) { /* * Allow bare port number for PermitListen * to indicate a wildcard listen host. */ xasprintf(&arg2, "*:%s", arg); } else { arg2 = xstrdup(arg); p = hpdelim(&arg); if (p == NULL) { fatal("%s line %d: %s missing host", filename, linenum, keyword); } p = cleanhostname(p); } if (arg == NULL || ((port = permitopen_port(arg)) < 0)) { fatal("%s line %d: %s bad port number", filename, linenum, keyword); } if (*activep && uvalue == 0) { opt_array_append(filename, linenum, keyword, chararrayptr, uintptr, arg2); } free(arg2); } break; case sForceCommand: if (str == NULL || *str == '\0') fatal("%s line %d: %s missing argument.", filename, linenum, keyword); len = strspn(str, WHITESPACE); if (*activep && options->adm_forced_command == NULL) options->adm_forced_command = xstrdup(str + len); argv_consume(&ac); break; case sChrootDirectory: charptr = &options->chroot_directory; arg = argv_next(&ac, &av); if (!arg || *arg == '\0') fatal("%s line %d: %s missing argument.", filename, linenum, keyword); if (*activep && *charptr == NULL) *charptr = xstrdup(arg); break; case sTrustedUserCAKeys: charptr = &options->trusted_user_ca_keys; goto parse_filename; case sRevokedKeys: charptr = &options->revoked_keys_file; goto parse_filename; case sSecurityKeyProvider: charptr = &options->sk_provider; arg = argv_next(&ac, &av); if (!arg || *arg == '\0') fatal("%s line %d: %s missing argument.", filename, linenum, keyword); if (*activep && *charptr == NULL) { *charptr = strcasecmp(arg, "internal") == 0 ? xstrdup(arg) : derelativise_path(arg); /* increase optional counter */ if (intptr != NULL) *intptr = *intptr + 1; } break; case sIPQoS: arg = argv_next(&ac, &av); if (!arg || *arg == '\0') fatal("%s line %d: %s missing argument.", filename, linenum, keyword); if ((value = parse_ipqos(arg)) == -1) fatal("%s line %d: Bad %s value: %s", filename, linenum, keyword, arg); arg = argv_next(&ac, &av); if (arg == NULL) value2 = value; else if ((value2 = parse_ipqos(arg)) == -1) fatal("%s line %d: Bad %s value: %s", filename, linenum, keyword, arg); if (*activep) { options->ip_qos_interactive = value; options->ip_qos_bulk = value2; } break; case sVersionAddendum: if (str == NULL || *str == '\0') fatal("%s line %d: %s missing argument.", filename, linenum, keyword); len = strspn(str, WHITESPACE); if (strchr(str + len, '\r') != NULL) { fatal("%.200s line %d: Invalid %s argument", filename, linenum, keyword); } if ((arg = strchr(line, '#')) != NULL) { *arg = '\0'; rtrim(line); } if (*activep && options->version_addendum == NULL) { if (strcasecmp(str + len, "none") == 0) options->version_addendum = xstrdup(""); else options->version_addendum = xstrdup(str + len); } argv_consume(&ac); break; case sAuthorizedKeysCommand: charptr = &options->authorized_keys_command; parse_command: len = strspn(str, WHITESPACE); if (str[len] != '/' && strcasecmp(str + len, "none") != 0) { fatal("%.200s line %d: %s must be an absolute path", filename, linenum, keyword); } if (*activep && *charptr == NULL) *charptr = xstrdup(str + len); argv_consume(&ac); break; case sAuthorizedKeysCommandUser: charptr = &options->authorized_keys_command_user; parse_localuser: arg = argv_next(&ac, &av); if (!arg || *arg == '\0') { fatal("%s line %d: missing %s argument.", filename, linenum, keyword); } if (*activep && *charptr == NULL) *charptr = xstrdup(arg); break; case sAuthorizedPrincipalsCommand: charptr = &options->authorized_principals_command; goto parse_command; case sAuthorizedPrincipalsCommandUser: charptr = &options->authorized_principals_command_user; goto parse_localuser; case sAuthenticationMethods: found = options->num_auth_methods == 0; value = 0; /* seen "any" pseudo-method */ value2 = 0; /* successfully parsed any method */ while ((arg = argv_next(&ac, &av)) != NULL) { if (strcmp(arg, "any") == 0) { if (options->num_auth_methods > 0) { fatal("%s line %d: \"any\" must " "appear alone in %s", filename, linenum, keyword); } value = 1; } else if (value) { fatal("%s line %d: \"any\" must appear " "alone in %s", filename, linenum, keyword); } else if (auth2_methods_valid(arg, 0) != 0) { fatal("%s line %d: invalid %s method list.", filename, linenum, keyword); } value2 = 1; if (!found || !*activep) continue; opt_array_append(filename, linenum, keyword, &options->auth_methods, &options->num_auth_methods, arg); } if (value2 == 0) { fatal("%s line %d: no %s specified", filename, linenum, keyword); } break; case sStreamLocalBindMask: arg = argv_next(&ac, &av); if (!arg || *arg == '\0') fatal("%s line %d: %s missing argument.", filename, linenum, keyword); /* Parse mode in octal format */ value = strtol(arg, &p, 8); if (arg == p || value < 0 || value > 0777) fatal("%s line %d: Invalid %s.", filename, linenum, keyword); if (*activep) options->fwd_opts.streamlocal_bind_mask = (mode_t)value; break; case sStreamLocalBindUnlink: intptr = &options->fwd_opts.streamlocal_bind_unlink; goto parse_flag; case sFingerprintHash: arg = argv_next(&ac, &av); if (!arg || *arg == '\0') fatal("%s line %d: %s missing argument.", filename, linenum, keyword); if ((value = ssh_digest_alg_by_name(arg)) == -1) fatal("%.200s line %d: Invalid %s algorithm \"%s\".", filename, linenum, keyword, arg); if (*activep) options->fingerprint_hash = value; break; case sExposeAuthInfo: intptr = &options->expose_userauth_info; goto parse_flag; case sRDomain: #if !defined(__OpenBSD__) && !defined(HAVE_SYS_SET_PROCESS_RDOMAIN) fatal("%s line %d: setting RDomain not supported on this " "platform.", filename, linenum); #endif charptr = &options->routing_domain; arg = argv_next(&ac, &av); if (!arg || *arg == '\0') fatal("%s line %d: %s missing argument.", filename, linenum, keyword); if (strcasecmp(arg, "none") != 0 && strcmp(arg, "%D") != 0 && !valid_rdomain(arg)) fatal("%s line %d: invalid routing domain", filename, linenum); if (*activep && *charptr == NULL) *charptr = xstrdup(arg); break; case sRequiredRSASize: intptr = &options->required_rsa_size; goto parse_int; case sChannelTimeout: uvalue = options->num_channel_timeouts; i = 0; while ((arg = argv_next(&ac, &av)) != NULL) { /* Allow "none" only in first position */ if (strcasecmp(arg, "none") == 0) { if (i > 0 || ac > 0) { error("%s line %d: keyword %s \"none\" " "argument must appear alone.", filename, linenum, keyword); goto out; } } else if (parse_timeout(arg, NULL, NULL) != 0) { fatal("%s line %d: invalid channel timeout %s", filename, linenum, arg); } if (!*activep || uvalue != 0) continue; opt_array_append(filename, linenum, keyword, &options->channel_timeouts, &options->num_channel_timeouts, arg); } break; case sUnusedConnectionTimeout: intptr = &options->unused_connection_timeout; /* peek at first arg for "none" so we can reuse parse_time */ if (av[0] != NULL && strcasecmp(av[0], "none") == 0) { (void)argv_next(&ac, &av); /* consume arg */ if (*activep) *intptr = 0; break; } goto parse_time; case sDeprecated: case sIgnore: case sUnsupported: do_log2(opcode == sIgnore ? SYSLOG_LEVEL_DEBUG2 : SYSLOG_LEVEL_INFO, "%s line %d: %s option %s", filename, linenum, opcode == sUnsupported ? "Unsupported" : "Deprecated", keyword); argv_consume(&ac); break; default: fatal("%s line %d: Missing handler for opcode %s (%d)", filename, linenum, keyword, opcode); } /* Check that there is no garbage at end of line. */ if (ac > 0) { error("%.200s line %d: keyword %s extra arguments " "at end of line", filename, linenum, keyword); goto out; } /* success */ ret = 0; out: argv_free(oav, oac); return ret; } int process_server_config_line(ServerOptions *options, char *line, const char *filename, int linenum, int *activep, struct connection_info *connectinfo, struct include_list *includes) { int inc_flags = 0; return process_server_config_line_depth(options, line, filename, linenum, activep, connectinfo, &inc_flags, 0, includes); } /* Reads the server configuration file. */ void load_server_config(const char *filename, struct sshbuf *conf) { struct stat st; char *line = NULL, *cp; size_t linesize = 0; FILE *f; int r; debug2_f("filename %s", filename); if ((f = fopen(filename, "r")) == NULL) { perror(filename); exit(1); } sshbuf_reset(conf); /* grow buffer, so realloc is avoided for large config files */ if (fstat(fileno(f), &st) == 0 && st.st_size > 0 && (r = sshbuf_allocate(conf, st.st_size)) != 0) fatal_fr(r, "allocate"); while (getline(&line, &linesize, f) != -1) { /* * Strip whitespace * NB - preserve newlines, they are needed to reproduce * line numbers later for error messages */ cp = line + strspn(line, " \t\r"); if ((r = sshbuf_put(conf, cp, strlen(cp))) != 0) fatal_fr(r, "sshbuf_put"); } free(line); if ((r = sshbuf_put_u8(conf, 0)) != 0) fatal_fr(r, "sshbuf_put_u8"); fclose(f); debug2_f("done config len = %zu", sshbuf_len(conf)); } void parse_server_match_config(ServerOptions *options, struct include_list *includes, struct connection_info *connectinfo) { ServerOptions mo; initialize_server_options(&mo); parse_server_config(&mo, "reprocess config", cfg, includes, connectinfo, 0); copy_set_server_options(options, &mo, 0); } int parse_server_match_testspec(struct connection_info *ci, char *spec) { char *p; while ((p = strsep(&spec, ",")) && *p != '\0') { if (strncmp(p, "addr=", 5) == 0) { ci->address = xstrdup(p + 5); } else if (strncmp(p, "host=", 5) == 0) { ci->host = xstrdup(p + 5); } else if (strncmp(p, "user=", 5) == 0) { ci->user = xstrdup(p + 5); } else if (strncmp(p, "laddr=", 6) == 0) { ci->laddress = xstrdup(p + 6); } else if (strncmp(p, "rdomain=", 8) == 0) { ci->rdomain = xstrdup(p + 8); } else if (strncmp(p, "lport=", 6) == 0) { ci->lport = a2port(p + 6); if (ci->lport == -1) { fprintf(stderr, "Invalid port '%s' in test mode" " specification %s\n", p+6, p); return -1; } } else { fprintf(stderr, "Invalid test mode specification %s\n", p); return -1; } } return 0; } +void +servconf_merge_subsystems(ServerOptions *dst, ServerOptions *src) +{ + u_int i, j, found; + + for (i = 0; i < src->num_subsystems; i++) { + found = 0; + for (j = 0; j < dst->num_subsystems; j++) { + if (strcmp(src->subsystem_name[i], + dst->subsystem_name[j]) == 0) { + found = 1; + break; + } + } + if (found) { + debug_f("override \"%s\"", dst->subsystem_name[j]); + free(dst->subsystem_command[j]); + free(dst->subsystem_args[j]); + dst->subsystem_command[j] = + xstrdup(src->subsystem_command[i]); + dst->subsystem_args[j] = + xstrdup(src->subsystem_args[i]); + continue; + } + debug_f("add \"%s\"", src->subsystem_name[i]); + dst->subsystem_name = xrecallocarray( + dst->subsystem_name, dst->num_subsystems, + dst->num_subsystems + 1, sizeof(*dst->subsystem_name)); + dst->subsystem_command = xrecallocarray( + dst->subsystem_command, dst->num_subsystems, + dst->num_subsystems + 1, sizeof(*dst->subsystem_command)); + dst->subsystem_args = xrecallocarray( + dst->subsystem_args, dst->num_subsystems, + dst->num_subsystems + 1, sizeof(*dst->subsystem_args)); + j = dst->num_subsystems++; + dst->subsystem_name[j] = xstrdup(src->subsystem_name[i]); + dst->subsystem_command[j] = xstrdup(src->subsystem_command[i]); + dst->subsystem_args[j] = xstrdup(src->subsystem_args[i]); + } +} + /* * Copy any supported values that are set. * * If the preauth flag is set, we do not bother copying the string or * array values that are not used pre-authentication, because any that we * do use must be explicitly sent in mm_getpwnamallow(). */ void copy_set_server_options(ServerOptions *dst, ServerOptions *src, int preauth) { #define M_CP_INTOPT(n) do {\ if (src->n != -1) \ dst->n = src->n; \ } while (0) M_CP_INTOPT(password_authentication); M_CP_INTOPT(gss_authentication); M_CP_INTOPT(pubkey_authentication); M_CP_INTOPT(pubkey_auth_options); M_CP_INTOPT(kerberos_authentication); M_CP_INTOPT(hostbased_authentication); M_CP_INTOPT(hostbased_uses_name_from_packet_only); M_CP_INTOPT(kbd_interactive_authentication); M_CP_INTOPT(permit_root_login); M_CP_INTOPT(permit_empty_passwd); M_CP_INTOPT(ignore_rhosts); M_CP_INTOPT(allow_tcp_forwarding); M_CP_INTOPT(allow_streamlocal_forwarding); M_CP_INTOPT(allow_agent_forwarding); M_CP_INTOPT(disable_forwarding); M_CP_INTOPT(expose_userauth_info); M_CP_INTOPT(permit_tun); M_CP_INTOPT(fwd_opts.gateway_ports); M_CP_INTOPT(fwd_opts.streamlocal_bind_unlink); M_CP_INTOPT(x11_display_offset); M_CP_INTOPT(x11_forwarding); M_CP_INTOPT(x11_use_localhost); M_CP_INTOPT(permit_tty); M_CP_INTOPT(permit_user_rc); M_CP_INTOPT(max_sessions); M_CP_INTOPT(max_authtries); M_CP_INTOPT(client_alive_count_max); M_CP_INTOPT(client_alive_interval); M_CP_INTOPT(ip_qos_interactive); M_CP_INTOPT(ip_qos_bulk); M_CP_INTOPT(rekey_limit); M_CP_INTOPT(rekey_interval); M_CP_INTOPT(log_level); M_CP_INTOPT(required_rsa_size); M_CP_INTOPT(unused_connection_timeout); /* * The bind_mask is a mode_t that may be unsigned, so we can't use * M_CP_INTOPT - it does a signed comparison that causes compiler * warnings. */ if (src->fwd_opts.streamlocal_bind_mask != (mode_t)-1) { dst->fwd_opts.streamlocal_bind_mask = src->fwd_opts.streamlocal_bind_mask; } /* M_CP_STROPT and M_CP_STRARRAYOPT should not appear before here */ #define M_CP_STROPT(n) do {\ if (src->n != NULL && dst->n != src->n) { \ free(dst->n); \ dst->n = src->n; \ } \ } while(0) #define M_CP_STRARRAYOPT(s, num_s) do {\ u_int i; \ if (src->num_s != 0) { \ for (i = 0; i < dst->num_s; i++) \ free(dst->s[i]); \ free(dst->s); \ dst->s = xcalloc(src->num_s, sizeof(*dst->s)); \ for (i = 0; i < src->num_s; i++) \ dst->s[i] = xstrdup(src->s[i]); \ dst->num_s = src->num_s; \ } \ } while(0) /* See comment in servconf.h */ COPY_MATCH_STRING_OPTS(); /* Arguments that accept '+...' need to be expanded */ assemble_algorithms(dst); /* * The only things that should be below this point are string options * which are only used after authentication. */ if (preauth) return; /* These options may be "none" to clear a global setting */ M_CP_STROPT(adm_forced_command); if (option_clear_or_none(dst->adm_forced_command)) { free(dst->adm_forced_command); dst->adm_forced_command = NULL; } M_CP_STROPT(chroot_directory); if (option_clear_or_none(dst->chroot_directory)) { free(dst->chroot_directory); dst->chroot_directory = NULL; } + + /* Subsystems require merging. */ + servconf_merge_subsystems(dst, src); } #undef M_CP_INTOPT #undef M_CP_STROPT #undef M_CP_STRARRAYOPT #define SERVCONF_MAX_DEPTH 16 static void parse_server_config_depth(ServerOptions *options, const char *filename, struct sshbuf *conf, struct include_list *includes, struct connection_info *connectinfo, int flags, int *activep, int depth) { int linenum, bad_options = 0; char *cp, *obuf, *cbuf; if (depth < 0 || depth > SERVCONF_MAX_DEPTH) fatal("Too many recursive configuration includes"); debug2_f("config %s len %zu%s", filename, sshbuf_len(conf), (flags & SSHCFG_NEVERMATCH ? " [checking syntax only]" : "")); if ((obuf = cbuf = sshbuf_dup_string(conf)) == NULL) fatal_f("sshbuf_dup_string failed"); linenum = 1; while ((cp = strsep(&cbuf, "\n")) != NULL) { if (process_server_config_line_depth(options, cp, filename, linenum++, activep, connectinfo, &flags, depth, includes) != 0) bad_options++; } free(obuf); if (bad_options > 0) fatal("%s: terminating, %d bad configuration options", filename, bad_options); } void parse_server_config(ServerOptions *options, const char *filename, struct sshbuf *conf, struct include_list *includes, struct connection_info *connectinfo, int reexec) { int active = connectinfo ? 0 : 1; parse_server_config_depth(options, filename, conf, includes, connectinfo, (connectinfo ? SSHCFG_MATCH_ONLY : 0), &active, 0); if (!reexec) process_queued_listen_addrs(options); } static const char * fmt_multistate_int(int val, const struct multistate *m) { u_int i; for (i = 0; m[i].key != NULL; i++) { if (m[i].value == val) return m[i].key; } return "UNKNOWN"; } static const char * fmt_intarg(ServerOpCodes code, int val) { if (val == -1) return "unset"; switch (code) { case sAddressFamily: return fmt_multistate_int(val, multistate_addressfamily); case sPermitRootLogin: return fmt_multistate_int(val, multistate_permitrootlogin); case sGatewayPorts: return fmt_multistate_int(val, multistate_gatewayports); case sCompression: return fmt_multistate_int(val, multistate_compression); case sAllowTcpForwarding: return fmt_multistate_int(val, multistate_tcpfwd); case sAllowStreamLocalForwarding: return fmt_multistate_int(val, multistate_tcpfwd); case sIgnoreRhosts: return fmt_multistate_int(val, multistate_ignore_rhosts); case sFingerprintHash: return ssh_digest_alg_name(val); default: switch (val) { case 0: return "no"; case 1: return "yes"; default: return "UNKNOWN"; } } } static void dump_cfg_int(ServerOpCodes code, int val) { if (code == sUnusedConnectionTimeout && val == 0) { printf("%s none\n", lookup_opcode_name(code)); return; } printf("%s %d\n", lookup_opcode_name(code), val); } static void dump_cfg_oct(ServerOpCodes code, int val) { printf("%s 0%o\n", lookup_opcode_name(code), val); } static void dump_cfg_fmtint(ServerOpCodes code, int val) { printf("%s %s\n", lookup_opcode_name(code), fmt_intarg(code, val)); } static void dump_cfg_string(ServerOpCodes code, const char *val) { printf("%s %s\n", lookup_opcode_name(code), val == NULL ? "none" : val); } static void dump_cfg_strarray(ServerOpCodes code, u_int count, char **vals) { u_int i; for (i = 0; i < count; i++) printf("%s %s\n", lookup_opcode_name(code), vals[i]); } static void dump_cfg_strarray_oneline(ServerOpCodes code, u_int count, char **vals) { u_int i; switch (code) { case sAuthenticationMethods: case sChannelTimeout: break; default: if (count <= 0) return; break; } printf("%s", lookup_opcode_name(code)); for (i = 0; i < count; i++) printf(" %s", vals[i]); if (code == sAuthenticationMethods && count == 0) printf(" any"); else if (code == sChannelTimeout && count == 0) printf(" none"); printf("\n"); } static char * format_listen_addrs(struct listenaddr *la) { int r; struct addrinfo *ai; char addr[NI_MAXHOST], port[NI_MAXSERV]; char *laddr1 = xstrdup(""), *laddr2 = NULL; /* * ListenAddress must be after Port. add_one_listen_addr pushes * addresses onto a stack, so to maintain ordering we need to * print these in reverse order. */ for (ai = la->addrs; ai; ai = ai->ai_next) { if ((r = getnameinfo(ai->ai_addr, ai->ai_addrlen, addr, sizeof(addr), port, sizeof(port), NI_NUMERICHOST|NI_NUMERICSERV)) != 0) { error("getnameinfo: %.100s", ssh_gai_strerror(r)); continue; } laddr2 = laddr1; if (ai->ai_family == AF_INET6) { xasprintf(&laddr1, "listenaddress [%s]:%s%s%s\n%s", addr, port, la->rdomain == NULL ? "" : " rdomain ", la->rdomain == NULL ? "" : la->rdomain, laddr2); } else { xasprintf(&laddr1, "listenaddress %s:%s%s%s\n%s", addr, port, la->rdomain == NULL ? "" : " rdomain ", la->rdomain == NULL ? "" : la->rdomain, laddr2); } free(laddr2); } return laddr1; } void dump_config(ServerOptions *o) { char *s; u_int i; /* these are usually at the top of the config */ for (i = 0; i < o->num_ports; i++) printf("port %d\n", o->ports[i]); dump_cfg_fmtint(sAddressFamily, o->address_family); for (i = 0; i < o->num_listen_addrs; i++) { s = format_listen_addrs(&o->listen_addrs[i]); printf("%s", s); free(s); } /* integer arguments */ #ifdef USE_PAM dump_cfg_fmtint(sUsePAM, o->use_pam); #endif dump_cfg_int(sLoginGraceTime, o->login_grace_time); dump_cfg_int(sX11DisplayOffset, o->x11_display_offset); dump_cfg_int(sMaxAuthTries, o->max_authtries); dump_cfg_int(sMaxSessions, o->max_sessions); dump_cfg_int(sClientAliveInterval, o->client_alive_interval); dump_cfg_int(sClientAliveCountMax, o->client_alive_count_max); dump_cfg_int(sRequiredRSASize, o->required_rsa_size); dump_cfg_oct(sStreamLocalBindMask, o->fwd_opts.streamlocal_bind_mask); dump_cfg_int(sUnusedConnectionTimeout, o->unused_connection_timeout); /* formatted integer arguments */ dump_cfg_fmtint(sPermitRootLogin, o->permit_root_login); dump_cfg_fmtint(sIgnoreRhosts, o->ignore_rhosts); dump_cfg_fmtint(sIgnoreUserKnownHosts, o->ignore_user_known_hosts); dump_cfg_fmtint(sHostbasedAuthentication, o->hostbased_authentication); dump_cfg_fmtint(sHostbasedUsesNameFromPacketOnly, o->hostbased_uses_name_from_packet_only); dump_cfg_fmtint(sPubkeyAuthentication, o->pubkey_authentication); #ifdef KRB5 dump_cfg_fmtint(sKerberosAuthentication, o->kerberos_authentication); dump_cfg_fmtint(sKerberosOrLocalPasswd, o->kerberos_or_local_passwd); dump_cfg_fmtint(sKerberosTicketCleanup, o->kerberos_ticket_cleanup); # ifdef USE_AFS dump_cfg_fmtint(sKerberosGetAFSToken, o->kerberos_get_afs_token); # endif #endif #ifdef GSSAPI dump_cfg_fmtint(sGssAuthentication, o->gss_authentication); dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds); #endif dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication); dump_cfg_fmtint(sKbdInteractiveAuthentication, o->kbd_interactive_authentication); dump_cfg_fmtint(sPrintMotd, o->print_motd); #ifndef DISABLE_LASTLOG dump_cfg_fmtint(sPrintLastLog, o->print_lastlog); #endif dump_cfg_fmtint(sX11Forwarding, o->x11_forwarding); dump_cfg_fmtint(sX11UseLocalhost, o->x11_use_localhost); dump_cfg_fmtint(sPermitTTY, o->permit_tty); dump_cfg_fmtint(sPermitUserRC, o->permit_user_rc); dump_cfg_fmtint(sStrictModes, o->strict_modes); dump_cfg_fmtint(sTCPKeepAlive, o->tcp_keep_alive); dump_cfg_fmtint(sEmptyPasswd, o->permit_empty_passwd); dump_cfg_fmtint(sCompression, o->compression); dump_cfg_fmtint(sGatewayPorts, o->fwd_opts.gateway_ports); dump_cfg_fmtint(sUseDNS, o->use_dns); dump_cfg_fmtint(sAllowTcpForwarding, o->allow_tcp_forwarding); dump_cfg_fmtint(sAllowAgentForwarding, o->allow_agent_forwarding); dump_cfg_fmtint(sDisableForwarding, o->disable_forwarding); dump_cfg_fmtint(sAllowStreamLocalForwarding, o->allow_streamlocal_forwarding); dump_cfg_fmtint(sStreamLocalBindUnlink, o->fwd_opts.streamlocal_bind_unlink); dump_cfg_fmtint(sFingerprintHash, o->fingerprint_hash); dump_cfg_fmtint(sExposeAuthInfo, o->expose_userauth_info); /* string arguments */ dump_cfg_string(sPidFile, o->pid_file); dump_cfg_string(sModuliFile, o->moduli_file); dump_cfg_string(sXAuthLocation, o->xauth_location); dump_cfg_string(sCiphers, o->ciphers); dump_cfg_string(sMacs, o->macs); dump_cfg_string(sBanner, o->banner); dump_cfg_string(sForceCommand, o->adm_forced_command); dump_cfg_string(sChrootDirectory, o->chroot_directory); dump_cfg_string(sTrustedUserCAKeys, o->trusted_user_ca_keys); dump_cfg_string(sRevokedKeys, o->revoked_keys_file); dump_cfg_string(sSecurityKeyProvider, o->sk_provider); dump_cfg_string(sAuthorizedPrincipalsFile, o->authorized_principals_file); dump_cfg_string(sVersionAddendum, *o->version_addendum == '\0' ? "none" : o->version_addendum); dump_cfg_string(sAuthorizedKeysCommand, o->authorized_keys_command); dump_cfg_string(sAuthorizedKeysCommandUser, o->authorized_keys_command_user); dump_cfg_string(sAuthorizedPrincipalsCommand, o->authorized_principals_command); dump_cfg_string(sAuthorizedPrincipalsCommandUser, o->authorized_principals_command_user); dump_cfg_string(sHostKeyAgent, o->host_key_agent); dump_cfg_string(sKexAlgorithms, o->kex_algorithms); dump_cfg_string(sCASignatureAlgorithms, o->ca_sign_algorithms); dump_cfg_string(sHostbasedAcceptedAlgorithms, o->hostbased_accepted_algos); dump_cfg_string(sHostKeyAlgorithms, o->hostkeyalgorithms); dump_cfg_string(sPubkeyAcceptedAlgorithms, o->pubkey_accepted_algos); #if defined(__OpenBSD__) || defined(HAVE_SYS_SET_PROCESS_RDOMAIN) dump_cfg_string(sRDomain, o->routing_domain); #endif /* string arguments requiring a lookup */ dump_cfg_string(sLogLevel, log_level_name(o->log_level)); dump_cfg_string(sLogFacility, log_facility_name(o->log_facility)); /* string array arguments */ dump_cfg_strarray_oneline(sAuthorizedKeysFile, o->num_authkeys_files, o->authorized_keys_files); dump_cfg_strarray(sHostKeyFile, o->num_host_key_files, o->host_key_files); dump_cfg_strarray(sHostCertificate, o->num_host_cert_files, o->host_cert_files); dump_cfg_strarray(sAllowUsers, o->num_allow_users, o->allow_users); dump_cfg_strarray(sDenyUsers, o->num_deny_users, o->deny_users); dump_cfg_strarray(sAllowGroups, o->num_allow_groups, o->allow_groups); dump_cfg_strarray(sDenyGroups, o->num_deny_groups, o->deny_groups); dump_cfg_strarray(sAcceptEnv, o->num_accept_env, o->accept_env); dump_cfg_strarray(sSetEnv, o->num_setenv, o->setenv); dump_cfg_strarray_oneline(sAuthenticationMethods, o->num_auth_methods, o->auth_methods); dump_cfg_strarray_oneline(sLogVerbose, o->num_log_verbose, o->log_verbose); dump_cfg_strarray_oneline(sChannelTimeout, o->num_channel_timeouts, o->channel_timeouts); /* other arguments */ for (i = 0; i < o->num_subsystems; i++) printf("subsystem %s %s\n", o->subsystem_name[i], o->subsystem_args[i]); printf("maxstartups %d:%d:%d\n", o->max_startups_begin, o->max_startups_rate, o->max_startups); printf("persourcemaxstartups "); if (o->per_source_max_startups == INT_MAX) printf("none\n"); else printf("%d\n", o->per_source_max_startups); printf("persourcenetblocksize %d:%d\n", o->per_source_masklen_ipv4, o->per_source_masklen_ipv6); s = NULL; for (i = 0; tunmode_desc[i].val != -1; i++) { if (tunmode_desc[i].val == o->permit_tun) { s = tunmode_desc[i].text; break; } } dump_cfg_string(sPermitTunnel, s); printf("ipqos %s ", iptos2str(o->ip_qos_interactive)); printf("%s\n", iptos2str(o->ip_qos_bulk)); printf("rekeylimit %llu %d\n", (unsigned long long)o->rekey_limit, o->rekey_interval); printf("permitopen"); if (o->num_permitted_opens == 0) printf(" any"); else { for (i = 0; i < o->num_permitted_opens; i++) printf(" %s", o->permitted_opens[i]); } printf("\n"); printf("permitlisten"); if (o->num_permitted_listens == 0) printf(" any"); else { for (i = 0; i < o->num_permitted_listens; i++) printf(" %s", o->permitted_listens[i]); } printf("\n"); if (o->permit_user_env_allowlist == NULL) { dump_cfg_fmtint(sPermitUserEnvironment, o->permit_user_env); } else { printf("permituserenvironment %s\n", o->permit_user_env_allowlist); } printf("pubkeyauthoptions"); if (o->pubkey_auth_options == 0) printf(" none"); if (o->pubkey_auth_options & PUBKEYAUTH_TOUCH_REQUIRED) printf(" touch-required"); if (o->pubkey_auth_options & PUBKEYAUTH_VERIFY_REQUIRED) printf(" verify-required"); printf("\n"); } diff --git a/servconf.h b/servconf.h index 7ad43de872d9..ed7b72e8e0e3 100644 --- a/servconf.h +++ b/servconf.h @@ -1,321 +1,323 @@ -/* $OpenBSD: servconf.h,v 1.159 2023/01/17 09:44:48 djm Exp $ */ +/* $OpenBSD: servconf.h,v 1.160 2023/09/06 23:35:35 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * Definitions for server configuration data and for the functions reading it. * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". */ #ifndef SERVCONF_H #define SERVCONF_H #include #define MAX_PORTS 256 /* Max # ports. */ -#define MAX_SUBSYSTEMS 256 /* Max # subsystems. */ - /* permit_root_login */ #define PERMIT_NOT_SET -1 #define PERMIT_NO 0 #define PERMIT_FORCED_ONLY 1 #define PERMIT_NO_PASSWD 2 #define PERMIT_YES 3 /* use_privsep */ #define PRIVSEP_OFF 0 #define PRIVSEP_ON 1 #define PRIVSEP_NOSANDBOX 2 /* PermitOpen */ #define PERMITOPEN_ANY 0 #define PERMITOPEN_NONE -2 /* IgnoreRhosts */ #define IGNORE_RHOSTS_NO 0 #define IGNORE_RHOSTS_YES 1 #define IGNORE_RHOSTS_SHOSTS 2 #define DEFAULT_AUTH_FAIL_MAX 6 /* Default for MaxAuthTries */ #define DEFAULT_SESSIONS_MAX 10 /* Default for MaxSessions */ /* Magic name for internal sftp-server */ #define INTERNAL_SFTP_NAME "internal-sftp" /* PubkeyAuthOptions flags */ #define PUBKEYAUTH_TOUCH_REQUIRED (1) #define PUBKEYAUTH_VERIFY_REQUIRED (1<<1) struct ssh; struct fwd_perm_list; /* * Used to store addresses from ListenAddr directives. These may be * incomplete, as they may specify addresses that need to be merged * with any ports requested by ListenPort. */ struct queued_listenaddr { char *addr; int port; /* <=0 if unspecified */ char *rdomain; }; /* Resolved listen addresses, grouped by optional routing domain */ struct listenaddr { char *rdomain; struct addrinfo *addrs; }; typedef struct { u_int num_ports; u_int ports_from_cmdline; int ports[MAX_PORTS]; /* Port number to listen on. */ struct queued_listenaddr *queued_listen_addrs; u_int num_queued_listens; struct listenaddr *listen_addrs; u_int num_listen_addrs; int address_family; /* Address family used by the server. */ char *routing_domain; /* Bind session to routing domain */ char **host_key_files; /* Files containing host keys. */ int *host_key_file_userprovided; /* Key was specified by user. */ u_int num_host_key_files; /* Number of files for host keys. */ char **host_cert_files; /* Files containing host certs. */ u_int num_host_cert_files; /* Number of files for host certs. */ char *host_key_agent; /* ssh-agent socket for host keys. */ char *pid_file; /* Where to put our pid */ char *moduli_file; /* moduli file for DH-GEX */ int login_grace_time; /* Disconnect if no auth in this time * (sec). */ int permit_root_login; /* PERMIT_*, see above */ int ignore_rhosts; /* Ignore .rhosts and .shosts. */ int ignore_user_known_hosts; /* Ignore ~/.ssh/known_hosts * for RhostsRsaAuth */ int print_motd; /* If true, print /etc/motd. */ int print_lastlog; /* If true, print lastlog */ int x11_forwarding; /* If true, permit inet (spoofing) X11 fwd. */ int x11_display_offset; /* What DISPLAY number to start * searching at */ int x11_use_localhost; /* If true, use localhost for fake X11 server. */ char *xauth_location; /* Location of xauth program */ int permit_tty; /* If false, deny pty allocation */ int permit_user_rc; /* If false, deny ~/.ssh/rc execution */ int strict_modes; /* If true, require string home dir modes. */ int tcp_keep_alive; /* If true, set SO_KEEPALIVE. */ int ip_qos_interactive; /* IP ToS/DSCP/class for interactive */ int ip_qos_bulk; /* IP ToS/DSCP/class for bulk traffic */ char *ciphers; /* Supported SSH2 ciphers. */ char *macs; /* Supported SSH2 macs. */ char *kex_algorithms; /* SSH2 kex methods in order of preference. */ struct ForwardOptions fwd_opts; /* forwarding options */ SyslogFacility log_facility; /* Facility for system logging. */ LogLevel log_level; /* Level for system logging. */ u_int num_log_verbose; /* Verbose log overrides */ char **log_verbose; int hostbased_authentication; /* If true, permit ssh2 hostbased auth */ int hostbased_uses_name_from_packet_only; /* experimental */ char *hostbased_accepted_algos; /* Algos allowed for hostbased */ char *hostkeyalgorithms; /* SSH2 server key types */ char *ca_sign_algorithms; /* Allowed CA signature algorithms */ int pubkey_authentication; /* If true, permit ssh2 pubkey authentication. */ char *pubkey_accepted_algos; /* Signature algos allowed for pubkey */ int pubkey_auth_options; /* -1 or mask of PUBKEYAUTH_* flags */ int kerberos_authentication; /* If true, permit Kerberos * authentication. */ int kerberos_or_local_passwd; /* If true, permit kerberos * and any other password * authentication mechanism, * such as SecurID or * /etc/passwd */ int kerberos_ticket_cleanup; /* If true, destroy ticket * file on logout. */ int kerberos_get_afs_token; /* If true, try to get AFS token if * authenticated with Kerberos. */ int gss_authentication; /* If true, permit GSSAPI authentication */ int gss_cleanup_creds; /* If true, destroy cred cache on logout */ int gss_strict_acceptor; /* If true, restrict the GSSAPI acceptor name */ int password_authentication; /* If true, permit password * authentication. */ int kbd_interactive_authentication; /* If true, permit */ int permit_empty_passwd; /* If false, do not permit empty * passwords. */ int permit_user_env; /* If true, read ~/.ssh/environment */ char *permit_user_env_allowlist; /* pattern-list of allowed env names */ int compression; /* If true, compression is allowed */ int allow_tcp_forwarding; /* One of FORWARD_* */ int allow_streamlocal_forwarding; /* One of FORWARD_* */ int allow_agent_forwarding; int disable_forwarding; u_int num_allow_users; char **allow_users; u_int num_deny_users; char **deny_users; u_int num_allow_groups; char **allow_groups; u_int num_deny_groups; char **deny_groups; u_int num_subsystems; - char *subsystem_name[MAX_SUBSYSTEMS]; - char *subsystem_command[MAX_SUBSYSTEMS]; - char *subsystem_args[MAX_SUBSYSTEMS]; + char **subsystem_name; + char **subsystem_command; + char **subsystem_args; u_int num_accept_env; char **accept_env; u_int num_setenv; char **setenv; int max_startups_begin; int max_startups_rate; int max_startups; int per_source_max_startups; int per_source_masklen_ipv4; int per_source_masklen_ipv6; int max_authtries; int max_sessions; char *banner; /* SSH-2 banner message */ int use_dns; int client_alive_interval; /* * poke the client this often to * see if it's still there */ int client_alive_count_max; /* * If the client is unresponsive * for this many intervals above, * disconnect the session */ u_int num_authkeys_files; /* Files containing public keys */ char **authorized_keys_files; char *adm_forced_command; int use_pam; /* Enable auth via PAM */ int permit_tun; char **permitted_opens; /* May also be one of PERMITOPEN_* */ u_int num_permitted_opens; char **permitted_listens; /* May also be one of PERMITOPEN_* */ u_int num_permitted_listens; char *chroot_directory; char *revoked_keys_file; char *trusted_user_ca_keys; char *authorized_keys_command; char *authorized_keys_command_user; char *authorized_principals_file; char *authorized_principals_command; char *authorized_principals_command_user; int64_t rekey_limit; int rekey_interval; char *version_addendum; /* Appended to SSH banner */ u_int num_auth_methods; char **auth_methods; int fingerprint_hash; int expose_userauth_info; u_int64_t timing_secret; char *sk_provider; int required_rsa_size; /* minimum size of RSA keys */ char **channel_timeouts; /* inactivity timeout by channel type */ u_int num_channel_timeouts; int unused_connection_timeout; } ServerOptions; /* Information about the incoming connection as used by Match */ struct connection_info { const char *user; const char *host; /* possibly resolved hostname */ const char *address; /* remote address */ const char *laddress; /* local address */ int lport; /* local port */ const char *rdomain; /* routing domain if available */ int test; /* test mode, allow some attributes to be * unspecified */ }; /* List of included files for re-exec from the parsed configuration */ struct include_item { char *selector; char *filename; struct sshbuf *contents; TAILQ_ENTRY(include_item) entry; }; TAILQ_HEAD(include_list, include_item); /* * These are string config options that must be copied between the * Match sub-config and the main config, and must be sent from the * privsep child to the privsep master. We use a macro to ensure all * the options are copied and the copies are done in the correct order. * * NB. an option must appear in servconf.c:copy_set_server_options() or * COPY_MATCH_STRING_OPTS here but never both. */ #define COPY_MATCH_STRING_OPTS() do { \ M_CP_STROPT(banner); \ M_CP_STROPT(trusted_user_ca_keys); \ M_CP_STROPT(revoked_keys_file); \ M_CP_STROPT(authorized_keys_command); \ M_CP_STROPT(authorized_keys_command_user); \ M_CP_STROPT(authorized_principals_file); \ M_CP_STROPT(authorized_principals_command); \ M_CP_STROPT(authorized_principals_command_user); \ M_CP_STROPT(hostbased_accepted_algos); \ M_CP_STROPT(pubkey_accepted_algos); \ M_CP_STROPT(ca_sign_algorithms); \ M_CP_STROPT(routing_domain); \ M_CP_STROPT(permit_user_env_allowlist); \ M_CP_STRARRAYOPT(authorized_keys_files, num_authkeys_files); \ M_CP_STRARRAYOPT(allow_users, num_allow_users); \ M_CP_STRARRAYOPT(deny_users, num_deny_users); \ M_CP_STRARRAYOPT(allow_groups, num_allow_groups); \ M_CP_STRARRAYOPT(deny_groups, num_deny_groups); \ M_CP_STRARRAYOPT(accept_env, num_accept_env); \ M_CP_STRARRAYOPT(setenv, num_setenv); \ M_CP_STRARRAYOPT(auth_methods, num_auth_methods); \ M_CP_STRARRAYOPT(permitted_opens, num_permitted_opens); \ M_CP_STRARRAYOPT(permitted_listens, num_permitted_listens); \ M_CP_STRARRAYOPT(channel_timeouts, num_channel_timeouts); \ M_CP_STRARRAYOPT(log_verbose, num_log_verbose); \ + M_CP_STRARRAYOPT(subsystem_name, num_subsystems); \ + M_CP_STRARRAYOPT(subsystem_command, num_subsystems); \ + M_CP_STRARRAYOPT(subsystem_args, num_subsystems); \ } while (0) struct connection_info *get_connection_info(struct ssh *, int, int); void initialize_server_options(ServerOptions *); void fill_default_server_options(ServerOptions *); int process_server_config_line(ServerOptions *, char *, const char *, int, int *, struct connection_info *, struct include_list *includes); void process_permitopen(struct ssh *ssh, ServerOptions *options); void process_channel_timeouts(struct ssh *ssh, ServerOptions *); void load_server_config(const char *, struct sshbuf *); void parse_server_config(ServerOptions *, const char *, struct sshbuf *, struct include_list *includes, struct connection_info *, int); void parse_server_match_config(ServerOptions *, struct include_list *includes, struct connection_info *); int parse_server_match_testspec(struct connection_info *, char *); int server_match_spec_complete(struct connection_info *); +void servconf_merge_subsystems(ServerOptions *, ServerOptions *); void copy_set_server_options(ServerOptions *, ServerOptions *, int); void dump_config(ServerOptions *); char *derelativise_path(const char *); void servconf_add_hostkey(const char *, const int, ServerOptions *, const char *path, int); void servconf_add_hostcert(const char *, const int, ServerOptions *, const char *path); #endif /* SERVCONF_H */ diff --git a/serverloop.c b/serverloop.c index de5fa2e3c2e8..f3683c2e4a6d 100644 --- a/serverloop.c +++ b/serverloop.c @@ -1,932 +1,932 @@ -/* $OpenBSD: serverloop.c,v 1.236 2023/03/08 04:43:12 guenther Exp $ */ +/* $OpenBSD: serverloop.c,v 1.237 2023/08/21 04:59:54 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * Server main loop for handling the interactive session. * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". * * SSH2 support by Markus Friedl. * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" #include #include #include #ifdef HAVE_SYS_TIME_H # include #endif #include #include #include #include #include #ifdef HAVE_POLL_H #include #endif #include #include #include #include #include #include "openbsd-compat/sys-queue.h" #include "xmalloc.h" #include "packet.h" #include "sshbuf.h" #include "log.h" #include "misc.h" #include "servconf.h" #include "canohost.h" #include "sshpty.h" #include "channels.h" #include "ssh2.h" #include "sshkey.h" #include "cipher.h" #include "kex.h" #include "hostfile.h" #include "auth.h" #include "session.h" #include "dispatch.h" #include "auth-options.h" #include "serverloop.h" #include "ssherr.h" extern ServerOptions options; /* XXX */ extern Authctxt *the_authctxt; extern struct sshauthopt *auth_opts; extern int use_privsep; static int no_more_sessions = 0; /* Disallow further sessions. */ static volatile sig_atomic_t child_terminated = 0; /* The child has terminated. */ /* Cleanup on signals (!use_privsep case only) */ static volatile sig_atomic_t received_sigterm = 0; /* prototypes */ static void server_init_dispatch(struct ssh *); /* requested tunnel forwarding interface(s), shared with session.c */ char *tun_fwd_ifnames = NULL; /* returns 1 if bind to specified port by specified user is permitted */ static int bind_permitted(int port, uid_t uid) { if (use_privsep) return 1; /* allow system to decide */ if (port < IPPORT_RESERVED && uid != 0) return 0; return 1; } static void sigchld_handler(int sig) { child_terminated = 1; } static void sigterm_handler(int sig) { received_sigterm = sig; } static void client_alive_check(struct ssh *ssh) { char remote_id[512]; int r, channel_id; /* timeout, check to see how many we have had */ if (options.client_alive_count_max > 0 && ssh_packet_inc_alive_timeouts(ssh) > options.client_alive_count_max) { sshpkt_fmt_connection_id(ssh, remote_id, sizeof(remote_id)); logit("Timeout, client not responding from %s", remote_id); cleanup_exit(255); } /* * send a bogus global/channel request with "wantreply", * we should get back a failure */ if ((channel_id = channel_find_open(ssh)) == -1) { if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || (r = sshpkt_put_cstring(ssh, "keepalive@openssh.com")) != 0 || (r = sshpkt_put_u8(ssh, 1)) != 0) /* boolean: want reply */ fatal_fr(r, "compose"); } else { channel_request_start(ssh, channel_id, "keepalive@openssh.com", 1); } if ((r = sshpkt_send(ssh)) != 0) fatal_fr(r, "send"); } /* * Sleep in ppoll() until we can do something. * Optionally, a maximum time can be specified for the duration of * the wait (0 = infinite). */ static void wait_until_can_do_something(struct ssh *ssh, int connection_in, int connection_out, struct pollfd **pfdp, u_int *npfd_allocp, u_int *npfd_activep, sigset_t *sigsetp, int *conn_in_readyp, int *conn_out_readyp) { struct timespec timeout; char remote_id[512]; int ret; int client_alive_scheduled = 0; u_int p; time_t now; static time_t last_client_time, unused_connection_expiry; *conn_in_readyp = *conn_out_readyp = 0; /* Prepare channel poll. First two pollfd entries are reserved */ ptimeout_init(&timeout); channel_prepare_poll(ssh, pfdp, npfd_allocp, npfd_activep, 2, &timeout); now = monotime(); if (*npfd_activep < 2) fatal_f("bad npfd %u", *npfd_activep); /* shouldn't happen */ if (options.rekey_interval > 0 && !ssh_packet_is_rekeying(ssh)) { ptimeout_deadline_sec(&timeout, ssh_packet_get_rekey_timeout(ssh)); } /* * If no channels are open and UnusedConnectionTimeout is set, then * start the clock to terminate the connection. */ if (options.unused_connection_timeout != 0) { if (channel_still_open(ssh) || unused_connection_expiry == 0) { unused_connection_expiry = now + options.unused_connection_timeout; } ptimeout_deadline_monotime(&timeout, unused_connection_expiry); } /* * if using client_alive, set the max timeout accordingly, * and indicate that this particular timeout was for client * alive by setting the client_alive_scheduled flag. * * this could be randomized somewhat to make traffic * analysis more difficult, but we're not doing it yet. */ if (options.client_alive_interval) { /* Time we last heard from the client OR sent a keepalive */ if (last_client_time == 0) last_client_time = now; ptimeout_deadline_sec(&timeout, options.client_alive_interval); /* XXX ? deadline_monotime(last_client_time + alive_interval) */ client_alive_scheduled = 1; } #if 0 /* wrong: bad condition XXX */ if (channel_not_very_much_buffered_data()) #endif /* Monitor client connection on reserved pollfd entries */ (*pfdp)[0].fd = connection_in; (*pfdp)[0].events = POLLIN; (*pfdp)[1].fd = connection_out; (*pfdp)[1].events = ssh_packet_have_data_to_write(ssh) ? POLLOUT : 0; /* * If child has terminated and there is enough buffer space to read * from it, then read as much as is available and exit. */ if (child_terminated && ssh_packet_not_very_much_data_to_write(ssh)) ptimeout_deadline_ms(&timeout, 100); /* Wait for something to happen, or the timeout to expire. */ ret = ppoll(*pfdp, *npfd_activep, ptimeout_get_tsp(&timeout), sigsetp); if (ret == -1) { for (p = 0; p < *npfd_activep; p++) (*pfdp)[p].revents = 0; if (errno != EINTR) fatal_f("ppoll: %.100s", strerror(errno)); return; } *conn_in_readyp = (*pfdp)[0].revents != 0; *conn_out_readyp = (*pfdp)[1].revents != 0; now = monotime(); /* need to reset after ppoll() */ /* ClientAliveInterval probing */ if (client_alive_scheduled) { if (ret == 0 && - now > last_client_time + options.client_alive_interval) { + now >= last_client_time + options.client_alive_interval) { /* ppoll timed out and we're due to probe */ client_alive_check(ssh); last_client_time = now; } else if (ret != 0 && *conn_in_readyp) { /* Data from peer; reset probe timer. */ last_client_time = now; } } /* UnusedConnectionTimeout handling */ if (unused_connection_expiry != 0 && now > unused_connection_expiry && !channel_still_open(ssh)) { sshpkt_fmt_connection_id(ssh, remote_id, sizeof(remote_id)); logit("terminating inactive connection from %s", remote_id); cleanup_exit(255); } } /* * Processes input from the client and the program. Input data is stored * in buffers and processed later. */ static int process_input(struct ssh *ssh, int connection_in) { int r; if ((r = ssh_packet_process_read(ssh, connection_in)) == 0) return 0; /* success */ if (r == SSH_ERR_SYSTEM_ERROR) { if (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK) return 0; if (errno == EPIPE) { verbose("Connection closed by %.100s port %d", ssh_remote_ipaddr(ssh), ssh_remote_port(ssh)); return -1; } verbose("Read error from remote host %s port %d: %s", ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), strerror(errno)); cleanup_exit(255); } return -1; } /* * Sends data from internal buffers to client program stdin. */ static void process_output(struct ssh *ssh, int connection_out) { int r; /* Send any buffered packet data to the client. */ if ((r = ssh_packet_write_poll(ssh)) != 0) { sshpkt_fatal(ssh, r, "%s: ssh_packet_write_poll", __func__); } } static void process_buffered_input_packets(struct ssh *ssh) { ssh_dispatch_run_fatal(ssh, DISPATCH_NONBLOCK, NULL); } static void collect_children(struct ssh *ssh) { pid_t pid; int status; if (child_terminated) { debug("Received SIGCHLD."); while ((pid = waitpid(-1, &status, WNOHANG)) > 0 || (pid == -1 && errno == EINTR)) if (pid > 0) session_close_by_pid(ssh, pid, status); child_terminated = 0; } } void server_loop2(struct ssh *ssh, Authctxt *authctxt) { struct pollfd *pfd = NULL; u_int npfd_alloc = 0, npfd_active = 0; int r, conn_in_ready, conn_out_ready; u_int connection_in, connection_out; sigset_t bsigset, osigset; debug("Entering interactive session for SSH2."); if (sigemptyset(&bsigset) == -1 || sigaddset(&bsigset, SIGCHLD) == -1) error_f("bsigset setup: %s", strerror(errno)); ssh_signal(SIGCHLD, sigchld_handler); child_terminated = 0; connection_in = ssh_packet_get_connection_in(ssh); connection_out = ssh_packet_get_connection_out(ssh); if (!use_privsep) { ssh_signal(SIGTERM, sigterm_handler); ssh_signal(SIGINT, sigterm_handler); ssh_signal(SIGQUIT, sigterm_handler); } server_init_dispatch(ssh); for (;;) { process_buffered_input_packets(ssh); if (!ssh_packet_is_rekeying(ssh) && ssh_packet_not_very_much_data_to_write(ssh)) channel_output_poll(ssh); /* * Block SIGCHLD while we check for dead children, then pass * the old signal mask through to ppoll() so that it'll wake * up immediately if a child exits after we've called waitpid(). */ if (sigprocmask(SIG_BLOCK, &bsigset, &osigset) == -1) error_f("bsigset sigprocmask: %s", strerror(errno)); collect_children(ssh); wait_until_can_do_something(ssh, connection_in, connection_out, &pfd, &npfd_alloc, &npfd_active, &osigset, &conn_in_ready, &conn_out_ready); if (sigprocmask(SIG_UNBLOCK, &bsigset, &osigset) == -1) error_f("osigset sigprocmask: %s", strerror(errno)); if (received_sigterm) { logit("Exiting on signal %d", (int)received_sigterm); /* Clean up sessions, utmp, etc. */ cleanup_exit(255); } channel_after_poll(ssh, pfd, npfd_active); if (conn_in_ready && process_input(ssh, connection_in) < 0) break; /* A timeout may have triggered rekeying */ if ((r = ssh_packet_check_rekey(ssh)) != 0) fatal_fr(r, "cannot start rekeying"); if (conn_out_ready) process_output(ssh, connection_out); } collect_children(ssh); free(pfd); /* free all channels, no more reads and writes */ channel_free_all(ssh); /* free remaining sessions, e.g. remove wtmp entries */ session_destroy_all(ssh, NULL); } static int server_input_keep_alive(int type, u_int32_t seq, struct ssh *ssh) { debug("Got %d/%u for keepalive", type, seq); /* * reset timeout, since we got a sane answer from the client. * even if this was generated by something other than * the bogus CHANNEL_REQUEST we send for keepalives. */ ssh_packet_set_alive_timeouts(ssh, 0); return 0; } static Channel * server_request_direct_tcpip(struct ssh *ssh, int *reason, const char **errmsg) { Channel *c = NULL; char *target = NULL, *originator = NULL; u_int target_port = 0, originator_port = 0; int r; if ((r = sshpkt_get_cstring(ssh, &target, NULL)) != 0 || (r = sshpkt_get_u32(ssh, &target_port)) != 0 || (r = sshpkt_get_cstring(ssh, &originator, NULL)) != 0 || (r = sshpkt_get_u32(ssh, &originator_port)) != 0 || (r = sshpkt_get_end(ssh)) != 0) sshpkt_fatal(ssh, r, "%s: parse packet", __func__); if (target_port > 0xFFFF) { error_f("invalid target port"); *reason = SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED; goto out; } if (originator_port > 0xFFFF) { error_f("invalid originator port"); *reason = SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED; goto out; } debug_f("originator %s port %u, target %s port %u", originator, originator_port, target, target_port); /* XXX fine grained permissions */ if ((options.allow_tcp_forwarding & FORWARD_LOCAL) != 0 && auth_opts->permit_port_forwarding_flag && !options.disable_forwarding) { c = channel_connect_to_port(ssh, target, target_port, "direct-tcpip", "direct-tcpip", reason, errmsg); } else { logit("refused local port forward: " "originator %s port %d, target %s port %d", originator, originator_port, target, target_port); if (reason != NULL) *reason = SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED; } out: free(originator); free(target); return c; } static Channel * server_request_direct_streamlocal(struct ssh *ssh) { Channel *c = NULL; char *target = NULL, *originator = NULL; u_int originator_port = 0; struct passwd *pw = the_authctxt->pw; int r; if (pw == NULL || !the_authctxt->valid) fatal_f("no/invalid user"); if ((r = sshpkt_get_cstring(ssh, &target, NULL)) != 0 || (r = sshpkt_get_cstring(ssh, &originator, NULL)) != 0 || (r = sshpkt_get_u32(ssh, &originator_port)) != 0 || (r = sshpkt_get_end(ssh)) != 0) sshpkt_fatal(ssh, r, "%s: parse packet", __func__); if (originator_port > 0xFFFF) { error_f("invalid originator port"); goto out; } debug_f("originator %s port %d, target %s", originator, originator_port, target); /* XXX fine grained permissions */ if ((options.allow_streamlocal_forwarding & FORWARD_LOCAL) != 0 && auth_opts->permit_port_forwarding_flag && !options.disable_forwarding && (pw->pw_uid == 0 || use_privsep)) { c = channel_connect_to_path(ssh, target, "direct-streamlocal@openssh.com", "direct-streamlocal"); } else { logit("refused streamlocal port forward: " "originator %s port %d, target %s", originator, originator_port, target); } out: free(originator); free(target); return c; } static Channel * server_request_tun(struct ssh *ssh) { Channel *c = NULL; u_int mode, tun; int r, sock; char *tmp, *ifname = NULL; if ((r = sshpkt_get_u32(ssh, &mode)) != 0) sshpkt_fatal(ssh, r, "%s: parse mode", __func__); switch (mode) { case SSH_TUNMODE_POINTOPOINT: case SSH_TUNMODE_ETHERNET: break; default: ssh_packet_send_debug(ssh, "Unsupported tunnel device mode."); return NULL; } if ((options.permit_tun & mode) == 0) { ssh_packet_send_debug(ssh, "Server has rejected tunnel device " "forwarding"); return NULL; } if ((r = sshpkt_get_u32(ssh, &tun)) != 0) sshpkt_fatal(ssh, r, "%s: parse device", __func__); if (tun > INT_MAX) { debug_f("invalid tun"); goto done; } if (auth_opts->force_tun_device != -1) { if (tun != SSH_TUNID_ANY && auth_opts->force_tun_device != (int)tun) goto done; tun = auth_opts->force_tun_device; } sock = tun_open(tun, mode, &ifname); if (sock < 0) goto done; debug("Tunnel forwarding using interface %s", ifname); c = channel_new(ssh, "tun", SSH_CHANNEL_OPEN, sock, sock, -1, CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, "tun", 1); c->datagram = 1; #if defined(SSH_TUN_FILTER) if (mode == SSH_TUNMODE_POINTOPOINT) channel_register_filter(ssh, c->self, sys_tun_infilter, sys_tun_outfilter, NULL, NULL); #endif /* * Update the list of names exposed to the session * XXX remove these if the tunnels are closed (won't matter * much if they are already in the environment though) */ tmp = tun_fwd_ifnames; xasprintf(&tun_fwd_ifnames, "%s%s%s", tun_fwd_ifnames == NULL ? "" : tun_fwd_ifnames, tun_fwd_ifnames == NULL ? "" : ",", ifname); free(tmp); free(ifname); done: if (c == NULL) ssh_packet_send_debug(ssh, "Failed to open the tunnel device."); return c; } static Channel * server_request_session(struct ssh *ssh) { Channel *c; int r; debug("input_session_request"); if ((r = sshpkt_get_end(ssh)) != 0) sshpkt_fatal(ssh, r, "%s: parse packet", __func__); if (no_more_sessions) { ssh_packet_disconnect(ssh, "Possible attack: attempt to open a " "session after additional sessions disabled"); } /* * A server session has no fd to read or write until a * CHANNEL_REQUEST for a shell is made, so we set the type to * SSH_CHANNEL_LARVAL. Additionally, a callback for handling all * CHANNEL_REQUEST messages is registered. */ c = channel_new(ssh, "session", SSH_CHANNEL_LARVAL, -1, -1, -1, /*window size*/0, CHAN_SES_PACKET_DEFAULT, 0, "server-session", 1); if (session_open(the_authctxt, c->self) != 1) { debug("session open failed, free channel %d", c->self); channel_free(ssh, c); return NULL; } channel_register_cleanup(ssh, c->self, session_close_by_channel, 0); return c; } static int server_input_channel_open(int type, u_int32_t seq, struct ssh *ssh) { Channel *c = NULL; char *ctype = NULL; const char *errmsg = NULL; int r, reason = SSH2_OPEN_CONNECT_FAILED; u_int rchan = 0, rmaxpack = 0, rwindow = 0; if ((r = sshpkt_get_cstring(ssh, &ctype, NULL)) != 0 || (r = sshpkt_get_u32(ssh, &rchan)) != 0 || (r = sshpkt_get_u32(ssh, &rwindow)) != 0 || (r = sshpkt_get_u32(ssh, &rmaxpack)) != 0) sshpkt_fatal(ssh, r, "%s: parse packet", __func__); debug_f("ctype %s rchan %u win %u max %u", ctype, rchan, rwindow, rmaxpack); if (strcmp(ctype, "session") == 0) { c = server_request_session(ssh); } else if (strcmp(ctype, "direct-tcpip") == 0) { c = server_request_direct_tcpip(ssh, &reason, &errmsg); } else if (strcmp(ctype, "direct-streamlocal@openssh.com") == 0) { c = server_request_direct_streamlocal(ssh); } else if (strcmp(ctype, "tun@openssh.com") == 0) { c = server_request_tun(ssh); } if (c != NULL) { debug_f("confirm %s", ctype); c->remote_id = rchan; c->have_remote_id = 1; c->remote_window = rwindow; c->remote_maxpacket = rmaxpack; if (c->type != SSH_CHANNEL_CONNECTING) { if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_OPEN_CONFIRMATION)) != 0 || (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || (r = sshpkt_put_u32(ssh, c->self)) != 0 || (r = sshpkt_put_u32(ssh, c->local_window)) != 0 || (r = sshpkt_put_u32(ssh, c->local_maxpacket)) != 0 || (r = sshpkt_send(ssh)) != 0) { sshpkt_fatal(ssh, r, "%s: send open confirm", __func__); } } } else { debug_f("failure %s", ctype); if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_OPEN_FAILURE)) != 0 || (r = sshpkt_put_u32(ssh, rchan)) != 0 || (r = sshpkt_put_u32(ssh, reason)) != 0 || (r = sshpkt_put_cstring(ssh, errmsg ? errmsg : "open failed")) != 0 || (r = sshpkt_put_cstring(ssh, "")) != 0 || (r = sshpkt_send(ssh)) != 0) { sshpkt_fatal(ssh, r, "%s: send open failure", __func__); } } free(ctype); return 0; } static int server_input_hostkeys_prove(struct ssh *ssh, struct sshbuf **respp) { struct sshbuf *resp = NULL; struct sshbuf *sigbuf = NULL; struct sshkey *key = NULL, *key_pub = NULL, *key_prv = NULL; int r, ndx, success = 0; const u_char *blob; const char *sigalg, *kex_rsa_sigalg = NULL; u_char *sig = 0; size_t blen, slen; if ((resp = sshbuf_new()) == NULL || (sigbuf = sshbuf_new()) == NULL) fatal_f("sshbuf_new"); if (sshkey_type_plain(sshkey_type_from_name( ssh->kex->hostkey_alg)) == KEY_RSA) kex_rsa_sigalg = ssh->kex->hostkey_alg; while (ssh_packet_remaining(ssh) > 0) { sshkey_free(key); key = NULL; if ((r = sshpkt_get_string_direct(ssh, &blob, &blen)) != 0 || (r = sshkey_from_blob(blob, blen, &key)) != 0) { error_fr(r, "parse key"); goto out; } /* * Better check that this is actually one of our hostkeys * before attempting to sign anything with it. */ if ((ndx = ssh->kex->host_key_index(key, 1, ssh)) == -1) { error_f("unknown host %s key", sshkey_type(key)); goto out; } /* * XXX refactor: make kex->sign just use an index rather * than passing in public and private keys */ if ((key_prv = get_hostkey_by_index(ndx)) == NULL && (key_pub = get_hostkey_public_by_index(ndx, ssh)) == NULL) { error_f("can't retrieve hostkey %d", ndx); goto out; } sshbuf_reset(sigbuf); free(sig); sig = NULL; /* * For RSA keys, prefer to use the signature type negotiated * during KEX to the default (SHA1). */ sigalg = NULL; if (sshkey_type_plain(key->type) == KEY_RSA) { if (kex_rsa_sigalg != NULL) sigalg = kex_rsa_sigalg; else if (ssh->kex->flags & KEX_RSA_SHA2_512_SUPPORTED) sigalg = "rsa-sha2-512"; else if (ssh->kex->flags & KEX_RSA_SHA2_256_SUPPORTED) sigalg = "rsa-sha2-256"; } debug3_f("sign %s key (index %d) using sigalg %s", sshkey_type(key), ndx, sigalg == NULL ? "default" : sigalg); if ((r = sshbuf_put_cstring(sigbuf, "hostkeys-prove-00@openssh.com")) != 0 || (r = sshbuf_put_stringb(sigbuf, ssh->kex->session_id)) != 0 || (r = sshkey_puts(key, sigbuf)) != 0 || (r = ssh->kex->sign(ssh, key_prv, key_pub, &sig, &slen, sshbuf_ptr(sigbuf), sshbuf_len(sigbuf), sigalg)) != 0 || (r = sshbuf_put_string(resp, sig, slen)) != 0) { error_fr(r, "assemble signature"); goto out; } } /* Success */ *respp = resp; resp = NULL; /* don't free it */ success = 1; out: free(sig); sshbuf_free(resp); sshbuf_free(sigbuf); sshkey_free(key); return success; } static int server_input_global_request(int type, u_int32_t seq, struct ssh *ssh) { char *rtype = NULL; u_char want_reply = 0; int r, success = 0, allocated_listen_port = 0; u_int port = 0; struct sshbuf *resp = NULL; struct passwd *pw = the_authctxt->pw; struct Forward fwd; memset(&fwd, 0, sizeof(fwd)); if (pw == NULL || !the_authctxt->valid) fatal_f("no/invalid user"); if ((r = sshpkt_get_cstring(ssh, &rtype, NULL)) != 0 || (r = sshpkt_get_u8(ssh, &want_reply)) != 0) sshpkt_fatal(ssh, r, "%s: parse packet", __func__); debug_f("rtype %s want_reply %d", rtype, want_reply); /* -R style forwarding */ if (strcmp(rtype, "tcpip-forward") == 0) { if ((r = sshpkt_get_cstring(ssh, &fwd.listen_host, NULL)) != 0 || (r = sshpkt_get_u32(ssh, &port)) != 0) sshpkt_fatal(ssh, r, "%s: parse tcpip-forward", __func__); debug_f("tcpip-forward listen %s port %u", fwd.listen_host, port); if (port <= INT_MAX) fwd.listen_port = (int)port; /* check permissions */ if (port > INT_MAX || (options.allow_tcp_forwarding & FORWARD_REMOTE) == 0 || !auth_opts->permit_port_forwarding_flag || options.disable_forwarding || (!want_reply && fwd.listen_port == 0) || (fwd.listen_port != 0 && !bind_permitted(fwd.listen_port, pw->pw_uid))) { success = 0; ssh_packet_send_debug(ssh, "Server has disabled port forwarding."); } else { /* Start listening on the port */ success = channel_setup_remote_fwd_listener(ssh, &fwd, &allocated_listen_port, &options.fwd_opts); } if ((resp = sshbuf_new()) == NULL) fatal_f("sshbuf_new"); if (allocated_listen_port != 0 && (r = sshbuf_put_u32(resp, allocated_listen_port)) != 0) fatal_fr(r, "sshbuf_put_u32"); } else if (strcmp(rtype, "cancel-tcpip-forward") == 0) { if ((r = sshpkt_get_cstring(ssh, &fwd.listen_host, NULL)) != 0 || (r = sshpkt_get_u32(ssh, &port)) != 0) sshpkt_fatal(ssh, r, "%s: parse cancel-tcpip-forward", __func__); debug_f("cancel-tcpip-forward addr %s port %d", fwd.listen_host, port); if (port <= INT_MAX) { fwd.listen_port = (int)port; success = channel_cancel_rport_listener(ssh, &fwd); } } else if (strcmp(rtype, "streamlocal-forward@openssh.com") == 0) { if ((r = sshpkt_get_cstring(ssh, &fwd.listen_path, NULL)) != 0) sshpkt_fatal(ssh, r, "%s: parse streamlocal-forward@openssh.com", __func__); debug_f("streamlocal-forward listen path %s", fwd.listen_path); /* check permissions */ if ((options.allow_streamlocal_forwarding & FORWARD_REMOTE) == 0 || !auth_opts->permit_port_forwarding_flag || options.disable_forwarding || (pw->pw_uid != 0 && !use_privsep)) { success = 0; ssh_packet_send_debug(ssh, "Server has disabled " "streamlocal forwarding."); } else { /* Start listening on the socket */ success = channel_setup_remote_fwd_listener(ssh, &fwd, NULL, &options.fwd_opts); } } else if (strcmp(rtype, "cancel-streamlocal-forward@openssh.com") == 0) { if ((r = sshpkt_get_cstring(ssh, &fwd.listen_path, NULL)) != 0) sshpkt_fatal(ssh, r, "%s: parse cancel-streamlocal-forward@openssh.com", __func__); debug_f("cancel-streamlocal-forward path %s", fwd.listen_path); success = channel_cancel_rport_listener(ssh, &fwd); } else if (strcmp(rtype, "no-more-sessions@openssh.com") == 0) { no_more_sessions = 1; success = 1; } else if (strcmp(rtype, "hostkeys-prove-00@openssh.com") == 0) { success = server_input_hostkeys_prove(ssh, &resp); } /* XXX sshpkt_get_end() */ if (want_reply) { if ((r = sshpkt_start(ssh, success ? SSH2_MSG_REQUEST_SUCCESS : SSH2_MSG_REQUEST_FAILURE)) != 0 || (success && resp != NULL && (r = sshpkt_putb(ssh, resp)) != 0) || (r = sshpkt_send(ssh)) != 0 || (r = ssh_packet_write_wait(ssh)) != 0) sshpkt_fatal(ssh, r, "%s: send reply", __func__); } free(fwd.listen_host); free(fwd.listen_path); free(rtype); sshbuf_free(resp); return 0; } static int server_input_channel_req(int type, u_int32_t seq, struct ssh *ssh) { Channel *c; int r, success = 0; char *rtype = NULL; u_char want_reply = 0; u_int id = 0; if ((r = sshpkt_get_u32(ssh, &id)) != 0 || (r = sshpkt_get_cstring(ssh, &rtype, NULL)) != 0 || (r = sshpkt_get_u8(ssh, &want_reply)) != 0) sshpkt_fatal(ssh, r, "%s: parse packet", __func__); debug("server_input_channel_req: channel %u request %s reply %d", id, rtype, want_reply); if (id >= INT_MAX || (c = channel_lookup(ssh, (int)id)) == NULL) { ssh_packet_disconnect(ssh, "%s: unknown channel %d", __func__, id); } if (!strcmp(rtype, "eow@openssh.com")) { if ((r = sshpkt_get_end(ssh)) != 0) sshpkt_fatal(ssh, r, "%s: parse packet", __func__); chan_rcvd_eow(ssh, c); } else if ((c->type == SSH_CHANNEL_LARVAL || c->type == SSH_CHANNEL_OPEN) && strcmp(c->ctype, "session") == 0) success = session_input_channel_req(ssh, c, rtype); if (want_reply && !(c->flags & CHAN_CLOSE_SENT)) { if (!c->have_remote_id) fatal_f("channel %d: no remote_id", c->self); if ((r = sshpkt_start(ssh, success ? SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE)) != 0 || (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || (r = sshpkt_send(ssh)) != 0) sshpkt_fatal(ssh, r, "%s: send reply", __func__); } free(rtype); return 0; } static void server_init_dispatch(struct ssh *ssh) { debug("server_init_dispatch"); ssh_dispatch_init(ssh, &dispatch_protocol_error); ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_CLOSE, &channel_input_oclose); ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_DATA, &channel_input_data); ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_EOF, &channel_input_ieof); ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_EXTENDED_DATA, &channel_input_extended_data); ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_OPEN, &server_input_channel_open); ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation); ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure); ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_REQUEST, &server_input_channel_req); ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_WINDOW_ADJUST, &channel_input_window_adjust); ssh_dispatch_set(ssh, SSH2_MSG_GLOBAL_REQUEST, &server_input_global_request); /* client_alive */ ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_SUCCESS, &server_input_keep_alive); ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_FAILURE, &server_input_keep_alive); ssh_dispatch_set(ssh, SSH2_MSG_REQUEST_SUCCESS, &server_input_keep_alive); ssh_dispatch_set(ssh, SSH2_MSG_REQUEST_FAILURE, &server_input_keep_alive); /* rekeying */ ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_input_kexinit); } diff --git a/session.c b/session.c index 89dcfdab628c..aa342e84de4c 100644 --- a/session.c +++ b/session.c @@ -1,2725 +1,2732 @@ -/* $OpenBSD: session.c,v 1.335 2023/03/07 06:09:14 dtucker Exp $ */ +/* $OpenBSD: session.c,v 1.336 2023/08/10 23:05:48 djm Exp $ */ /* * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". * * SSH2 support by Markus Friedl. * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" #include #ifdef HAVE_SYS_STAT_H # include #endif #include #include #include #include #include #include #include #include #include #ifdef HAVE_PATHS_H #include #endif #include #include #include #include #include #include #include #include #include "openbsd-compat/sys-queue.h" #include "xmalloc.h" #include "ssh.h" #include "ssh2.h" #include "sshpty.h" #include "packet.h" #include "sshbuf.h" #include "ssherr.h" #include "match.h" #include "uidswap.h" #include "channels.h" #include "sshkey.h" #include "cipher.h" #ifdef GSSAPI #include "ssh-gss.h" #endif #include "hostfile.h" #include "auth.h" #include "auth-options.h" #include "authfd.h" #include "pathnames.h" #include "log.h" #include "misc.h" #include "servconf.h" #include "sshlogin.h" #include "serverloop.h" #include "canohost.h" #include "session.h" #include "kex.h" #include "monitor_wrap.h" #include "sftp.h" #include "atomicio.h" #if defined(KRB5) && defined(USE_AFS) #include #endif #ifdef WITH_SELINUX #include #endif #define IS_INTERNAL_SFTP(c) \ (!strncmp(c, INTERNAL_SFTP_NAME, sizeof(INTERNAL_SFTP_NAME) - 1) && \ (c[sizeof(INTERNAL_SFTP_NAME) - 1] == '\0' || \ c[sizeof(INTERNAL_SFTP_NAME) - 1] == ' ' || \ c[sizeof(INTERNAL_SFTP_NAME) - 1] == '\t')) /* func */ Session *session_new(void); void session_set_fds(struct ssh *, Session *, int, int, int, int, int); void session_pty_cleanup(Session *); void session_proctitle(Session *); int session_setup_x11fwd(struct ssh *, Session *); int do_exec_pty(struct ssh *, Session *, const char *); int do_exec_no_pty(struct ssh *, Session *, const char *); int do_exec(struct ssh *, Session *, const char *); void do_login(struct ssh *, Session *, const char *); void do_child(struct ssh *, Session *, const char *); void do_motd(void); int check_quietlogin(Session *, const char *); static void do_authenticated2(struct ssh *, Authctxt *); static int session_pty_req(struct ssh *, Session *); /* import */ extern ServerOptions options; extern char *__progname; extern int debug_flag; extern u_int utmp_len; extern int startup_pipe; extern void destroy_sensitive_data(void); extern struct sshbuf *loginmsg; extern struct sshauthopt *auth_opts; extern char *tun_fwd_ifnames; /* serverloop.c */ /* original command from peer. */ const char *original_command = NULL; /* data */ static int sessions_first_unused = -1; static int sessions_nalloc = 0; static Session *sessions = NULL; #define SUBSYSTEM_NONE 0 #define SUBSYSTEM_EXT 1 #define SUBSYSTEM_INT_SFTP 2 #define SUBSYSTEM_INT_SFTP_ERROR 3 #ifdef HAVE_LOGIN_CAP login_cap_t *lc; #endif static int is_child = 0; static int in_chroot = 0; /* File containing userauth info, if ExposeAuthInfo set */ static char *auth_info_file = NULL; /* Name and directory of socket for authentication agent forwarding. */ static char *auth_sock_name = NULL; static char *auth_sock_dir = NULL; /* removes the agent forwarding socket */ static void auth_sock_cleanup_proc(struct passwd *pw) { if (auth_sock_name != NULL) { temporarily_use_uid(pw); unlink(auth_sock_name); rmdir(auth_sock_dir); auth_sock_name = NULL; restore_uid(); } } static int auth_input_request_forwarding(struct ssh *ssh, struct passwd * pw) { Channel *nc; int sock = -1; if (auth_sock_name != NULL) { error("authentication forwarding requested twice."); return 0; } /* Temporarily drop privileged uid for mkdir/bind. */ temporarily_use_uid(pw); /* Allocate a buffer for the socket name, and format the name. */ auth_sock_dir = xstrdup("/tmp/ssh-XXXXXXXXXX"); /* Create private directory for socket */ if (mkdtemp(auth_sock_dir) == NULL) { ssh_packet_send_debug(ssh, "Agent forwarding disabled: " "mkdtemp() failed: %.100s", strerror(errno)); restore_uid(); free(auth_sock_dir); auth_sock_dir = NULL; goto authsock_err; } xasprintf(&auth_sock_name, "%s/agent.%ld", auth_sock_dir, (long) getpid()); /* Start a Unix listener on auth_sock_name. */ sock = unix_listener(auth_sock_name, SSH_LISTEN_BACKLOG, 0); /* Restore the privileged uid. */ restore_uid(); /* Check for socket/bind/listen failure. */ if (sock < 0) goto authsock_err; /* Allocate a channel for the authentication agent socket. */ nc = channel_new(ssh, "auth-listener", SSH_CHANNEL_AUTH_SOCKET, sock, sock, -1, CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 0, "auth socket", 1); nc->path = xstrdup(auth_sock_name); return 1; authsock_err: free(auth_sock_name); if (auth_sock_dir != NULL) { temporarily_use_uid(pw); rmdir(auth_sock_dir); restore_uid(); free(auth_sock_dir); } if (sock != -1) close(sock); auth_sock_name = NULL; auth_sock_dir = NULL; return 0; } static void display_loginmsg(void) { int r; if (sshbuf_len(loginmsg) == 0) return; if ((r = sshbuf_put_u8(loginmsg, 0)) != 0) fatal_fr(r, "sshbuf_put_u8"); printf("%s", (char *)sshbuf_ptr(loginmsg)); sshbuf_reset(loginmsg); } static void prepare_auth_info_file(struct passwd *pw, struct sshbuf *info) { int fd = -1, success = 0; if (!options.expose_userauth_info || info == NULL) return; temporarily_use_uid(pw); auth_info_file = xstrdup("/tmp/sshauth.XXXXXXXXXXXXXXX"); if ((fd = mkstemp(auth_info_file)) == -1) { error_f("mkstemp: %s", strerror(errno)); goto out; } if (atomicio(vwrite, fd, sshbuf_mutable_ptr(info), sshbuf_len(info)) != sshbuf_len(info)) { error_f("write: %s", strerror(errno)); goto out; } if (close(fd) != 0) { error_f("close: %s", strerror(errno)); goto out; } success = 1; out: if (!success) { if (fd != -1) close(fd); free(auth_info_file); auth_info_file = NULL; } restore_uid(); } static void set_fwdpermit_from_authopts(struct ssh *ssh, const struct sshauthopt *opts) { char *tmp, *cp, *host; int port; size_t i; if ((options.allow_tcp_forwarding & FORWARD_LOCAL) != 0) { channel_clear_permission(ssh, FORWARD_USER, FORWARD_LOCAL); for (i = 0; i < auth_opts->npermitopen; i++) { tmp = cp = xstrdup(auth_opts->permitopen[i]); /* This shouldn't fail as it has already been checked */ if ((host = hpdelim2(&cp, NULL)) == NULL) fatal_f("internal error: hpdelim"); host = cleanhostname(host); if (cp == NULL || (port = permitopen_port(cp)) < 0) fatal_f("internal error: permitopen port"); channel_add_permission(ssh, FORWARD_USER, FORWARD_LOCAL, host, port); free(tmp); } } if ((options.allow_tcp_forwarding & FORWARD_REMOTE) != 0) { channel_clear_permission(ssh, FORWARD_USER, FORWARD_REMOTE); for (i = 0; i < auth_opts->npermitlisten; i++) { tmp = cp = xstrdup(auth_opts->permitlisten[i]); /* This shouldn't fail as it has already been checked */ if ((host = hpdelim(&cp)) == NULL) fatal_f("internal error: hpdelim"); host = cleanhostname(host); if (cp == NULL || (port = permitopen_port(cp)) < 0) fatal_f("internal error: permitlisten port"); channel_add_permission(ssh, FORWARD_USER, FORWARD_REMOTE, host, port); free(tmp); } } } void do_authenticated(struct ssh *ssh, Authctxt *authctxt) { setproctitle("%s", authctxt->pw->pw_name); auth_log_authopts("active", auth_opts, 0); /* setup the channel layer */ /* XXX - streamlocal? */ set_fwdpermit_from_authopts(ssh, auth_opts); if (!auth_opts->permit_port_forwarding_flag || options.disable_forwarding) { channel_disable_admin(ssh, FORWARD_LOCAL); channel_disable_admin(ssh, FORWARD_REMOTE); } else { if ((options.allow_tcp_forwarding & FORWARD_LOCAL) == 0) channel_disable_admin(ssh, FORWARD_LOCAL); else channel_permit_all(ssh, FORWARD_LOCAL); if ((options.allow_tcp_forwarding & FORWARD_REMOTE) == 0) channel_disable_admin(ssh, FORWARD_REMOTE); else channel_permit_all(ssh, FORWARD_REMOTE); } auth_debug_send(ssh); prepare_auth_info_file(authctxt->pw, authctxt->session_info); do_authenticated2(ssh, authctxt); do_cleanup(ssh, authctxt); } /* Check untrusted xauth strings for metacharacters */ static int xauth_valid_string(const char *s) { size_t i; for (i = 0; s[i] != '\0'; i++) { if (!isalnum((u_char)s[i]) && s[i] != '.' && s[i] != ':' && s[i] != '/' && s[i] != '-' && s[i] != '_') return 0; } return 1; } #define USE_PIPES 1 /* * This is called to fork and execute a command when we have no tty. This * will call do_child from the child, and server_loop from the parent after * setting up file descriptors and such. */ int do_exec_no_pty(struct ssh *ssh, Session *s, const char *command) { pid_t pid; #ifdef USE_PIPES int pin[2], pout[2], perr[2]; if (s == NULL) fatal("do_exec_no_pty: no session"); /* Allocate pipes for communicating with the program. */ if (pipe(pin) == -1) { error_f("pipe in: %.100s", strerror(errno)); return -1; } if (pipe(pout) == -1) { error_f("pipe out: %.100s", strerror(errno)); close(pin[0]); close(pin[1]); return -1; } if (pipe(perr) == -1) { error_f("pipe err: %.100s", strerror(errno)); close(pin[0]); close(pin[1]); close(pout[0]); close(pout[1]); return -1; } #else int inout[2], err[2]; if (s == NULL) fatal("do_exec_no_pty: no session"); /* Uses socket pairs to communicate with the program. */ if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1) { error_f("socketpair #1: %.100s", strerror(errno)); return -1; } if (socketpair(AF_UNIX, SOCK_STREAM, 0, err) == -1) { error_f("socketpair #2: %.100s", strerror(errno)); close(inout[0]); close(inout[1]); return -1; } #endif session_proctitle(s); /* Fork the child. */ switch ((pid = fork())) { case -1: error_f("fork: %.100s", strerror(errno)); #ifdef USE_PIPES close(pin[0]); close(pin[1]); close(pout[0]); close(pout[1]); close(perr[0]); close(perr[1]); #else close(inout[0]); close(inout[1]); close(err[0]); close(err[1]); #endif return -1; case 0: is_child = 1; /* * Create a new session and process group since the 4.4BSD * setlogin() affects the entire process group. */ if (setsid() == -1) error("setsid failed: %.100s", strerror(errno)); #ifdef USE_PIPES /* * Redirect stdin. We close the parent side of the socket * pair, and make the child side the standard input. */ close(pin[1]); if (dup2(pin[0], 0) == -1) perror("dup2 stdin"); close(pin[0]); /* Redirect stdout. */ close(pout[0]); if (dup2(pout[1], 1) == -1) perror("dup2 stdout"); close(pout[1]); /* Redirect stderr. */ close(perr[0]); if (dup2(perr[1], 2) == -1) perror("dup2 stderr"); close(perr[1]); #else /* * Redirect stdin, stdout, and stderr. Stdin and stdout will * use the same socket, as some programs (particularly rdist) * seem to depend on it. */ close(inout[1]); close(err[1]); if (dup2(inout[0], 0) == -1) /* stdin */ perror("dup2 stdin"); if (dup2(inout[0], 1) == -1) /* stdout (same as stdin) */ perror("dup2 stdout"); close(inout[0]); if (dup2(err[0], 2) == -1) /* stderr */ perror("dup2 stderr"); close(err[0]); #endif /* Do processing for the child (exec command etc). */ do_child(ssh, s, command); /* NOTREACHED */ default: break; } #ifdef HAVE_CYGWIN cygwin_set_impersonation_token(INVALID_HANDLE_VALUE); #endif s->pid = pid; /* Set interactive/non-interactive mode. */ ssh_packet_set_interactive(ssh, s->display != NULL, options.ip_qos_interactive, options.ip_qos_bulk); /* * Clear loginmsg, since it's the child's responsibility to display * it to the user, otherwise multiple sessions may accumulate * multiple copies of the login messages. */ sshbuf_reset(loginmsg); #ifdef USE_PIPES /* We are the parent. Close the child sides of the pipes. */ close(pin[0]); close(pout[1]); close(perr[1]); session_set_fds(ssh, s, pin[1], pout[0], perr[0], s->is_subsystem, 0); #else /* We are the parent. Close the child sides of the socket pairs. */ close(inout[0]); close(err[0]); /* * Enter the interactive session. Note: server_loop must be able to * handle the case that fdin and fdout are the same. */ session_set_fds(ssh, s, inout[1], inout[1], err[1], s->is_subsystem, 0); #endif return 0; } /* * This is called to fork and execute a command when we have a tty. This * will call do_child from the child, and server_loop from the parent after * setting up file descriptors, controlling tty, updating wtmp, utmp, * lastlog, and other such operations. */ int do_exec_pty(struct ssh *ssh, Session *s, const char *command) { int fdout, ptyfd, ttyfd, ptymaster; pid_t pid; if (s == NULL) fatal("do_exec_pty: no session"); ptyfd = s->ptyfd; ttyfd = s->ttyfd; /* * Create another descriptor of the pty master side for use as the * standard input. We could use the original descriptor, but this * simplifies code in server_loop. The descriptor is bidirectional. * Do this before forking (and cleanup in the child) so as to * detect and gracefully fail out-of-fd conditions. */ if ((fdout = dup(ptyfd)) == -1) { error_f("dup #1: %s", strerror(errno)); close(ttyfd); close(ptyfd); return -1; } /* we keep a reference to the pty master */ if ((ptymaster = dup(ptyfd)) == -1) { error_f("dup #2: %s", strerror(errno)); close(ttyfd); close(ptyfd); close(fdout); return -1; } /* Fork the child. */ switch ((pid = fork())) { case -1: error_f("fork: %.100s", strerror(errno)); close(fdout); close(ptymaster); close(ttyfd); close(ptyfd); return -1; case 0: is_child = 1; close(fdout); close(ptymaster); /* Close the master side of the pseudo tty. */ close(ptyfd); /* Make the pseudo tty our controlling tty. */ pty_make_controlling_tty(&ttyfd, s->tty); /* Redirect stdin/stdout/stderr from the pseudo tty. */ if (dup2(ttyfd, 0) == -1) error("dup2 stdin: %s", strerror(errno)); if (dup2(ttyfd, 1) == -1) error("dup2 stdout: %s", strerror(errno)); if (dup2(ttyfd, 2) == -1) error("dup2 stderr: %s", strerror(errno)); /* Close the extra descriptor for the pseudo tty. */ close(ttyfd); /* record login, etc. similar to login(1) */ #ifndef HAVE_OSF_SIA do_login(ssh, s, command); #endif /* * Do common processing for the child, such as execing * the command. */ do_child(ssh, s, command); /* NOTREACHED */ default: break; } #ifdef HAVE_CYGWIN cygwin_set_impersonation_token(INVALID_HANDLE_VALUE); #endif s->pid = pid; /* Parent. Close the slave side of the pseudo tty. */ close(ttyfd); /* Enter interactive session. */ s->ptymaster = ptymaster; ssh_packet_set_interactive(ssh, 1, options.ip_qos_interactive, options.ip_qos_bulk); session_set_fds(ssh, s, ptyfd, fdout, -1, 1, 1); return 0; } /* * This is called to fork and execute a command. If another command is * to be forced, execute that instead. */ int do_exec(struct ssh *ssh, Session *s, const char *command) { int ret; const char *forced = NULL, *tty = NULL; char session_type[1024]; if (options.adm_forced_command) { original_command = command; command = options.adm_forced_command; forced = "(config)"; } else if (auth_opts->force_command != NULL) { original_command = command; command = auth_opts->force_command; forced = "(key-option)"; } s->forced = 0; if (forced != NULL) { s->forced = 1; if (IS_INTERNAL_SFTP(command)) { s->is_subsystem = s->is_subsystem ? SUBSYSTEM_INT_SFTP : SUBSYSTEM_INT_SFTP_ERROR; } else if (s->is_subsystem) s->is_subsystem = SUBSYSTEM_EXT; snprintf(session_type, sizeof(session_type), "forced-command %s '%.900s'", forced, command); } else if (s->is_subsystem) { snprintf(session_type, sizeof(session_type), "subsystem '%.900s'", s->subsys); } else if (command == NULL) { snprintf(session_type, sizeof(session_type), "shell"); } else { /* NB. we don't log unforced commands to preserve privacy */ snprintf(session_type, sizeof(session_type), "command"); } if (s->ttyfd != -1) { tty = s->tty; if (strncmp(tty, "/dev/", 5) == 0) tty += 5; } verbose("Starting session: %s%s%s for %s from %.200s port %d id %d", session_type, tty == NULL ? "" : " on ", tty == NULL ? "" : tty, s->pw->pw_name, ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), s->self); #ifdef SSH_AUDIT_EVENTS if (command != NULL) PRIVSEP(audit_run_command(command)); else if (s->ttyfd == -1) { char *shell = s->pw->pw_shell; if (shell[0] == '\0') /* empty shell means /bin/sh */ shell =_PATH_BSHELL; PRIVSEP(audit_run_command(shell)); } #endif if (s->ttyfd != -1) ret = do_exec_pty(ssh, s, command); else ret = do_exec_no_pty(ssh, s, command); original_command = NULL; /* * Clear loginmsg: it's the child's responsibility to display * it to the user, otherwise multiple sessions may accumulate * multiple copies of the login messages. */ sshbuf_reset(loginmsg); return ret; } /* administrative, login(1)-like work */ void do_login(struct ssh *ssh, Session *s, const char *command) { socklen_t fromlen; struct sockaddr_storage from; struct passwd * pw = s->pw; pid_t pid = getpid(); /* * Get IP address of client. If the connection is not a socket, let * the address be 0.0.0.0. */ memset(&from, 0, sizeof(from)); fromlen = sizeof(from); if (ssh_packet_connection_is_on_socket(ssh)) { if (getpeername(ssh_packet_get_connection_in(ssh), (struct sockaddr *)&from, &fromlen) == -1) { debug("getpeername: %.100s", strerror(errno)); cleanup_exit(255); } } /* Record that there was a login on that tty from the remote host. */ if (!use_privsep) record_login(pid, s->tty, pw->pw_name, pw->pw_uid, session_get_remote_name_or_ip(ssh, utmp_len, options.use_dns), (struct sockaddr *)&from, fromlen); #ifdef USE_PAM /* * If password change is needed, do it now. * This needs to occur before the ~/.hushlogin check. */ if (options.use_pam && !use_privsep && s->authctxt->force_pwchange) { display_loginmsg(); do_pam_chauthtok(); s->authctxt->force_pwchange = 0; /* XXX - signal [net] parent to enable forwardings */ } #endif if (check_quietlogin(s, command)) return; display_loginmsg(); do_motd(); } /* * Display the message of the day. */ void do_motd(void) { FILE *f; char buf[256]; if (options.print_motd) { #ifdef HAVE_LOGIN_CAP f = fopen(login_getcapstr(lc, "welcome", "/etc/motd", "/etc/motd"), "r"); #else f = fopen("/etc/motd", "r"); #endif if (f) { while (fgets(buf, sizeof(buf), f)) fputs(buf, stdout); fclose(f); } } } /* * Check for quiet login, either .hushlogin or command given. */ int check_quietlogin(Session *s, const char *command) { char buf[256]; struct passwd *pw = s->pw; struct stat st; /* Return 1 if .hushlogin exists or a command given. */ if (command != NULL) return 1; snprintf(buf, sizeof(buf), "%.200s/.hushlogin", pw->pw_dir); #ifdef HAVE_LOGIN_CAP if (login_getcapbool(lc, "hushlogin", 0) || stat(buf, &st) >= 0) return 1; #else if (stat(buf, &st) >= 0) return 1; #endif return 0; } /* * Reads environment variables from the given file and adds/overrides them * into the environment. If the file does not exist, this does nothing. * Otherwise, it must consist of empty lines, comments (line starts with '#') * and assignments of the form name=value. No other forms are allowed. * If allowlist is not NULL, then it is interpreted as a pattern list and * only variable names that match it will be accepted. */ static void read_environment_file(char ***env, u_int *envsize, const char *filename, const char *allowlist) { FILE *f; char *line = NULL, *cp, *value; size_t linesize = 0; u_int lineno = 0; f = fopen(filename, "r"); if (!f) return; while (getline(&line, &linesize, f) != -1) { if (++lineno > 1000) fatal("Too many lines in environment file %s", filename); for (cp = line; *cp == ' ' || *cp == '\t'; cp++) ; if (!*cp || *cp == '#' || *cp == '\n') continue; cp[strcspn(cp, "\n")] = '\0'; value = strchr(cp, '='); if (value == NULL) { fprintf(stderr, "Bad line %u in %.100s\n", lineno, filename); continue; } /* * Replace the equals sign by nul, and advance value to * the value string. */ *value = '\0'; value++; if (allowlist != NULL && match_pattern_list(cp, allowlist, 0) != 1) continue; child_set_env(env, envsize, cp, value); } free(line); fclose(f); } #ifdef HAVE_ETC_DEFAULT_LOGIN /* * Return named variable from specified environment, or NULL if not present. */ static char * child_get_env(char **env, const char *name) { int i; size_t len; len = strlen(name); for (i=0; env[i] != NULL; i++) if (strncmp(name, env[i], len) == 0 && env[i][len] == '=') return(env[i] + len + 1); return NULL; } /* * Read /etc/default/login. * We pick up the PATH (or SUPATH for root) and UMASK. */ static void read_etc_default_login(char ***env, u_int *envsize, uid_t uid) { char **tmpenv = NULL, *var; u_int i, tmpenvsize = 0; u_long mask; /* * We don't want to copy the whole file to the child's environment, * so we use a temporary environment and copy the variables we're * interested in. */ read_environment_file(&tmpenv, &tmpenvsize, "/etc/default/login", options.permit_user_env_allowlist); if (tmpenv == NULL) return; if (uid == 0) var = child_get_env(tmpenv, "SUPATH"); else var = child_get_env(tmpenv, "PATH"); if (var != NULL) child_set_env(env, envsize, "PATH", var); if ((var = child_get_env(tmpenv, "UMASK")) != NULL) if (sscanf(var, "%5lo", &mask) == 1) umask((mode_t)mask); for (i = 0; tmpenv[i] != NULL; i++) free(tmpenv[i]); free(tmpenv); } #endif /* HAVE_ETC_DEFAULT_LOGIN */ #if defined(USE_PAM) || defined(HAVE_CYGWIN) static void copy_environment_denylist(char **source, char ***env, u_int *envsize, const char *denylist) { char *var_name, *var_val; int i; if (source == NULL) return; for(i = 0; source[i] != NULL; i++) { var_name = xstrdup(source[i]); if ((var_val = strstr(var_name, "=")) == NULL) { free(var_name); continue; } *var_val++ = '\0'; if (denylist == NULL || match_pattern_list(var_name, denylist, 0) != 1) { debug3("Copy environment: %s=%s", var_name, var_val); child_set_env(env, envsize, var_name, var_val); } free(var_name); } } #endif /* defined(USE_PAM) || defined(HAVE_CYGWIN) */ #ifdef HAVE_CYGWIN static void copy_environment(char **source, char ***env, u_int *envsize) { copy_environment_denylist(source, env, envsize, NULL); } #endif static char ** do_setup_env(struct ssh *ssh, Session *s, const char *shell) { char buf[256]; size_t n; u_int i, envsize; char *ocp, *cp, *value, **env, *laddr; struct passwd *pw = s->pw; #if !defined (HAVE_LOGIN_CAP) && !defined (HAVE_CYGWIN) char *path = NULL; #endif /* Initialize the environment. */ envsize = 100; env = xcalloc(envsize, sizeof(char *)); env[0] = NULL; #ifdef HAVE_CYGWIN /* * The Windows environment contains some setting which are * important for a running system. They must not be dropped. */ { char **p; p = fetch_windows_environment(); copy_environment(p, &env, &envsize); free_windows_environment(p); } #endif #ifdef GSSAPI /* Allow any GSSAPI methods that we've used to alter * the child's environment as they see fit */ ssh_gssapi_do_child(&env, &envsize); #endif /* Set basic environment. */ for (i = 0; i < s->num_env; i++) child_set_env(&env, &envsize, s->env[i].name, s->env[i].val); child_set_env(&env, &envsize, "USER", pw->pw_name); child_set_env(&env, &envsize, "LOGNAME", pw->pw_name); #ifdef _AIX child_set_env(&env, &envsize, "LOGIN", pw->pw_name); #endif child_set_env(&env, &envsize, "HOME", pw->pw_dir); #ifdef HAVE_LOGIN_CAP if (setusercontext(lc, pw, pw->pw_uid, LOGIN_SETPATH) < 0) child_set_env(&env, &envsize, "PATH", _PATH_STDPATH); else child_set_env(&env, &envsize, "PATH", getenv("PATH")); #else /* HAVE_LOGIN_CAP */ # ifndef HAVE_CYGWIN /* * There's no standard path on Windows. The path contains * important components pointing to the system directories, * needed for loading shared libraries. So the path better * remains intact here. */ # ifdef HAVE_ETC_DEFAULT_LOGIN read_etc_default_login(&env, &envsize, pw->pw_uid); path = child_get_env(env, "PATH"); # endif /* HAVE_ETC_DEFAULT_LOGIN */ if (path == NULL || *path == '\0') { child_set_env(&env, &envsize, "PATH", s->pw->pw_uid == 0 ? SUPERUSER_PATH : _PATH_STDPATH); } # endif /* HAVE_CYGWIN */ #endif /* HAVE_LOGIN_CAP */ if (!options.use_pam) { snprintf(buf, sizeof buf, "%.200s/%.50s", _PATH_MAILDIR, pw->pw_name); child_set_env(&env, &envsize, "MAIL", buf); } /* Normal systems set SHELL by default. */ child_set_env(&env, &envsize, "SHELL", shell); if (getenv("TZ")) child_set_env(&env, &envsize, "TZ", getenv("TZ")); if (s->term) child_set_env(&env, &envsize, "TERM", s->term); if (s->display) child_set_env(&env, &envsize, "DISPLAY", s->display); /* * Since we clear KRB5CCNAME at startup, if it's set now then it * must have been set by a native authentication method (eg AIX or * SIA), so copy it to the child. */ { char *cp; if ((cp = getenv("KRB5CCNAME")) != NULL) child_set_env(&env, &envsize, "KRB5CCNAME", cp); } #ifdef _AIX { char *cp; if ((cp = getenv("AUTHSTATE")) != NULL) child_set_env(&env, &envsize, "AUTHSTATE", cp); read_environment_file(&env, &envsize, "/etc/environment", options.permit_user_env_allowlist); } #endif #ifdef KRB5 if (s->authctxt->krb5_ccname) child_set_env(&env, &envsize, "KRB5CCNAME", s->authctxt->krb5_ccname); #endif if (auth_sock_name != NULL) child_set_env(&env, &envsize, SSH_AUTHSOCKET_ENV_NAME, auth_sock_name); /* Set custom environment options from pubkey authentication. */ if (options.permit_user_env) { for (n = 0 ; n < auth_opts->nenv; n++) { ocp = xstrdup(auth_opts->env[n]); cp = strchr(ocp, '='); if (cp != NULL) { *cp = '\0'; /* Apply PermitUserEnvironment allowlist */ if (options.permit_user_env_allowlist == NULL || match_pattern_list(ocp, options.permit_user_env_allowlist, 0) == 1) child_set_env(&env, &envsize, ocp, cp + 1); } free(ocp); } } /* read $HOME/.ssh/environment. */ if (options.permit_user_env) { snprintf(buf, sizeof buf, "%.200s/%s/environment", pw->pw_dir, _PATH_SSH_USER_DIR); read_environment_file(&env, &envsize, buf, options.permit_user_env_allowlist); } #ifdef USE_PAM /* * Pull in any environment variables that may have * been set by PAM. */ if (options.use_pam) { char **p; /* * Don't allow PAM-internal env vars to leak * back into the session environment. */ #define PAM_ENV_DENYLIST "SSH_AUTH_INFO*,SSH_CONNECTION*" p = fetch_pam_child_environment(); copy_environment_denylist(p, &env, &envsize, PAM_ENV_DENYLIST); free_pam_environment(p); p = fetch_pam_environment(); copy_environment_denylist(p, &env, &envsize, PAM_ENV_DENYLIST); free_pam_environment(p); } #endif /* USE_PAM */ /* Environment specified by admin */ for (i = 0; i < options.num_setenv; i++) { cp = xstrdup(options.setenv[i]); if ((value = strchr(cp, '=')) == NULL) { /* shouldn't happen; vars are checked in servconf.c */ fatal("Invalid config SetEnv: %s", options.setenv[i]); } *value++ = '\0'; child_set_env(&env, &envsize, cp, value); free(cp); } /* SSH_CLIENT deprecated */ snprintf(buf, sizeof buf, "%.50s %d %d", ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), ssh_local_port(ssh)); child_set_env(&env, &envsize, "SSH_CLIENT", buf); laddr = get_local_ipaddr(ssh_packet_get_connection_in(ssh)); snprintf(buf, sizeof buf, "%.50s %d %.50s %d", ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), laddr, ssh_local_port(ssh)); free(laddr); child_set_env(&env, &envsize, "SSH_CONNECTION", buf); if (tun_fwd_ifnames != NULL) child_set_env(&env, &envsize, "SSH_TUNNEL", tun_fwd_ifnames); if (auth_info_file != NULL) child_set_env(&env, &envsize, "SSH_USER_AUTH", auth_info_file); if (s->ttyfd != -1) child_set_env(&env, &envsize, "SSH_TTY", s->tty); if (original_command) child_set_env(&env, &envsize, "SSH_ORIGINAL_COMMAND", original_command); if (debug_flag) { /* dump the environment */ fprintf(stderr, "Environment:\n"); for (i = 0; env[i]; i++) fprintf(stderr, " %.200s\n", env[i]); } return env; } /* * Run $HOME/.ssh/rc, /etc/ssh/sshrc, or xauth (whichever is found * first in this order). */ static void do_rc_files(struct ssh *ssh, Session *s, const char *shell) { FILE *f = NULL; char *cmd = NULL, *user_rc = NULL; int do_xauth; struct stat st; do_xauth = s->display != NULL && s->auth_proto != NULL && s->auth_data != NULL; xasprintf(&user_rc, "%s/%s", s->pw->pw_dir, _PATH_SSH_USER_RC); /* ignore _PATH_SSH_USER_RC for subsystems and admin forced commands */ if (!s->is_subsystem && options.adm_forced_command == NULL && auth_opts->permit_user_rc && options.permit_user_rc && stat(user_rc, &st) >= 0) { if (xasprintf(&cmd, "%s -c '%s %s'", shell, _PATH_BSHELL, user_rc) == -1) fatal_f("xasprintf: %s", strerror(errno)); if (debug_flag) fprintf(stderr, "Running %s\n", cmd); f = popen(cmd, "w"); if (f) { if (do_xauth) fprintf(f, "%s %s\n", s->auth_proto, s->auth_data); pclose(f); } else fprintf(stderr, "Could not run %s\n", user_rc); } else if (stat(_PATH_SSH_SYSTEM_RC, &st) >= 0) { if (debug_flag) fprintf(stderr, "Running %s %s\n", _PATH_BSHELL, _PATH_SSH_SYSTEM_RC); f = popen(_PATH_BSHELL " " _PATH_SSH_SYSTEM_RC, "w"); if (f) { if (do_xauth) fprintf(f, "%s %s\n", s->auth_proto, s->auth_data); pclose(f); } else fprintf(stderr, "Could not run %s\n", _PATH_SSH_SYSTEM_RC); } else if (do_xauth && options.xauth_location != NULL) { /* Add authority data to .Xauthority if appropriate. */ if (debug_flag) { fprintf(stderr, "Running %.500s remove %.100s\n", options.xauth_location, s->auth_display); fprintf(stderr, "%.500s add %.100s %.100s %.100s\n", options.xauth_location, s->auth_display, s->auth_proto, s->auth_data); } if (xasprintf(&cmd, "%s -q -", options.xauth_location) == -1) fatal_f("xasprintf: %s", strerror(errno)); f = popen(cmd, "w"); if (f) { fprintf(f, "remove %s\n", s->auth_display); fprintf(f, "add %s %s %s\n", s->auth_display, s->auth_proto, s->auth_data); pclose(f); } else { fprintf(stderr, "Could not run %s\n", cmd); } } free(cmd); free(user_rc); } static void do_nologin(struct passwd *pw) { FILE *f = NULL; char buf[1024], *nl, *def_nl = _PATH_NOLOGIN; struct stat sb; #ifdef HAVE_LOGIN_CAP if (login_getcapbool(lc, "ignorenologin", 0) || pw->pw_uid == 0) return; nl = login_getcapstr(lc, "nologin", def_nl, def_nl); #else if (pw->pw_uid == 0) return; nl = def_nl; #endif if (stat(nl, &sb) == -1) return; /* /etc/nologin exists. Print its contents if we can and exit. */ logit("User %.100s not allowed because %s exists", pw->pw_name, nl); if ((f = fopen(nl, "r")) != NULL) { while (fgets(buf, sizeof(buf), f)) fputs(buf, stderr); fclose(f); } exit(254); } /* * Chroot into a directory after checking it for safety: all path components * must be root-owned directories with strict permissions. */ static void safely_chroot(const char *path, uid_t uid) { const char *cp; char component[PATH_MAX]; struct stat st; if (!path_absolute(path)) fatal("chroot path does not begin at root"); if (strlen(path) >= sizeof(component)) fatal("chroot path too long"); /* * Descend the path, checking that each component is a * root-owned directory with strict permissions. */ for (cp = path; cp != NULL;) { if ((cp = strchr(cp, '/')) == NULL) strlcpy(component, path, sizeof(component)); else { cp++; memcpy(component, path, cp - path); component[cp - path] = '\0'; } debug3_f("checking '%s'", component); if (stat(component, &st) != 0) fatal_f("stat(\"%s\"): %s", component, strerror(errno)); if (st.st_uid != 0 || (st.st_mode & 022) != 0) fatal("bad ownership or modes for chroot " "directory %s\"%s\"", cp == NULL ? "" : "component ", component); if (!S_ISDIR(st.st_mode)) fatal("chroot path %s\"%s\" is not a directory", cp == NULL ? "" : "component ", component); } if (chdir(path) == -1) fatal("Unable to chdir to chroot path \"%s\": " "%s", path, strerror(errno)); if (chroot(path) == -1) fatal("chroot(\"%s\"): %s", path, strerror(errno)); if (chdir("/") == -1) fatal_f("chdir(/) after chroot: %s", strerror(errno)); verbose("Changed root directory to \"%s\"", path); } /* Set login name, uid, gid, and groups. */ void do_setusercontext(struct passwd *pw) { char uidstr[32], *chroot_path, *tmp; platform_setusercontext(pw); if (platform_privileged_uidswap()) { #ifdef HAVE_LOGIN_CAP if (setusercontext(lc, pw, pw->pw_uid, (LOGIN_SETALL & ~(LOGIN_SETPATH|LOGIN_SETUSER))) < 0) { perror("unable to set user context"); exit(1); } #else if (setlogin(pw->pw_name) < 0) error("setlogin failed: %s", strerror(errno)); if (setgid(pw->pw_gid) < 0) { perror("setgid"); exit(1); } /* Initialize the group list. */ if (initgroups(pw->pw_name, pw->pw_gid) < 0) { perror("initgroups"); exit(1); } endgrent(); #endif platform_setusercontext_post_groups(pw); if (!in_chroot && options.chroot_directory != NULL && strcasecmp(options.chroot_directory, "none") != 0) { tmp = tilde_expand_filename(options.chroot_directory, pw->pw_uid); snprintf(uidstr, sizeof(uidstr), "%llu", (unsigned long long)pw->pw_uid); chroot_path = percent_expand(tmp, "h", pw->pw_dir, "u", pw->pw_name, "U", uidstr, (char *)NULL); safely_chroot(chroot_path, pw->pw_uid); free(tmp); free(chroot_path); /* Make sure we don't attempt to chroot again */ free(options.chroot_directory); options.chroot_directory = NULL; in_chroot = 1; } #ifdef HAVE_LOGIN_CAP if (setusercontext(lc, pw, pw->pw_uid, LOGIN_SETUSER) < 0) { perror("unable to set user context (setuser)"); exit(1); } /* * FreeBSD's setusercontext() will not apply the user's * own umask setting unless running with the user's UID. */ (void) setusercontext(lc, pw, pw->pw_uid, LOGIN_SETUMASK); #else # ifdef USE_LIBIAF /* * In a chroot environment, the set_id() will always fail; * typically because of the lack of necessary authentication * services and runtime such as ./usr/lib/libiaf.so, * ./usr/lib/libpam.so.1, and ./etc/passwd We skip it in the * internal sftp chroot case. We'll lose auditing and ACLs but * permanently_set_uid will take care of the rest. */ if (!in_chroot && set_id(pw->pw_name) != 0) fatal("set_id(%s) Failed", pw->pw_name); # endif /* USE_LIBIAF */ /* Permanently switch to the desired uid. */ permanently_set_uid(pw); #endif } else if (options.chroot_directory != NULL && strcasecmp(options.chroot_directory, "none") != 0) { fatal("server lacks privileges to chroot to ChrootDirectory"); } if (getuid() != pw->pw_uid || geteuid() != pw->pw_uid) fatal("Failed to set uids to %u.", (u_int) pw->pw_uid); } static void do_pwchange(Session *s) { fflush(NULL); fprintf(stderr, "WARNING: Your password has expired.\n"); if (s->ttyfd != -1) { fprintf(stderr, "You must change your password now and login again!\n"); #ifdef WITH_SELINUX setexeccon(NULL); #endif #ifdef PASSWD_NEEDS_USERNAME execl(_PATH_PASSWD_PROG, "passwd", s->pw->pw_name, (char *)NULL); #else execl(_PATH_PASSWD_PROG, "passwd", (char *)NULL); #endif perror("passwd"); } else { fprintf(stderr, "Password change required but no TTY available.\n"); } exit(1); } static void child_close_fds(struct ssh *ssh) { extern int auth_sock; if (auth_sock != -1) { close(auth_sock); auth_sock = -1; } if (ssh_packet_get_connection_in(ssh) == ssh_packet_get_connection_out(ssh)) close(ssh_packet_get_connection_in(ssh)); else { close(ssh_packet_get_connection_in(ssh)); close(ssh_packet_get_connection_out(ssh)); } /* * Close all descriptors related to channels. They will still remain * open in the parent. */ /* XXX better use close-on-exec? -markus */ channel_close_all(ssh); /* * Close any extra file descriptors. Note that there may still be * descriptors left by system functions. They will be closed later. */ endpwent(); /* Stop directing logs to a high-numbered fd before we close it */ log_redirect_stderr_to(NULL); /* * Close any extra open file descriptors so that we don't have them * hanging around in clients. Note that we want to do this after * initgroups, because at least on Solaris 2.3 it leaves file * descriptors open. */ closefrom(STDERR_FILENO + 1); } /* * Performs common processing for the child, such as setting up the * environment, closing extra file descriptors, setting the user and group * ids, and executing the command or shell. */ #define ARGV_MAX 10 void do_child(struct ssh *ssh, Session *s, const char *command) { extern char **environ; char **env, *argv[ARGV_MAX], remote_id[512]; const char *shell, *shell0; struct passwd *pw = s->pw; int r = 0; sshpkt_fmt_connection_id(ssh, remote_id, sizeof(remote_id)); /* remove hostkey from the child's memory */ destroy_sensitive_data(); ssh_packet_clear_keys(ssh); /* Force a password change */ if (s->authctxt->force_pwchange) { do_setusercontext(pw); child_close_fds(ssh); do_pwchange(s); exit(1); } /* * Login(1) does this as well, and it needs uid 0 for the "-h" * switch, so we let login(1) to this for us. */ #ifdef HAVE_OSF_SIA session_setup_sia(pw, s->ttyfd == -1 ? NULL : s->tty); if (!check_quietlogin(s, command)) do_motd(); #else /* HAVE_OSF_SIA */ /* When PAM is enabled we rely on it to do the nologin check */ if (!options.use_pam) do_nologin(pw); do_setusercontext(pw); /* * PAM session modules in do_setusercontext may have * generated messages, so if this in an interactive * login then display them too. */ if (!check_quietlogin(s, command)) display_loginmsg(); #endif /* HAVE_OSF_SIA */ #ifdef USE_PAM if (options.use_pam && !is_pam_session_open()) { debug3("PAM session not opened, exiting"); display_loginmsg(); exit(254); } #endif /* * Get the shell from the password data. An empty shell field is * legal, and means /bin/sh. */ shell = (pw->pw_shell[0] == '\0') ? _PATH_BSHELL : pw->pw_shell; /* * Make sure $SHELL points to the shell from the password file, * even if shell is overridden from login.conf */ env = do_setup_env(ssh, s, shell); #ifdef HAVE_LOGIN_CAP shell = login_getcapstr(lc, "shell", (char *)shell, (char *)shell); #endif /* * Close the connection descriptors; note that this is the child, and * the server will still have the socket open, and it is important * that we do not shutdown it. Note that the descriptors cannot be * closed before building the environment, as we call * ssh_remote_ipaddr there. */ child_close_fds(ssh); /* * Must take new environment into use so that .ssh/rc, * /etc/ssh/sshrc and xauth are run in the proper environment. */ environ = env; #if defined(KRB5) && defined(USE_AFS) /* * At this point, we check to see if AFS is active and if we have * a valid Kerberos 5 TGT. If so, it seems like a good idea to see * if we can (and need to) extend the ticket into an AFS token. If * we don't do this, we run into potential problems if the user's * home directory is in AFS and it's not world-readable. */ if (options.kerberos_get_afs_token && k_hasafs() && (s->authctxt->krb5_ctx != NULL)) { char cell[64]; debug("Getting AFS token"); k_setpag(); if (k_afs_cell_of_file(pw->pw_dir, cell, sizeof(cell)) == 0) krb5_afslog(s->authctxt->krb5_ctx, s->authctxt->krb5_fwd_ccache, cell, NULL); krb5_afslog_home(s->authctxt->krb5_ctx, s->authctxt->krb5_fwd_ccache, NULL, NULL, pw->pw_dir); } #endif /* Change current directory to the user's home directory. */ if (chdir(pw->pw_dir) == -1) { /* Suppress missing homedir warning for chroot case */ #ifdef HAVE_LOGIN_CAP r = login_getcapbool(lc, "requirehome", 0); #endif if (r || !in_chroot) { fprintf(stderr, "Could not chdir to home " "directory %s: %s\n", pw->pw_dir, strerror(errno)); } if (r) exit(1); } closefrom(STDERR_FILENO + 1); do_rc_files(ssh, s, shell); /* restore SIGPIPE for child */ ssh_signal(SIGPIPE, SIG_DFL); if (s->is_subsystem == SUBSYSTEM_INT_SFTP_ERROR) { error("Connection from %s: refusing non-sftp session", remote_id); printf("This service allows sftp connections only.\n"); fflush(NULL); exit(1); } else if (s->is_subsystem == SUBSYSTEM_INT_SFTP) { extern int optind, optreset; int i; char *p, *args; setproctitle("%s@%s", s->pw->pw_name, INTERNAL_SFTP_NAME); args = xstrdup(command ? command : "sftp-server"); for (i = 0, (p = strtok(args, " ")); p; (p = strtok(NULL, " "))) if (i < ARGV_MAX - 1) argv[i++] = p; argv[i] = NULL; optind = optreset = 1; __progname = argv[0]; #ifdef WITH_SELINUX ssh_selinux_change_context("sftpd_t"); #endif exit(sftp_server_main(i, argv, s->pw)); } fflush(NULL); /* Get the last component of the shell name. */ if ((shell0 = strrchr(shell, '/')) != NULL) shell0++; else shell0 = shell; /* * If we have no command, execute the shell. In this case, the shell * name to be passed in argv[0] is preceded by '-' to indicate that * this is a login shell. */ if (!command) { char argv0[256]; /* Start the shell. Set initial character to '-'. */ argv0[0] = '-'; if (strlcpy(argv0 + 1, shell0, sizeof(argv0) - 1) >= sizeof(argv0) - 1) { errno = EINVAL; perror(shell); exit(1); } /* Execute the shell. */ argv[0] = argv0; argv[1] = NULL; execve(shell, argv, env); /* Executing the shell failed. */ perror(shell); exit(1); } /* * Execute the command using the user's shell. This uses the -c * option to execute the command. */ argv[0] = (char *) shell0; argv[1] = "-c"; argv[2] = (char *) command; argv[3] = NULL; execve(shell, argv, env); perror(shell); exit(1); } void session_unused(int id) { debug3_f("session id %d unused", id); if (id >= options.max_sessions || id >= sessions_nalloc) { fatal_f("insane session id %d (max %d nalloc %d)", id, options.max_sessions, sessions_nalloc); } memset(&sessions[id], 0, sizeof(*sessions)); sessions[id].self = id; sessions[id].used = 0; sessions[id].chanid = -1; sessions[id].ptyfd = -1; sessions[id].ttyfd = -1; sessions[id].ptymaster = -1; sessions[id].x11_chanids = NULL; sessions[id].next_unused = sessions_first_unused; sessions_first_unused = id; } Session * session_new(void) { Session *s, *tmp; if (sessions_first_unused == -1) { if (sessions_nalloc >= options.max_sessions) return NULL; debug2_f("allocate (allocated %d max %d)", sessions_nalloc, options.max_sessions); tmp = xrecallocarray(sessions, sessions_nalloc, sessions_nalloc + 1, sizeof(*sessions)); if (tmp == NULL) { error_f("cannot allocate %d sessions", sessions_nalloc + 1); return NULL; } sessions = tmp; session_unused(sessions_nalloc++); } if (sessions_first_unused >= sessions_nalloc || sessions_first_unused < 0) { fatal_f("insane first_unused %d max %d nalloc %d", sessions_first_unused, options.max_sessions, sessions_nalloc); } s = &sessions[sessions_first_unused]; if (s->used) fatal_f("session %d already used", sessions_first_unused); sessions_first_unused = s->next_unused; s->used = 1; s->next_unused = -1; debug("session_new: session %d", s->self); return s; } static void session_dump(void) { int i; for (i = 0; i < sessions_nalloc; i++) { Session *s = &sessions[i]; debug("dump: used %d next_unused %d session %d " "channel %d pid %ld", s->used, s->next_unused, s->self, s->chanid, (long)s->pid); } } int session_open(Authctxt *authctxt, int chanid) { Session *s = session_new(); debug("session_open: channel %d", chanid); if (s == NULL) { error("no more sessions"); return 0; } s->authctxt = authctxt; s->pw = authctxt->pw; if (s->pw == NULL || !authctxt->valid) fatal("no user for session %d", s->self); debug("session_open: session %d: link with channel %d", s->self, chanid); s->chanid = chanid; return 1; } Session * session_by_tty(char *tty) { int i; for (i = 0; i < sessions_nalloc; i++) { Session *s = &sessions[i]; if (s->used && s->ttyfd != -1 && strcmp(s->tty, tty) == 0) { debug("session_by_tty: session %d tty %s", i, tty); return s; } } debug("session_by_tty: unknown tty %.100s", tty); session_dump(); return NULL; } static Session * session_by_channel(int id) { int i; for (i = 0; i < sessions_nalloc; i++) { Session *s = &sessions[i]; if (s->used && s->chanid == id) { debug("session_by_channel: session %d channel %d", i, id); return s; } } debug("session_by_channel: unknown channel %d", id); session_dump(); return NULL; } static Session * session_by_x11_channel(int id) { int i, j; for (i = 0; i < sessions_nalloc; i++) { Session *s = &sessions[i]; if (s->x11_chanids == NULL || !s->used) continue; for (j = 0; s->x11_chanids[j] != -1; j++) { if (s->x11_chanids[j] == id) { debug("session_by_x11_channel: session %d " "channel %d", s->self, id); return s; } } } debug("session_by_x11_channel: unknown channel %d", id); session_dump(); return NULL; } static Session * session_by_pid(pid_t pid) { int i; debug("session_by_pid: pid %ld", (long)pid); for (i = 0; i < sessions_nalloc; i++) { Session *s = &sessions[i]; if (s->used && s->pid == pid) return s; } error("session_by_pid: unknown pid %ld", (long)pid); session_dump(); return NULL; } static int session_window_change_req(struct ssh *ssh, Session *s) { int r; if ((r = sshpkt_get_u32(ssh, &s->col)) != 0 || (r = sshpkt_get_u32(ssh, &s->row)) != 0 || (r = sshpkt_get_u32(ssh, &s->xpixel)) != 0 || (r = sshpkt_get_u32(ssh, &s->ypixel)) != 0 || (r = sshpkt_get_end(ssh)) != 0) sshpkt_fatal(ssh, r, "%s: parse packet", __func__); pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel); return 1; } static int session_pty_req(struct ssh *ssh, Session *s) { int r; if (!auth_opts->permit_pty_flag || !options.permit_tty) { debug("Allocating a pty not permitted for this connection."); return 0; } if (s->ttyfd != -1) { ssh_packet_disconnect(ssh, "Protocol error: you already have a pty."); return 0; } if ((r = sshpkt_get_cstring(ssh, &s->term, NULL)) != 0 || (r = sshpkt_get_u32(ssh, &s->col)) != 0 || (r = sshpkt_get_u32(ssh, &s->row)) != 0 || (r = sshpkt_get_u32(ssh, &s->xpixel)) != 0 || (r = sshpkt_get_u32(ssh, &s->ypixel)) != 0) sshpkt_fatal(ssh, r, "%s: parse packet", __func__); if (strcmp(s->term, "") == 0) { free(s->term); s->term = NULL; } /* Allocate a pty and open it. */ debug("Allocating pty."); if (!PRIVSEP(pty_allocate(&s->ptyfd, &s->ttyfd, s->tty, sizeof(s->tty)))) { free(s->term); s->term = NULL; s->ptyfd = -1; s->ttyfd = -1; error("session_pty_req: session %d alloc failed", s->self); return 0; } debug("session_pty_req: session %d alloc %s", s->self, s->tty); ssh_tty_parse_modes(ssh, s->ttyfd); if ((r = sshpkt_get_end(ssh)) != 0) sshpkt_fatal(ssh, r, "%s: parse packet", __func__); if (!use_privsep) pty_setowner(s->pw, s->tty); /* Set window size from the packet. */ pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel); session_proctitle(s); return 1; } static int session_subsystem_req(struct ssh *ssh, Session *s) { struct stat st; int r, success = 0; char *prog, *cmd, *type; u_int i; if ((r = sshpkt_get_cstring(ssh, &s->subsys, NULL)) != 0 || (r = sshpkt_get_end(ssh)) != 0) sshpkt_fatal(ssh, r, "%s: parse packet", __func__); debug2("subsystem request for %.100s by user %s", s->subsys, s->pw->pw_name); for (i = 0; i < options.num_subsystems; i++) { if (strcmp(s->subsys, options.subsystem_name[i]) == 0) { prog = options.subsystem_command[i]; cmd = options.subsystem_args[i]; if (strcmp(INTERNAL_SFTP_NAME, prog) == 0) { s->is_subsystem = SUBSYSTEM_INT_SFTP; debug("subsystem: %s", prog); } else { if (stat(prog, &st) == -1) debug("subsystem: cannot stat %s: %s", prog, strerror(errno)); s->is_subsystem = SUBSYSTEM_EXT; debug("subsystem: exec() %s", cmd); } xasprintf(&type, "session:subsystem:%s", options.subsystem_name[i]); channel_set_xtype(ssh, s->chanid, type); free(type); success = do_exec(ssh, s, cmd) == 0; break; } } if (!success) logit("subsystem request for %.100s by user %s failed, " "subsystem not found", s->subsys, s->pw->pw_name); return success; } static int session_x11_req(struct ssh *ssh, Session *s) { int r, success; u_char single_connection = 0; if (s->auth_proto != NULL || s->auth_data != NULL) { error("session_x11_req: session %d: " "x11 forwarding already active", s->self); return 0; } if ((r = sshpkt_get_u8(ssh, &single_connection)) != 0 || (r = sshpkt_get_cstring(ssh, &s->auth_proto, NULL)) != 0 || (r = sshpkt_get_cstring(ssh, &s->auth_data, NULL)) != 0 || (r = sshpkt_get_u32(ssh, &s->screen)) != 0 || (r = sshpkt_get_end(ssh)) != 0) sshpkt_fatal(ssh, r, "%s: parse packet", __func__); s->single_connection = single_connection; if (xauth_valid_string(s->auth_proto) && xauth_valid_string(s->auth_data)) success = session_setup_x11fwd(ssh, s); else { success = 0; error("Invalid X11 forwarding data"); } if (!success) { free(s->auth_proto); free(s->auth_data); s->auth_proto = NULL; s->auth_data = NULL; } return success; } static int session_shell_req(struct ssh *ssh, Session *s) { int r; if ((r = sshpkt_get_end(ssh)) != 0) sshpkt_fatal(ssh, r, "%s: parse packet", __func__); channel_set_xtype(ssh, s->chanid, "session:shell"); return do_exec(ssh, s, NULL) == 0; } static int session_exec_req(struct ssh *ssh, Session *s) { u_int success; int r; char *command = NULL; if ((r = sshpkt_get_cstring(ssh, &command, NULL)) != 0 || (r = sshpkt_get_end(ssh)) != 0) sshpkt_fatal(ssh, r, "%s: parse packet", __func__); channel_set_xtype(ssh, s->chanid, "session:command"); success = do_exec(ssh, s, command) == 0; free(command); return success; } static int session_break_req(struct ssh *ssh, Session *s) { int r; if ((r = sshpkt_get_u32(ssh, NULL)) != 0 || /* ignore */ (r = sshpkt_get_end(ssh)) != 0) sshpkt_fatal(ssh, r, "%s: parse packet", __func__); if (s->ptymaster == -1 || tcsendbreak(s->ptymaster, 0) == -1) return 0; return 1; } static int session_env_req(struct ssh *ssh, Session *s) { char *name, *val; u_int i; int r; if ((r = sshpkt_get_cstring(ssh, &name, NULL)) != 0 || (r = sshpkt_get_cstring(ssh, &val, NULL)) != 0 || (r = sshpkt_get_end(ssh)) != 0) sshpkt_fatal(ssh, r, "%s: parse packet", __func__); /* Don't set too many environment variables */ if (s->num_env > 128) { debug2("Ignoring env request %s: too many env vars", name); goto fail; } for (i = 0; i < options.num_accept_env; i++) { if (match_pattern(name, options.accept_env[i])) { debug2("Setting env %d: %s=%s", s->num_env, name, val); s->env = xrecallocarray(s->env, s->num_env, s->num_env + 1, sizeof(*s->env)); s->env[s->num_env].name = name; s->env[s->num_env].val = val; s->num_env++; return (1); } } debug2("Ignoring env request %s: disallowed name", name); fail: free(name); free(val); return (0); } /* * Conversion of signals from ssh channel request names. * Subset of signals from RFC 4254 section 6.10C, with SIGINFO as * local extension. */ static int name2sig(char *name) { #define SSH_SIG(x) if (strcmp(name, #x) == 0) return SIG ## x SSH_SIG(HUP); SSH_SIG(INT); SSH_SIG(KILL); SSH_SIG(QUIT); SSH_SIG(TERM); SSH_SIG(USR1); SSH_SIG(USR2); #undef SSH_SIG #ifdef SIGINFO if (strcmp(name, "INFO@openssh.com") == 0) return SIGINFO; #endif return -1; } static int session_signal_req(struct ssh *ssh, Session *s) { char *signame = NULL; int r, sig, success = 0; if ((r = sshpkt_get_cstring(ssh, &signame, NULL)) != 0 || (r = sshpkt_get_end(ssh)) != 0) { error_fr(r, "parse"); goto out; } if ((sig = name2sig(signame)) == -1) { error_f("unsupported signal \"%s\"", signame); goto out; } if (s->pid <= 0) { error_f("no pid for session %d", s->self); goto out; } if (s->forced || s->is_subsystem) { error_f("refusing to send signal %s to %s session", signame, s->forced ? "forced-command" : "subsystem"); goto out; } if (!use_privsep || mm_is_monitor()) { error_f("session signalling requires privilege separation"); goto out; } debug_f("signal %s, killpg(%ld, %d)", signame, (long)s->pid, sig); temporarily_use_uid(s->pw); r = killpg(s->pid, sig); restore_uid(); if (r != 0) { error_f("killpg(%ld, %d): %s", (long)s->pid, sig, strerror(errno)); goto out; } /* success */ success = 1; out: free(signame); return success; } static int session_auth_agent_req(struct ssh *ssh, Session *s) { static int called = 0; int r; if ((r = sshpkt_get_end(ssh)) != 0) sshpkt_fatal(ssh, r, "%s: parse packet", __func__); if (!auth_opts->permit_agent_forwarding_flag || !options.allow_agent_forwarding) { debug_f("agent forwarding disabled"); return 0; } if (called) { return 0; } else { called = 1; return auth_input_request_forwarding(ssh, s->pw); } } int session_input_channel_req(struct ssh *ssh, Channel *c, const char *rtype) { int success = 0; Session *s; if ((s = session_by_channel(c->self)) == NULL) { logit_f("no session %d req %.100s", c->self, rtype); return 0; } debug_f("session %d req %s", s->self, rtype); /* * a session is in LARVAL state until a shell, a command * or a subsystem is executed */ if (c->type == SSH_CHANNEL_LARVAL) { if (strcmp(rtype, "shell") == 0) { success = session_shell_req(ssh, s); } else if (strcmp(rtype, "exec") == 0) { success = session_exec_req(ssh, s); } else if (strcmp(rtype, "pty-req") == 0) { success = session_pty_req(ssh, s); } else if (strcmp(rtype, "x11-req") == 0) { success = session_x11_req(ssh, s); } else if (strcmp(rtype, "auth-agent-req@openssh.com") == 0) { success = session_auth_agent_req(ssh, s); } else if (strcmp(rtype, "subsystem") == 0) { success = session_subsystem_req(ssh, s); } else if (strcmp(rtype, "env") == 0) { success = session_env_req(ssh, s); } } if (strcmp(rtype, "window-change") == 0) { success = session_window_change_req(ssh, s); } else if (strcmp(rtype, "break") == 0) { success = session_break_req(ssh, s); } else if (strcmp(rtype, "signal") == 0) { success = session_signal_req(ssh, s); } return success; } void session_set_fds(struct ssh *ssh, Session *s, int fdin, int fdout, int fderr, int ignore_fderr, int is_tty) { /* * now that have a child and a pipe to the child, * we can activate our channel and register the fd's */ if (s->chanid == -1) fatal("no channel for session %d", s->self); channel_set_fds(ssh, s->chanid, fdout, fdin, fderr, ignore_fderr ? CHAN_EXTENDED_IGNORE : CHAN_EXTENDED_READ, 1, is_tty, CHAN_SES_WINDOW_DEFAULT); } /* * Function to perform pty cleanup. Also called if we get aborted abnormally * (e.g., due to a dropped connection). */ void session_pty_cleanup2(Session *s) { if (s == NULL) { error_f("no session"); return; } if (s->ttyfd == -1) return; debug_f("session %d release %s", s->self, s->tty); /* Record that the user has logged out. */ if (s->pid != 0) record_logout(s->pid, s->tty, s->pw->pw_name); /* Release the pseudo-tty. */ if (getuid() == 0) pty_release(s->tty); /* * Close the server side of the socket pairs. We must do this after * the pty cleanup, so that another process doesn't get this pty * while we're still cleaning up. */ if (s->ptymaster != -1 && close(s->ptymaster) == -1) error("close(s->ptymaster/%d): %s", s->ptymaster, strerror(errno)); /* unlink pty from session */ s->ttyfd = -1; } void session_pty_cleanup(Session *s) { PRIVSEP(session_pty_cleanup2(s)); } static char * sig2name(int sig) { #define SSH_SIG(x) if (sig == SIG ## x) return #x SSH_SIG(ABRT); SSH_SIG(ALRM); SSH_SIG(FPE); SSH_SIG(HUP); SSH_SIG(ILL); SSH_SIG(INT); SSH_SIG(KILL); SSH_SIG(PIPE); SSH_SIG(QUIT); SSH_SIG(SEGV); SSH_SIG(TERM); SSH_SIG(USR1); SSH_SIG(USR2); #undef SSH_SIG return "SIG@openssh.com"; } static void session_close_x11(struct ssh *ssh, int id) { Channel *c; if ((c = channel_by_id(ssh, id)) == NULL) { debug_f("x11 channel %d missing", id); } else { /* Detach X11 listener */ debug_f("detach x11 channel %d", id); channel_cancel_cleanup(ssh, id); if (c->ostate != CHAN_OUTPUT_CLOSED) chan_mark_dead(ssh, c); } } static void session_close_single_x11(struct ssh *ssh, int id, int force, void *arg) { Session *s; u_int i; debug3_f("channel %d", id); channel_cancel_cleanup(ssh, id); if ((s = session_by_x11_channel(id)) == NULL) fatal_f("no x11 channel %d", id); for (i = 0; s->x11_chanids[i] != -1; i++) { debug_f("session %d: closing channel %d", s->self, s->x11_chanids[i]); /* * The channel "id" is already closing, but make sure we * close all of its siblings. */ if (s->x11_chanids[i] != id) session_close_x11(ssh, s->x11_chanids[i]); } free(s->x11_chanids); s->x11_chanids = NULL; free(s->display); s->display = NULL; free(s->auth_proto); s->auth_proto = NULL; free(s->auth_data); s->auth_data = NULL; free(s->auth_display); s->auth_display = NULL; } static void session_exit_message(struct ssh *ssh, Session *s, int status) { Channel *c; int r; + char *note = NULL; if ((c = channel_lookup(ssh, s->chanid)) == NULL) fatal_f("session %d: no channel %d", s->self, s->chanid); - debug_f("session %d channel %d pid %ld", - s->self, s->chanid, (long)s->pid); if (WIFEXITED(status)) { channel_request_start(ssh, s->chanid, "exit-status", 0); if ((r = sshpkt_put_u32(ssh, WEXITSTATUS(status))) != 0 || (r = sshpkt_send(ssh)) != 0) sshpkt_fatal(ssh, r, "%s: exit reply", __func__); + xasprintf(¬e, "exit %d", WEXITSTATUS(status)); } else if (WIFSIGNALED(status)) { channel_request_start(ssh, s->chanid, "exit-signal", 0); #ifndef WCOREDUMP # define WCOREDUMP(x) (0) #endif if ((r = sshpkt_put_cstring(ssh, sig2name(WTERMSIG(status)))) != 0 || (r = sshpkt_put_u8(ssh, WCOREDUMP(status)? 1 : 0)) != 0 || (r = sshpkt_put_cstring(ssh, "")) != 0 || (r = sshpkt_put_cstring(ssh, "")) != 0 || (r = sshpkt_send(ssh)) != 0) sshpkt_fatal(ssh, r, "%s: exit reply", __func__); + xasprintf(¬e, "signal %d%s", WTERMSIG(status), + WCOREDUMP(status) ? " core dumped" : ""); } else { /* Some weird exit cause. Just exit. */ - ssh_packet_disconnect(ssh, "wait returned status %04x.", status); + ssh_packet_disconnect(ssh, "wait returned status %04x.", + status); } + debug_f("session %d channel %d pid %ld %s", s->self, s->chanid, + (long)s->pid, note == NULL ? "UNKNOWN" : note); + free(note); + /* disconnect channel */ debug_f("release channel %d", s->chanid); /* * Adjust cleanup callback attachment to send close messages when * the channel gets EOF. The session will be then be closed * by session_close_by_channel when the child sessions close their fds. */ channel_register_cleanup(ssh, c->self, session_close_by_channel, 1); /* * emulate a write failure with 'chan_write_failed', nobody will be * interested in data we write. * Note that we must not call 'chan_read_failed', since there could * be some more data waiting in the pipe. */ if (c->ostate != CHAN_OUTPUT_CLOSED) chan_write_failed(ssh, c); } void session_close(struct ssh *ssh, Session *s) { u_int i; verbose("Close session: user %s from %.200s port %d id %d", s->pw->pw_name, ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), s->self); if (s->ttyfd != -1) session_pty_cleanup(s); free(s->term); free(s->display); free(s->x11_chanids); free(s->auth_display); free(s->auth_data); free(s->auth_proto); free(s->subsys); if (s->env != NULL) { for (i = 0; i < s->num_env; i++) { free(s->env[i].name); free(s->env[i].val); } free(s->env); } session_proctitle(s); session_unused(s->self); } void session_close_by_pid(struct ssh *ssh, pid_t pid, int status) { Session *s = session_by_pid(pid); if (s == NULL) { debug_f("no session for pid %ld", (long)pid); return; } if (s->chanid != -1) session_exit_message(ssh, s, status); if (s->ttyfd != -1) session_pty_cleanup(s); s->pid = 0; } /* * this is called when a channel dies before * the session 'child' itself dies */ void session_close_by_channel(struct ssh *ssh, int id, int force, void *arg) { Session *s = session_by_channel(id); u_int i; if (s == NULL) { debug_f("no session for id %d", id); return; } debug_f("channel %d child %ld", id, (long)s->pid); if (s->pid != 0) { debug_f("channel %d: has child, ttyfd %d", id, s->ttyfd); /* * delay detach of session (unless this is a forced close), * but release pty, since the fd's to the child are already * closed */ if (s->ttyfd != -1) session_pty_cleanup(s); if (!force) return; } /* detach by removing callback */ channel_cancel_cleanup(ssh, s->chanid); /* Close any X11 listeners associated with this session */ if (s->x11_chanids != NULL) { for (i = 0; s->x11_chanids[i] != -1; i++) { session_close_x11(ssh, s->x11_chanids[i]); s->x11_chanids[i] = -1; } } s->chanid = -1; session_close(ssh, s); } void session_destroy_all(struct ssh *ssh, void (*closefunc)(Session *)) { int i; for (i = 0; i < sessions_nalloc; i++) { Session *s = &sessions[i]; if (s->used) { if (closefunc != NULL) closefunc(s); else session_close(ssh, s); } } } static char * session_tty_list(void) { static char buf[1024]; int i; char *cp; buf[0] = '\0'; for (i = 0; i < sessions_nalloc; i++) { Session *s = &sessions[i]; if (s->used && s->ttyfd != -1) { if (strncmp(s->tty, "/dev/", 5) != 0) { cp = strrchr(s->tty, '/'); cp = (cp == NULL) ? s->tty : cp + 1; } else cp = s->tty + 5; if (buf[0] != '\0') strlcat(buf, ",", sizeof buf); strlcat(buf, cp, sizeof buf); } } if (buf[0] == '\0') strlcpy(buf, "notty", sizeof buf); return buf; } void session_proctitle(Session *s) { if (s->pw == NULL) error("no user for session %d", s->self); else setproctitle("%s@%s", s->pw->pw_name, session_tty_list()); } int session_setup_x11fwd(struct ssh *ssh, Session *s) { struct stat st; char display[512], auth_display[512]; char hostname[NI_MAXHOST]; u_int i; if (!auth_opts->permit_x11_forwarding_flag) { ssh_packet_send_debug(ssh, "X11 forwarding disabled by key options."); return 0; } if (!options.x11_forwarding) { debug("X11 forwarding disabled in server configuration file."); return 0; } if (options.xauth_location == NULL || (stat(options.xauth_location, &st) == -1)) { ssh_packet_send_debug(ssh, "No xauth program; cannot forward X11."); return 0; } if (s->display != NULL) { debug("X11 display already set."); return 0; } if (x11_create_display_inet(ssh, options.x11_display_offset, options.x11_use_localhost, s->single_connection, &s->display_number, &s->x11_chanids) == -1) { debug("x11_create_display_inet failed."); return 0; } for (i = 0; s->x11_chanids[i] != -1; i++) { channel_register_cleanup(ssh, s->x11_chanids[i], session_close_single_x11, 0); } /* Set up a suitable value for the DISPLAY variable. */ if (gethostname(hostname, sizeof(hostname)) == -1) fatal("gethostname: %.100s", strerror(errno)); /* * auth_display must be used as the displayname when the * authorization entry is added with xauth(1). This will be * different than the DISPLAY string for localhost displays. */ if (options.x11_use_localhost) { snprintf(display, sizeof display, "localhost:%u.%u", s->display_number, s->screen); snprintf(auth_display, sizeof auth_display, "unix:%u.%u", s->display_number, s->screen); s->display = xstrdup(display); s->auth_display = xstrdup(auth_display); } else { #ifdef IPADDR_IN_DISPLAY struct hostent *he; struct in_addr my_addr; he = gethostbyname(hostname); if (he == NULL) { error("Can't get IP address for X11 DISPLAY."); ssh_packet_send_debug(ssh, "Can't get IP address for X11 DISPLAY."); return 0; } memcpy(&my_addr, he->h_addr_list[0], sizeof(struct in_addr)); snprintf(display, sizeof display, "%.50s:%u.%u", inet_ntoa(my_addr), s->display_number, s->screen); #else snprintf(display, sizeof display, "%.400s:%u.%u", hostname, s->display_number, s->screen); #endif s->display = xstrdup(display); s->auth_display = xstrdup(display); } return 1; } static void do_authenticated2(struct ssh *ssh, Authctxt *authctxt) { server_loop2(ssh, authctxt); } void do_cleanup(struct ssh *ssh, Authctxt *authctxt) { static int called = 0; debug("do_cleanup"); /* no cleanup if we're in the child for login shell */ if (is_child) return; /* avoid double cleanup */ if (called) return; called = 1; if (authctxt == NULL) return; #ifdef USE_PAM if (options.use_pam) { sshpam_cleanup(); sshpam_thread_cleanup(); } #endif if (!authctxt->authenticated) return; #ifdef KRB5 if (options.kerberos_ticket_cleanup && authctxt->krb5_ctx) krb5_cleanup_proc(authctxt); #endif #ifdef GSSAPI if (options.gss_cleanup_creds) ssh_gssapi_cleanup_creds(); #endif /* remove agent socket */ auth_sock_cleanup_proc(authctxt->pw); /* remove userauth info */ if (auth_info_file != NULL) { temporarily_use_uid(authctxt->pw); unlink(auth_info_file); restore_uid(); free(auth_info_file); auth_info_file = NULL; } /* * Cleanup ptys/utmp only if privsep is disabled, * or if running in monitor. */ if (!use_privsep || mm_is_monitor()) session_destroy_all(ssh, session_pty_cleanup2); } /* Return a name for the remote host that fits inside utmp_size */ const char * session_get_remote_name_or_ip(struct ssh *ssh, u_int utmp_size, int use_dns) { const char *remote = ""; if (utmp_size > 0) remote = auth_get_canonical_hostname(ssh, use_dns); if (utmp_size == 0 || strlen(remote) > utmp_size) remote = ssh_remote_ipaddr(ssh); return remote; } diff --git a/sftp-client.c b/sftp-client.c index 098b9121a015..2598029f7bd2 100644 --- a/sftp-client.c +++ b/sftp-client.c @@ -1,2978 +1,3009 @@ -/* $OpenBSD: sftp-client.c,v 1.171 2023/04/30 22:54:22 djm Exp $ */ +/* $OpenBSD: sftp-client.c,v 1.174 2023/09/08 06:10:02 djm Exp $ */ /* * Copyright (c) 2001-2004 Damien Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* XXX: memleaks */ /* XXX: signed vs unsigned */ /* XXX: remove all logging, only return status codes */ /* XXX: copy between two remote sites */ #include "includes.h" #include #ifdef HAVE_SYS_STATVFS_H #include #endif #include "openbsd-compat/sys-queue.h" #ifdef HAVE_SYS_STAT_H # include #endif #ifdef HAVE_SYS_TIME_H # include #endif #include #include #include #ifdef HAVE_POLL_H #include #else # ifdef HAVE_SYS_POLL_H # include # endif #endif #include #include #include #include #include #include #include #include "xmalloc.h" #include "ssherr.h" #include "sshbuf.h" #include "log.h" #include "atomicio.h" #include "progressmeter.h" #include "misc.h" #include "utf8.h" #include "sftp.h" #include "sftp-common.h" #include "sftp-client.h" extern volatile sig_atomic_t interrupted; extern int showprogress; /* Default size of buffer for up/download (fix sftp.1 scp.1 if changed) */ #define DEFAULT_COPY_BUFLEN 32768 /* Default number of concurrent xfer requests (fix sftp.1 scp.1 if changed) */ #define DEFAULT_NUM_REQUESTS 64 /* Minimum amount of data to read at a time */ #define MIN_READ_SIZE 512 /* Maximum depth to descend in directory trees */ #define MAX_DIR_DEPTH 64 /* Directory separator characters */ #ifdef HAVE_CYGWIN # define SFTP_DIRECTORY_CHARS "/\\" #else /* HAVE_CYGWIN */ # define SFTP_DIRECTORY_CHARS "/" #endif /* HAVE_CYGWIN */ struct sftp_conn { int fd_in; int fd_out; u_int download_buflen; u_int upload_buflen; u_int num_requests; u_int version; u_int msg_id; #define SFTP_EXT_POSIX_RENAME 0x00000001 #define SFTP_EXT_STATVFS 0x00000002 #define SFTP_EXT_FSTATVFS 0x00000004 #define SFTP_EXT_HARDLINK 0x00000008 #define SFTP_EXT_FSYNC 0x00000010 #define SFTP_EXT_LSETSTAT 0x00000020 #define SFTP_EXT_LIMITS 0x00000040 #define SFTP_EXT_PATH_EXPAND 0x00000080 #define SFTP_EXT_COPY_DATA 0x00000100 #define SFTP_EXT_GETUSERSGROUPS_BY_ID 0x00000200 u_int exts; u_int64_t limit_kbps; struct bwlimit bwlimit_in, bwlimit_out; }; /* Tracks in-progress requests during file transfers */ struct request { u_int id; size_t len; u_int64_t offset; TAILQ_ENTRY(request) tq; }; TAILQ_HEAD(requests, request); static u_char * get_handle(struct sftp_conn *conn, u_int expected_id, size_t *len, const char *errfmt, ...) __attribute__((format(printf, 4, 5))); static struct request * request_enqueue(struct requests *requests, u_int id, size_t len, uint64_t offset) { struct request *req; req = xcalloc(1, sizeof(*req)); req->id = id; req->len = len; req->offset = offset; TAILQ_INSERT_TAIL(requests, req, tq); return req; } static struct request * request_find(struct requests *requests, u_int id) { struct request *req; for (req = TAILQ_FIRST(requests); req != NULL && req->id != id; req = TAILQ_NEXT(req, tq)) ; return req; } static int sftpio(void *_bwlimit, size_t amount) { struct bwlimit *bwlimit = (struct bwlimit *)_bwlimit; refresh_progress_meter(0); if (bwlimit != NULL) bandwidth_limit(bwlimit, amount); return 0; } static void send_msg(struct sftp_conn *conn, struct sshbuf *m) { u_char mlen[4]; struct iovec iov[2]; if (sshbuf_len(m) > SFTP_MAX_MSG_LENGTH) fatal("Outbound message too long %zu", sshbuf_len(m)); /* Send length first */ put_u32(mlen, sshbuf_len(m)); iov[0].iov_base = mlen; iov[0].iov_len = sizeof(mlen); iov[1].iov_base = (u_char *)sshbuf_ptr(m); iov[1].iov_len = sshbuf_len(m); if (atomiciov6(writev, conn->fd_out, iov, 2, sftpio, conn->limit_kbps > 0 ? &conn->bwlimit_out : NULL) != sshbuf_len(m) + sizeof(mlen)) fatal("Couldn't send packet: %s", strerror(errno)); sshbuf_reset(m); } static void get_msg_extended(struct sftp_conn *conn, struct sshbuf *m, int initial) { u_int msg_len; u_char *p; int r; sshbuf_reset(m); if ((r = sshbuf_reserve(m, 4, &p)) != 0) fatal_fr(r, "reserve"); if (atomicio6(read, conn->fd_in, p, 4, sftpio, conn->limit_kbps > 0 ? &conn->bwlimit_in : NULL) != 4) { if (errno == EPIPE || errno == ECONNRESET) fatal("Connection closed"); else fatal("Couldn't read packet: %s", strerror(errno)); } if ((r = sshbuf_get_u32(m, &msg_len)) != 0) fatal_fr(r, "sshbuf_get_u32"); if (msg_len > SFTP_MAX_MSG_LENGTH) { do_log2(initial ? SYSLOG_LEVEL_ERROR : SYSLOG_LEVEL_FATAL, "Received message too long %u", msg_len); fatal("Ensure the remote shell produces no output " "for non-interactive sessions."); } if ((r = sshbuf_reserve(m, msg_len, &p)) != 0) fatal_fr(r, "reserve"); if (atomicio6(read, conn->fd_in, p, msg_len, sftpio, conn->limit_kbps > 0 ? &conn->bwlimit_in : NULL) != msg_len) { if (errno == EPIPE) fatal("Connection closed"); else fatal("Read packet: %s", strerror(errno)); } } static void get_msg(struct sftp_conn *conn, struct sshbuf *m) { get_msg_extended(conn, m, 0); } static void send_string_request(struct sftp_conn *conn, u_int id, u_int code, const char *s, u_int len) { struct sshbuf *msg; int r; if ((msg = sshbuf_new()) == NULL) fatal_f("sshbuf_new failed"); if ((r = sshbuf_put_u8(msg, code)) != 0 || (r = sshbuf_put_u32(msg, id)) != 0 || (r = sshbuf_put_string(msg, s, len)) != 0) fatal_fr(r, "compose"); send_msg(conn, msg); debug3("Sent message fd %d T:%u I:%u", conn->fd_out, code, id); sshbuf_free(msg); } static void send_string_attrs_request(struct sftp_conn *conn, u_int id, u_int code, const void *s, u_int len, Attrib *a) { struct sshbuf *msg; int r; if ((msg = sshbuf_new()) == NULL) fatal_f("sshbuf_new failed"); if ((r = sshbuf_put_u8(msg, code)) != 0 || (r = sshbuf_put_u32(msg, id)) != 0 || (r = sshbuf_put_string(msg, s, len)) != 0 || (r = encode_attrib(msg, a)) != 0) fatal_fr(r, "compose"); send_msg(conn, msg); debug3("Sent message fd %d T:%u I:%u F:0x%04x M:%05o", conn->fd_out, code, id, a->flags, a->perm); sshbuf_free(msg); } static u_int get_status(struct sftp_conn *conn, u_int expected_id) { struct sshbuf *msg; u_char type; u_int id, status; int r; if ((msg = sshbuf_new()) == NULL) fatal_f("sshbuf_new failed"); get_msg(conn, msg); if ((r = sshbuf_get_u8(msg, &type)) != 0 || (r = sshbuf_get_u32(msg, &id)) != 0) fatal_fr(r, "compose"); if (id != expected_id) fatal("ID mismatch (%u != %u)", id, expected_id); if (type != SSH2_FXP_STATUS) fatal("Expected SSH2_FXP_STATUS(%u) packet, got %u", SSH2_FXP_STATUS, type); if ((r = sshbuf_get_u32(msg, &status)) != 0) fatal_fr(r, "parse"); sshbuf_free(msg); debug3("SSH2_FXP_STATUS %u", status); return status; } static u_char * get_handle(struct sftp_conn *conn, u_int expected_id, size_t *len, const char *errfmt, ...) { struct sshbuf *msg; u_int id, status; u_char type; u_char *handle; char errmsg[256]; va_list args; int r; va_start(args, errfmt); if (errfmt != NULL) vsnprintf(errmsg, sizeof(errmsg), errfmt, args); va_end(args); if ((msg = sshbuf_new()) == NULL) fatal_f("sshbuf_new failed"); get_msg(conn, msg); if ((r = sshbuf_get_u8(msg, &type)) != 0 || (r = sshbuf_get_u32(msg, &id)) != 0) fatal_fr(r, "parse"); if (id != expected_id) fatal("%s: ID mismatch (%u != %u)", errfmt == NULL ? __func__ : errmsg, id, expected_id); if (type == SSH2_FXP_STATUS) { if ((r = sshbuf_get_u32(msg, &status)) != 0) fatal_fr(r, "parse status"); if (errfmt != NULL) error("%s: %s", errmsg, fx2txt(status)); sshbuf_free(msg); return(NULL); } else if (type != SSH2_FXP_HANDLE) fatal("%s: Expected SSH2_FXP_HANDLE(%u) packet, got %u", errfmt == NULL ? __func__ : errmsg, SSH2_FXP_HANDLE, type); if ((r = sshbuf_get_string(msg, &handle, len)) != 0) fatal_fr(r, "parse handle"); sshbuf_free(msg); return handle; } -/* XXX returning &static is error-prone. Refactor to fill *Attrib argument */ -static Attrib * -get_decode_stat(struct sftp_conn *conn, u_int expected_id, int quiet) +static int +get_decode_stat(struct sftp_conn *conn, u_int expected_id, int quiet, Attrib *a) { struct sshbuf *msg; u_int id; u_char type; int r; - static Attrib a; + Attrib attr; + if (a != NULL) + memset(a, '\0', sizeof(*a)); if ((msg = sshbuf_new()) == NULL) fatal_f("sshbuf_new failed"); get_msg(conn, msg); if ((r = sshbuf_get_u8(msg, &type)) != 0 || (r = sshbuf_get_u32(msg, &id)) != 0) fatal_fr(r, "parse"); if (id != expected_id) fatal("ID mismatch (%u != %u)", id, expected_id); if (type == SSH2_FXP_STATUS) { u_int status; if ((r = sshbuf_get_u32(msg, &status)) != 0) fatal_fr(r, "parse status"); if (quiet) debug("stat remote: %s", fx2txt(status)); else error("stat remote: %s", fx2txt(status)); sshbuf_free(msg); - return(NULL); + return -1; } else if (type != SSH2_FXP_ATTRS) { fatal("Expected SSH2_FXP_ATTRS(%u) packet, got %u", SSH2_FXP_ATTRS, type); } - if ((r = decode_attrib(msg, &a)) != 0) { + if ((r = decode_attrib(msg, &attr)) != 0) { error_fr(r, "decode_attrib"); sshbuf_free(msg); - return NULL; + return -1; } + /* success */ + if (a != NULL) + *a = attr; debug3("Received stat reply T:%u I:%u F:0x%04x M:%05o", - type, id, a.flags, a.perm); + type, id, attr.flags, attr.perm); sshbuf_free(msg); - return &a; + return 0; } static int get_decode_statvfs(struct sftp_conn *conn, struct sftp_statvfs *st, u_int expected_id, int quiet) { struct sshbuf *msg; u_char type; u_int id; u_int64_t flag; int r; if ((msg = sshbuf_new()) == NULL) fatal_f("sshbuf_new failed"); get_msg(conn, msg); if ((r = sshbuf_get_u8(msg, &type)) != 0 || (r = sshbuf_get_u32(msg, &id)) != 0) fatal_fr(r, "parse"); debug3("Received statvfs reply T:%u I:%u", type, id); if (id != expected_id) fatal("ID mismatch (%u != %u)", id, expected_id); if (type == SSH2_FXP_STATUS) { u_int status; if ((r = sshbuf_get_u32(msg, &status)) != 0) fatal_fr(r, "parse status"); if (quiet) debug("remote statvfs: %s", fx2txt(status)); else error("remote statvfs: %s", fx2txt(status)); sshbuf_free(msg); return -1; } else if (type != SSH2_FXP_EXTENDED_REPLY) { fatal("Expected SSH2_FXP_EXTENDED_REPLY(%u) packet, got %u", SSH2_FXP_EXTENDED_REPLY, type); } memset(st, 0, sizeof(*st)); if ((r = sshbuf_get_u64(msg, &st->f_bsize)) != 0 || (r = sshbuf_get_u64(msg, &st->f_frsize)) != 0 || (r = sshbuf_get_u64(msg, &st->f_blocks)) != 0 || (r = sshbuf_get_u64(msg, &st->f_bfree)) != 0 || (r = sshbuf_get_u64(msg, &st->f_bavail)) != 0 || (r = sshbuf_get_u64(msg, &st->f_files)) != 0 || (r = sshbuf_get_u64(msg, &st->f_ffree)) != 0 || (r = sshbuf_get_u64(msg, &st->f_favail)) != 0 || (r = sshbuf_get_u64(msg, &st->f_fsid)) != 0 || (r = sshbuf_get_u64(msg, &flag)) != 0 || (r = sshbuf_get_u64(msg, &st->f_namemax)) != 0) fatal_fr(r, "parse statvfs"); st->f_flag = (flag & SSH2_FXE_STATVFS_ST_RDONLY) ? ST_RDONLY : 0; st->f_flag |= (flag & SSH2_FXE_STATVFS_ST_NOSUID) ? ST_NOSUID : 0; sshbuf_free(msg); return 0; } struct sftp_conn * -do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests, +sftp_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests, u_int64_t limit_kbps) { u_char type; struct sshbuf *msg; struct sftp_conn *ret; int r; ret = xcalloc(1, sizeof(*ret)); ret->msg_id = 1; ret->fd_in = fd_in; ret->fd_out = fd_out; ret->download_buflen = ret->upload_buflen = transfer_buflen ? transfer_buflen : DEFAULT_COPY_BUFLEN; ret->num_requests = num_requests ? num_requests : DEFAULT_NUM_REQUESTS; ret->exts = 0; ret->limit_kbps = 0; if ((msg = sshbuf_new()) == NULL) fatal_f("sshbuf_new failed"); if ((r = sshbuf_put_u8(msg, SSH2_FXP_INIT)) != 0 || (r = sshbuf_put_u32(msg, SSH2_FILEXFER_VERSION)) != 0) fatal_fr(r, "parse"); send_msg(ret, msg); get_msg_extended(ret, msg, 1); /* Expecting a VERSION reply */ if ((r = sshbuf_get_u8(msg, &type)) != 0) fatal_fr(r, "parse type"); if (type != SSH2_FXP_VERSION) { error("Invalid packet back from SSH2_FXP_INIT (type %u)", type); sshbuf_free(msg); free(ret); return(NULL); } if ((r = sshbuf_get_u32(msg, &ret->version)) != 0) fatal_fr(r, "parse version"); debug2("Remote version: %u", ret->version); /* Check for extensions */ while (sshbuf_len(msg) > 0) { char *name; u_char *value; size_t vlen; int known = 0; if ((r = sshbuf_get_cstring(msg, &name, NULL)) != 0 || (r = sshbuf_get_string(msg, &value, &vlen)) != 0) fatal_fr(r, "parse extension"); if (strcmp(name, "posix-rename@openssh.com") == 0 && strcmp((char *)value, "1") == 0) { ret->exts |= SFTP_EXT_POSIX_RENAME; known = 1; } else if (strcmp(name, "statvfs@openssh.com") == 0 && strcmp((char *)value, "2") == 0) { ret->exts |= SFTP_EXT_STATVFS; known = 1; } else if (strcmp(name, "fstatvfs@openssh.com") == 0 && strcmp((char *)value, "2") == 0) { ret->exts |= SFTP_EXT_FSTATVFS; known = 1; } else if (strcmp(name, "hardlink@openssh.com") == 0 && strcmp((char *)value, "1") == 0) { ret->exts |= SFTP_EXT_HARDLINK; known = 1; } else if (strcmp(name, "fsync@openssh.com") == 0 && strcmp((char *)value, "1") == 0) { ret->exts |= SFTP_EXT_FSYNC; known = 1; } else if (strcmp(name, "lsetstat@openssh.com") == 0 && strcmp((char *)value, "1") == 0) { ret->exts |= SFTP_EXT_LSETSTAT; known = 1; } else if (strcmp(name, "limits@openssh.com") == 0 && strcmp((char *)value, "1") == 0) { ret->exts |= SFTP_EXT_LIMITS; known = 1; } else if (strcmp(name, "expand-path@openssh.com") == 0 && strcmp((char *)value, "1") == 0) { ret->exts |= SFTP_EXT_PATH_EXPAND; known = 1; } else if (strcmp(name, "copy-data") == 0 && strcmp((char *)value, "1") == 0) { ret->exts |= SFTP_EXT_COPY_DATA; known = 1; } else if (strcmp(name, "users-groups-by-id@openssh.com") == 0 && strcmp((char *)value, "1") == 0) { ret->exts |= SFTP_EXT_GETUSERSGROUPS_BY_ID; known = 1; } if (known) { debug2("Server supports extension \"%s\" revision %s", name, value); } else { debug2("Unrecognised server extension \"%s\"", name); } free(name); free(value); } sshbuf_free(msg); /* Query the server for its limits */ if (ret->exts & SFTP_EXT_LIMITS) { struct sftp_limits limits; - if (do_limits(ret, &limits) != 0) + if (sftp_get_limits(ret, &limits) != 0) fatal_f("limits failed"); /* If the caller did not specify, find a good value */ if (transfer_buflen == 0) { ret->download_buflen = MINIMUM(limits.read_length, SFTP_MAX_MSG_LENGTH - 1024); ret->upload_buflen = MINIMUM(limits.write_length, SFTP_MAX_MSG_LENGTH - 1024); ret->download_buflen = MAXIMUM(ret->download_buflen, 64); ret->upload_buflen = MAXIMUM(ret->upload_buflen, 64); debug3("server upload/download buffer sizes " "%llu / %llu; using %u / %u", (unsigned long long)limits.write_length, (unsigned long long)limits.read_length, ret->upload_buflen, ret->download_buflen); } /* Use the server limit to scale down our value only */ if (num_requests == 0 && limits.open_handles) { ret->num_requests = MINIMUM(DEFAULT_NUM_REQUESTS, limits.open_handles); if (ret->num_requests == 0) ret->num_requests = 1; debug3("server handle limit %llu; using %u", (unsigned long long)limits.open_handles, ret->num_requests); } } /* Some filexfer v.0 servers don't support large packets */ if (ret->version == 0) { ret->download_buflen = MINIMUM(ret->download_buflen, 20480); ret->upload_buflen = MINIMUM(ret->upload_buflen, 20480); } ret->limit_kbps = limit_kbps; if (ret->limit_kbps > 0) { bandwidth_limit_init(&ret->bwlimit_in, ret->limit_kbps, ret->download_buflen); bandwidth_limit_init(&ret->bwlimit_out, ret->limit_kbps, ret->upload_buflen); } return ret; } u_int sftp_proto_version(struct sftp_conn *conn) { return conn->version; } int -do_limits(struct sftp_conn *conn, struct sftp_limits *limits) +sftp_get_limits(struct sftp_conn *conn, struct sftp_limits *limits) { u_int id, msg_id; u_char type; struct sshbuf *msg; int r; if ((conn->exts & SFTP_EXT_LIMITS) == 0) { error("Server does not support limits@openssh.com extension"); return -1; } if ((msg = sshbuf_new()) == NULL) fatal_f("sshbuf_new failed"); id = conn->msg_id++; if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 || (r = sshbuf_put_u32(msg, id)) != 0 || (r = sshbuf_put_cstring(msg, "limits@openssh.com")) != 0) fatal_fr(r, "compose"); send_msg(conn, msg); debug3("Sent message limits@openssh.com I:%u", id); get_msg(conn, msg); if ((r = sshbuf_get_u8(msg, &type)) != 0 || (r = sshbuf_get_u32(msg, &msg_id)) != 0) fatal_fr(r, "parse"); debug3("Received limits reply T:%u I:%u", type, msg_id); if (id != msg_id) fatal("ID mismatch (%u != %u)", msg_id, id); if (type != SSH2_FXP_EXTENDED_REPLY) { debug_f("expected SSH2_FXP_EXTENDED_REPLY(%u) packet, got %u", SSH2_FXP_EXTENDED_REPLY, type); /* Disable the limits extension */ conn->exts &= ~SFTP_EXT_LIMITS; sshbuf_free(msg); return 0; } memset(limits, 0, sizeof(*limits)); if ((r = sshbuf_get_u64(msg, &limits->packet_length)) != 0 || (r = sshbuf_get_u64(msg, &limits->read_length)) != 0 || (r = sshbuf_get_u64(msg, &limits->write_length)) != 0 || (r = sshbuf_get_u64(msg, &limits->open_handles)) != 0) fatal_fr(r, "parse limits"); sshbuf_free(msg); return 0; } int -do_close(struct sftp_conn *conn, const u_char *handle, u_int handle_len) +sftp_close(struct sftp_conn *conn, const u_char *handle, u_int handle_len) { u_int id, status; struct sshbuf *msg; int r; if ((msg = sshbuf_new()) == NULL) fatal_f("sshbuf_new failed"); id = conn->msg_id++; if ((r = sshbuf_put_u8(msg, SSH2_FXP_CLOSE)) != 0 || (r = sshbuf_put_u32(msg, id)) != 0 || (r = sshbuf_put_string(msg, handle, handle_len)) != 0) fatal_fr(r, "parse"); send_msg(conn, msg); debug3("Sent message SSH2_FXP_CLOSE I:%u", id); status = get_status(conn, id); if (status != SSH2_FX_OK) error("close remote: %s", fx2txt(status)); sshbuf_free(msg); return status == SSH2_FX_OK ? 0 : -1; } static int -do_lsreaddir(struct sftp_conn *conn, const char *path, int print_flag, +sftp_lsreaddir(struct sftp_conn *conn, const char *path, int print_flag, SFTP_DIRENT ***dir) { struct sshbuf *msg; u_int count, id, i, expected_id, ents = 0; size_t handle_len; u_char type, *handle; int status = SSH2_FX_FAILURE; int r; if (dir) *dir = NULL; id = conn->msg_id++; if ((msg = sshbuf_new()) == NULL) fatal_f("sshbuf_new failed"); if ((r = sshbuf_put_u8(msg, SSH2_FXP_OPENDIR)) != 0 || (r = sshbuf_put_u32(msg, id)) != 0 || (r = sshbuf_put_cstring(msg, path)) != 0) fatal_fr(r, "compose OPENDIR"); send_msg(conn, msg); handle = get_handle(conn, id, &handle_len, "remote readdir(\"%s\")", path); if (handle == NULL) { sshbuf_free(msg); return -1; } if (dir) { ents = 0; *dir = xcalloc(1, sizeof(**dir)); (*dir)[0] = NULL; } for (; !interrupted;) { id = expected_id = conn->msg_id++; debug3("Sending SSH2_FXP_READDIR I:%u", id); sshbuf_reset(msg); if ((r = sshbuf_put_u8(msg, SSH2_FXP_READDIR)) != 0 || (r = sshbuf_put_u32(msg, id)) != 0 || (r = sshbuf_put_string(msg, handle, handle_len)) != 0) fatal_fr(r, "compose READDIR"); send_msg(conn, msg); sshbuf_reset(msg); get_msg(conn, msg); if ((r = sshbuf_get_u8(msg, &type)) != 0 || (r = sshbuf_get_u32(msg, &id)) != 0) fatal_fr(r, "parse"); debug3("Received reply T:%u I:%u", type, id); if (id != expected_id) fatal("ID mismatch (%u != %u)", id, expected_id); if (type == SSH2_FXP_STATUS) { u_int rstatus; if ((r = sshbuf_get_u32(msg, &rstatus)) != 0) fatal_fr(r, "parse status"); debug3("Received SSH2_FXP_STATUS %d", rstatus); if (rstatus == SSH2_FX_EOF) break; error("Couldn't read directory: %s", fx2txt(rstatus)); goto out; } else if (type != SSH2_FXP_NAME) fatal("Expected SSH2_FXP_NAME(%u) packet, got %u", SSH2_FXP_NAME, type); if ((r = sshbuf_get_u32(msg, &count)) != 0) fatal_fr(r, "parse count"); if (count > SSHBUF_SIZE_MAX) fatal_f("nonsensical number of entries"); if (count == 0) break; debug3("Received %d SSH2_FXP_NAME responses", count); for (i = 0; i < count; i++) { char *filename, *longname; Attrib a; if ((r = sshbuf_get_cstring(msg, &filename, NULL)) != 0 || (r = sshbuf_get_cstring(msg, &longname, NULL)) != 0) fatal_fr(r, "parse filenames"); if ((r = decode_attrib(msg, &a)) != 0) { error_fr(r, "couldn't decode attrib"); free(filename); free(longname); goto out; } if (print_flag) mprintf("%s\n", longname); /* * Directory entries should never contain '/' * These can be used to attack recursive ops * (e.g. send '../../../../etc/passwd') */ if (strpbrk(filename, SFTP_DIRECTORY_CHARS) != NULL) { error("Server sent suspect path \"%s\" " "during readdir of \"%s\"", filename, path); } else if (dir) { *dir = xreallocarray(*dir, ents + 2, sizeof(**dir)); (*dir)[ents] = xcalloc(1, sizeof(***dir)); (*dir)[ents]->filename = xstrdup(filename); (*dir)[ents]->longname = xstrdup(longname); memcpy(&(*dir)[ents]->a, &a, sizeof(a)); (*dir)[++ents] = NULL; } free(filename); free(longname); } } status = 0; out: sshbuf_free(msg); - do_close(conn, handle, handle_len); + sftp_close(conn, handle, handle_len); free(handle); if (status != 0 && dir != NULL) { /* Don't return results on error */ - free_sftp_dirents(*dir); + sftp_free_dirents(*dir); *dir = NULL; } else if (interrupted && dir != NULL && *dir != NULL) { /* Don't return partial matches on interrupt */ - free_sftp_dirents(*dir); + sftp_free_dirents(*dir); *dir = xcalloc(1, sizeof(**dir)); **dir = NULL; } return status == SSH2_FX_OK ? 0 : -1; } int -do_readdir(struct sftp_conn *conn, const char *path, SFTP_DIRENT ***dir) +sftp_readdir(struct sftp_conn *conn, const char *path, SFTP_DIRENT ***dir) { - return(do_lsreaddir(conn, path, 0, dir)); + return sftp_lsreaddir(conn, path, 0, dir); } -void free_sftp_dirents(SFTP_DIRENT **s) +void sftp_free_dirents(SFTP_DIRENT **s) { int i; if (s == NULL) return; for (i = 0; s[i]; i++) { free(s[i]->filename); free(s[i]->longname); free(s[i]); } free(s); } int -do_rm(struct sftp_conn *conn, const char *path) +sftp_rm(struct sftp_conn *conn, const char *path) { u_int status, id; debug2("Sending SSH2_FXP_REMOVE \"%s\"", path); id = conn->msg_id++; send_string_request(conn, id, SSH2_FXP_REMOVE, path, strlen(path)); status = get_status(conn, id); if (status != SSH2_FX_OK) error("remote delete %s: %s", path, fx2txt(status)); return status == SSH2_FX_OK ? 0 : -1; } int -do_mkdir(struct sftp_conn *conn, const char *path, Attrib *a, int print_flag) +sftp_mkdir(struct sftp_conn *conn, const char *path, Attrib *a, int print_flag) { u_int status, id; debug2("Sending SSH2_FXP_MKDIR \"%s\"", path); id = conn->msg_id++; send_string_attrs_request(conn, id, SSH2_FXP_MKDIR, path, strlen(path), a); status = get_status(conn, id); if (status != SSH2_FX_OK && print_flag) error("remote mkdir \"%s\": %s", path, fx2txt(status)); return status == SSH2_FX_OK ? 0 : -1; } int -do_rmdir(struct sftp_conn *conn, const char *path) +sftp_rmdir(struct sftp_conn *conn, const char *path) { u_int status, id; debug2("Sending SSH2_FXP_RMDIR \"%s\"", path); id = conn->msg_id++; send_string_request(conn, id, SSH2_FXP_RMDIR, path, strlen(path)); status = get_status(conn, id); if (status != SSH2_FX_OK) error("remote rmdir \"%s\": %s", path, fx2txt(status)); return status == SSH2_FX_OK ? 0 : -1; } -Attrib * -do_stat(struct sftp_conn *conn, const char *path, int quiet) +int +sftp_stat(struct sftp_conn *conn, const char *path, int quiet, Attrib *a) { u_int id; debug2("Sending SSH2_FXP_STAT \"%s\"", path); id = conn->msg_id++; send_string_request(conn, id, conn->version == 0 ? SSH2_FXP_STAT_VERSION_0 : SSH2_FXP_STAT, path, strlen(path)); - return(get_decode_stat(conn, id, quiet)); + return get_decode_stat(conn, id, quiet, a); } -Attrib * -do_lstat(struct sftp_conn *conn, const char *path, int quiet) +int +sftp_lstat(struct sftp_conn *conn, const char *path, int quiet, Attrib *a) { u_int id; if (conn->version == 0) { - if (quiet) - debug("Server version does not support lstat operation"); - else - logit("Server version does not support lstat operation"); - return(do_stat(conn, path, quiet)); + do_log2(quiet ? SYSLOG_LEVEL_DEBUG1 : SYSLOG_LEVEL_INFO, + "Server version does not support lstat operation"); + return sftp_stat(conn, path, quiet, a); } id = conn->msg_id++; send_string_request(conn, id, SSH2_FXP_LSTAT, path, strlen(path)); - return(get_decode_stat(conn, id, quiet)); + return get_decode_stat(conn, id, quiet, a); } #ifdef notyet -Attrib * -do_fstat(struct sftp_conn *conn, const u_char *handle, u_int handle_len, - int quiet) +int +sftp_fstat(struct sftp_conn *conn, const u_char *handle, u_int handle_len, + int quiet, Attrib *a) { u_int id; debug2("Sending SSH2_FXP_FSTAT \"%s\""); id = conn->msg_id++; send_string_request(conn, id, SSH2_FXP_FSTAT, handle, handle_len); - return(get_decode_stat(conn, id, quiet)); + return get_decode_stat(conn, id, quiet, a); } #endif int -do_setstat(struct sftp_conn *conn, const char *path, Attrib *a) +sftp_setstat(struct sftp_conn *conn, const char *path, Attrib *a) { u_int status, id; debug2("Sending SSH2_FXP_SETSTAT \"%s\"", path); id = conn->msg_id++; send_string_attrs_request(conn, id, SSH2_FXP_SETSTAT, path, strlen(path), a); status = get_status(conn, id); if (status != SSH2_FX_OK) error("remote setstat \"%s\": %s", path, fx2txt(status)); return status == SSH2_FX_OK ? 0 : -1; } int -do_fsetstat(struct sftp_conn *conn, const u_char *handle, u_int handle_len, +sftp_fsetstat(struct sftp_conn *conn, const u_char *handle, u_int handle_len, Attrib *a) { u_int status, id; debug2("Sending SSH2_FXP_FSETSTAT"); id = conn->msg_id++; send_string_attrs_request(conn, id, SSH2_FXP_FSETSTAT, handle, handle_len, a); status = get_status(conn, id); if (status != SSH2_FX_OK) error("remote fsetstat: %s", fx2txt(status)); return status == SSH2_FX_OK ? 0 : -1; } /* Implements both the realpath and expand-path operations */ static char * -do_realpath_expand(struct sftp_conn *conn, const char *path, int expand) +sftp_realpath_expand(struct sftp_conn *conn, const char *path, int expand) { struct sshbuf *msg; u_int expected_id, count, id; char *filename, *longname; Attrib a; u_char type; int r; const char *what = "SSH2_FXP_REALPATH"; if (expand) what = "expand-path@openssh.com"; if ((msg = sshbuf_new()) == NULL) fatal_f("sshbuf_new failed"); expected_id = id = conn->msg_id++; if (expand) { debug2("Sending SSH2_FXP_EXTENDED(expand-path@openssh.com) " "\"%s\"", path); if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 || (r = sshbuf_put_u32(msg, id)) != 0 || (r = sshbuf_put_cstring(msg, "expand-path@openssh.com")) != 0 || (r = sshbuf_put_cstring(msg, path)) != 0) fatal_fr(r, "compose %s", what); send_msg(conn, msg); } else { debug2("Sending SSH2_FXP_REALPATH \"%s\"", path); send_string_request(conn, id, SSH2_FXP_REALPATH, path, strlen(path)); } get_msg(conn, msg); if ((r = sshbuf_get_u8(msg, &type)) != 0 || (r = sshbuf_get_u32(msg, &id)) != 0) fatal_fr(r, "parse"); if (id != expected_id) fatal("ID mismatch (%u != %u)", id, expected_id); if (type == SSH2_FXP_STATUS) { u_int status; char *errmsg; if ((r = sshbuf_get_u32(msg, &status)) != 0 || (r = sshbuf_get_cstring(msg, &errmsg, NULL)) != 0) fatal_fr(r, "parse status"); error("%s %s: %s", expand ? "expand" : "realpath", path, *errmsg == '\0' ? fx2txt(status) : errmsg); free(errmsg); sshbuf_free(msg); return NULL; } else if (type != SSH2_FXP_NAME) fatal("Expected SSH2_FXP_NAME(%u) packet, got %u", SSH2_FXP_NAME, type); if ((r = sshbuf_get_u32(msg, &count)) != 0) fatal_fr(r, "parse count"); if (count != 1) fatal("Got multiple names (%d) from %s", count, what); if ((r = sshbuf_get_cstring(msg, &filename, NULL)) != 0 || (r = sshbuf_get_cstring(msg, &longname, NULL)) != 0 || (r = decode_attrib(msg, &a)) != 0) fatal_fr(r, "parse filename/attrib"); debug3("%s %s -> %s", what, path, filename); free(longname); sshbuf_free(msg); return(filename); } char * -do_realpath(struct sftp_conn *conn, const char *path) +sftp_realpath(struct sftp_conn *conn, const char *path) { - return do_realpath_expand(conn, path, 0); + return sftp_realpath_expand(conn, path, 0); } int -can_expand_path(struct sftp_conn *conn) +sftp_can_expand_path(struct sftp_conn *conn) { return (conn->exts & SFTP_EXT_PATH_EXPAND) != 0; } char * -do_expand_path(struct sftp_conn *conn, const char *path) +sftp_expand_path(struct sftp_conn *conn, const char *path) { - if (!can_expand_path(conn)) { + if (!sftp_can_expand_path(conn)) { debug3_f("no server support, fallback to realpath"); - return do_realpath_expand(conn, path, 0); + return sftp_realpath_expand(conn, path, 0); } - return do_realpath_expand(conn, path, 1); + return sftp_realpath_expand(conn, path, 1); } int -do_copy(struct sftp_conn *conn, const char *oldpath, const char *newpath) +sftp_copy(struct sftp_conn *conn, const char *oldpath, const char *newpath) { - Attrib junk, *a; + Attrib junk, attr; struct sshbuf *msg; u_char *old_handle, *new_handle; u_int mode, status, id; size_t old_handle_len, new_handle_len; int r; /* Return if the extension is not supported */ if ((conn->exts & SFTP_EXT_COPY_DATA) == 0) { error("Server does not support copy-data extension"); return -1; } /* Make sure the file exists, and we can copy its perms */ - if ((a = do_stat(conn, oldpath, 0)) == NULL) + if (sftp_stat(conn, oldpath, 0, &attr) != 0) return -1; /* Do not preserve set[ug]id here, as we do not preserve ownership */ - if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { - mode = a->perm & 0777; + if (attr.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { + mode = attr.perm & 0777; - if (!S_ISREG(a->perm)) { + if (!S_ISREG(attr.perm)) { error("Cannot copy non-regular file: %s", oldpath); return -1; } } else { /* NB: The user's umask will apply to this */ mode = 0666; } /* Set up the new perms for the new file */ - attrib_clear(a); - a->perm = mode; - a->flags |= SSH2_FILEXFER_ATTR_PERMISSIONS; + attrib_clear(&attr); + attr.perm = mode; + attr.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS; if ((msg = sshbuf_new()) == NULL) fatal("%s: sshbuf_new failed", __func__); attrib_clear(&junk); /* Send empty attributes */ /* Open the old file for reading */ id = conn->msg_id++; if ((r = sshbuf_put_u8(msg, SSH2_FXP_OPEN)) != 0 || (r = sshbuf_put_u32(msg, id)) != 0 || (r = sshbuf_put_cstring(msg, oldpath)) != 0 || (r = sshbuf_put_u32(msg, SSH2_FXF_READ)) != 0 || (r = encode_attrib(msg, &junk)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); send_msg(conn, msg); debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, oldpath); sshbuf_reset(msg); old_handle = get_handle(conn, id, &old_handle_len, "remote open(\"%s\")", oldpath); if (old_handle == NULL) { sshbuf_free(msg); return -1; } /* Open the new file for writing */ id = conn->msg_id++; if ((r = sshbuf_put_u8(msg, SSH2_FXP_OPEN)) != 0 || (r = sshbuf_put_u32(msg, id)) != 0 || (r = sshbuf_put_cstring(msg, newpath)) != 0 || (r = sshbuf_put_u32(msg, SSH2_FXF_WRITE|SSH2_FXF_CREAT| SSH2_FXF_TRUNC)) != 0 || - (r = encode_attrib(msg, a)) != 0) + (r = encode_attrib(msg, &attr)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); send_msg(conn, msg); debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, newpath); sshbuf_reset(msg); new_handle = get_handle(conn, id, &new_handle_len, "remote open(\"%s\")", newpath); if (new_handle == NULL) { sshbuf_free(msg); free(old_handle); return -1; } /* Copy the file data */ id = conn->msg_id++; if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 || (r = sshbuf_put_u32(msg, id)) != 0 || (r = sshbuf_put_cstring(msg, "copy-data")) != 0 || (r = sshbuf_put_string(msg, old_handle, old_handle_len)) != 0 || (r = sshbuf_put_u64(msg, 0)) != 0 || (r = sshbuf_put_u64(msg, 0)) != 0 || (r = sshbuf_put_string(msg, new_handle, new_handle_len)) != 0 || (r = sshbuf_put_u64(msg, 0)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); send_msg(conn, msg); debug3("Sent message copy-data \"%s\" 0 0 -> \"%s\" 0", oldpath, newpath); status = get_status(conn, id); if (status != SSH2_FX_OK) error("Couldn't copy file \"%s\" to \"%s\": %s", oldpath, newpath, fx2txt(status)); /* Clean up everything */ sshbuf_free(msg); - do_close(conn, old_handle, old_handle_len); - do_close(conn, new_handle, new_handle_len); + sftp_close(conn, old_handle, old_handle_len); + sftp_close(conn, new_handle, new_handle_len); free(old_handle); free(new_handle); return status == SSH2_FX_OK ? 0 : -1; } int -do_rename(struct sftp_conn *conn, const char *oldpath, const char *newpath, +sftp_rename(struct sftp_conn *conn, const char *oldpath, const char *newpath, int force_legacy) { struct sshbuf *msg; u_int status, id; int r, use_ext = (conn->exts & SFTP_EXT_POSIX_RENAME) && !force_legacy; if ((msg = sshbuf_new()) == NULL) fatal_f("sshbuf_new failed"); /* Send rename request */ id = conn->msg_id++; if (use_ext) { debug2("Sending SSH2_FXP_EXTENDED(posix-rename@openssh.com) " "\"%s\" to \"%s\"", oldpath, newpath); if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 || (r = sshbuf_put_u32(msg, id)) != 0 || (r = sshbuf_put_cstring(msg, "posix-rename@openssh.com")) != 0) fatal_fr(r, "compose posix-rename"); } else { debug2("Sending SSH2_FXP_RENAME \"%s\" to \"%s\"", oldpath, newpath); if ((r = sshbuf_put_u8(msg, SSH2_FXP_RENAME)) != 0 || (r = sshbuf_put_u32(msg, id)) != 0) fatal_fr(r, "compose rename"); } if ((r = sshbuf_put_cstring(msg, oldpath)) != 0 || (r = sshbuf_put_cstring(msg, newpath)) != 0) fatal_fr(r, "compose paths"); send_msg(conn, msg); debug3("Sent message %s \"%s\" -> \"%s\"", use_ext ? "posix-rename@openssh.com" : "SSH2_FXP_RENAME", oldpath, newpath); sshbuf_free(msg); status = get_status(conn, id); if (status != SSH2_FX_OK) error("remote rename \"%s\" to \"%s\": %s", oldpath, newpath, fx2txt(status)); return status == SSH2_FX_OK ? 0 : -1; } int -do_hardlink(struct sftp_conn *conn, const char *oldpath, const char *newpath) +sftp_hardlink(struct sftp_conn *conn, const char *oldpath, const char *newpath) { struct sshbuf *msg; u_int status, id; int r; if ((conn->exts & SFTP_EXT_HARDLINK) == 0) { error("Server does not support hardlink@openssh.com extension"); return -1; } debug2("Sending SSH2_FXP_EXTENDED(hardlink@openssh.com) " "\"%s\" to \"%s\"", oldpath, newpath); if ((msg = sshbuf_new()) == NULL) fatal_f("sshbuf_new failed"); /* Send link request */ id = conn->msg_id++; if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 || (r = sshbuf_put_u32(msg, id)) != 0 || (r = sshbuf_put_cstring(msg, "hardlink@openssh.com")) != 0 || (r = sshbuf_put_cstring(msg, oldpath)) != 0 || (r = sshbuf_put_cstring(msg, newpath)) != 0) fatal_fr(r, "compose"); send_msg(conn, msg); debug3("Sent message hardlink@openssh.com \"%s\" -> \"%s\"", oldpath, newpath); sshbuf_free(msg); status = get_status(conn, id); if (status != SSH2_FX_OK) error("remote link \"%s\" to \"%s\": %s", oldpath, newpath, fx2txt(status)); return status == SSH2_FX_OK ? 0 : -1; } int -do_symlink(struct sftp_conn *conn, const char *oldpath, const char *newpath) +sftp_symlink(struct sftp_conn *conn, const char *oldpath, const char *newpath) { struct sshbuf *msg; u_int status, id; int r; if (conn->version < 3) { error("This server does not support the symlink operation"); return(SSH2_FX_OP_UNSUPPORTED); } debug2("Sending SSH2_FXP_SYMLINK \"%s\" to \"%s\"", oldpath, newpath); if ((msg = sshbuf_new()) == NULL) fatal_f("sshbuf_new failed"); /* Send symlink request */ id = conn->msg_id++; if ((r = sshbuf_put_u8(msg, SSH2_FXP_SYMLINK)) != 0 || (r = sshbuf_put_u32(msg, id)) != 0 || (r = sshbuf_put_cstring(msg, oldpath)) != 0 || (r = sshbuf_put_cstring(msg, newpath)) != 0) fatal_fr(r, "compose"); send_msg(conn, msg); debug3("Sent message SSH2_FXP_SYMLINK \"%s\" -> \"%s\"", oldpath, newpath); sshbuf_free(msg); status = get_status(conn, id); if (status != SSH2_FX_OK) error("remote symlink file \"%s\" to \"%s\": %s", oldpath, newpath, fx2txt(status)); return status == SSH2_FX_OK ? 0 : -1; } int -do_fsync(struct sftp_conn *conn, u_char *handle, u_int handle_len) +sftp_fsync(struct sftp_conn *conn, u_char *handle, u_int handle_len) { struct sshbuf *msg; u_int status, id; int r; /* Silently return if the extension is not supported */ if ((conn->exts & SFTP_EXT_FSYNC) == 0) return -1; debug2("Sending SSH2_FXP_EXTENDED(fsync@openssh.com)"); /* Send fsync request */ if ((msg = sshbuf_new()) == NULL) fatal_f("sshbuf_new failed"); id = conn->msg_id++; if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 || (r = sshbuf_put_u32(msg, id)) != 0 || (r = sshbuf_put_cstring(msg, "fsync@openssh.com")) != 0 || (r = sshbuf_put_string(msg, handle, handle_len)) != 0) fatal_fr(r, "compose"); send_msg(conn, msg); debug3("Sent message fsync@openssh.com I:%u", id); sshbuf_free(msg); status = get_status(conn, id); if (status != SSH2_FX_OK) error("remote fsync: %s", fx2txt(status)); return status == SSH2_FX_OK ? 0 : -1; } #ifdef notyet char * -do_readlink(struct sftp_conn *conn, const char *path) +sftp_readlink(struct sftp_conn *conn, const char *path) { struct sshbuf *msg; u_int expected_id, count, id; char *filename, *longname; Attrib a; u_char type; int r; debug2("Sending SSH2_FXP_READLINK \"%s\"", path); expected_id = id = conn->msg_id++; send_string_request(conn, id, SSH2_FXP_READLINK, path, strlen(path)); if ((msg = sshbuf_new()) == NULL) fatal_f("sshbuf_new failed"); get_msg(conn, msg); if ((r = sshbuf_get_u8(msg, &type)) != 0 || (r = sshbuf_get_u32(msg, &id)) != 0) fatal_fr(r, "parse"); if (id != expected_id) fatal("ID mismatch (%u != %u)", id, expected_id); if (type == SSH2_FXP_STATUS) { u_int status; if ((r = sshbuf_get_u32(msg, &status)) != 0) fatal_fr(r, "parse status"); error("Couldn't readlink: %s", fx2txt(status)); sshbuf_free(msg); return(NULL); } else if (type != SSH2_FXP_NAME) fatal("Expected SSH2_FXP_NAME(%u) packet, got %u", SSH2_FXP_NAME, type); if ((r = sshbuf_get_u32(msg, &count)) != 0) fatal_fr(r, "parse count"); if (count != 1) fatal("Got multiple names (%d) from SSH_FXP_READLINK", count); if ((r = sshbuf_get_cstring(msg, &filename, NULL)) != 0 || (r = sshbuf_get_cstring(msg, &longname, NULL)) != 0 || (r = decode_attrib(msg, &a)) != 0) fatal_fr(r, "parse filenames/attrib"); debug3("SSH_FXP_READLINK %s -> %s", path, filename); free(longname); sshbuf_free(msg); return filename; } #endif int -do_statvfs(struct sftp_conn *conn, const char *path, struct sftp_statvfs *st, +sftp_statvfs(struct sftp_conn *conn, const char *path, struct sftp_statvfs *st, int quiet) { struct sshbuf *msg; u_int id; int r; if ((conn->exts & SFTP_EXT_STATVFS) == 0) { error("Server does not support statvfs@openssh.com extension"); return -1; } debug2("Sending SSH2_FXP_EXTENDED(statvfs@openssh.com) \"%s\"", path); id = conn->msg_id++; if ((msg = sshbuf_new()) == NULL) fatal_f("sshbuf_new failed"); if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 || (r = sshbuf_put_u32(msg, id)) != 0 || (r = sshbuf_put_cstring(msg, "statvfs@openssh.com")) != 0 || (r = sshbuf_put_cstring(msg, path)) != 0) fatal_fr(r, "compose"); send_msg(conn, msg); sshbuf_free(msg); return get_decode_statvfs(conn, st, id, quiet); } #ifdef notyet int -do_fstatvfs(struct sftp_conn *conn, const u_char *handle, u_int handle_len, +sftp_fstatvfs(struct sftp_conn *conn, const u_char *handle, u_int handle_len, struct sftp_statvfs *st, int quiet) { struct sshbuf *msg; u_int id; if ((conn->exts & SFTP_EXT_FSTATVFS) == 0) { error("Server does not support fstatvfs@openssh.com extension"); return -1; } debug2("Sending SSH2_FXP_EXTENDED(fstatvfs@openssh.com)"); id = conn->msg_id++; if ((msg = sshbuf_new()) == NULL) fatal_f("sshbuf_new failed"); if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 || (r = sshbuf_put_u32(msg, id)) != 0 || (r = sshbuf_put_cstring(msg, "fstatvfs@openssh.com")) != 0 || (r = sshbuf_put_string(msg, handle, handle_len)) != 0) fatal_fr(r, "compose"); send_msg(conn, msg); sshbuf_free(msg); return get_decode_statvfs(conn, st, id, quiet); } #endif int -do_lsetstat(struct sftp_conn *conn, const char *path, Attrib *a) +sftp_lsetstat(struct sftp_conn *conn, const char *path, Attrib *a) { struct sshbuf *msg; u_int status, id; int r; if ((conn->exts & SFTP_EXT_LSETSTAT) == 0) { error("Server does not support lsetstat@openssh.com extension"); return -1; } debug2("Sending SSH2_FXP_EXTENDED(lsetstat@openssh.com) \"%s\"", path); id = conn->msg_id++; if ((msg = sshbuf_new()) == NULL) fatal_f("sshbuf_new failed"); if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 || (r = sshbuf_put_u32(msg, id)) != 0 || (r = sshbuf_put_cstring(msg, "lsetstat@openssh.com")) != 0 || (r = sshbuf_put_cstring(msg, path)) != 0 || (r = encode_attrib(msg, a)) != 0) fatal_fr(r, "compose"); send_msg(conn, msg); sshbuf_free(msg); status = get_status(conn, id); if (status != SSH2_FX_OK) error("remote lsetstat \"%s\": %s", path, fx2txt(status)); return status == SSH2_FX_OK ? 0 : -1; } static void send_read_request(struct sftp_conn *conn, u_int id, u_int64_t offset, u_int len, const u_char *handle, u_int handle_len) { struct sshbuf *msg; int r; if ((msg = sshbuf_new()) == NULL) fatal_f("sshbuf_new failed"); if ((r = sshbuf_put_u8(msg, SSH2_FXP_READ)) != 0 || (r = sshbuf_put_u32(msg, id)) != 0 || (r = sshbuf_put_string(msg, handle, handle_len)) != 0 || (r = sshbuf_put_u64(msg, offset)) != 0 || (r = sshbuf_put_u32(msg, len)) != 0) fatal_fr(r, "compose"); send_msg(conn, msg); sshbuf_free(msg); } static int send_open(struct sftp_conn *conn, const char *path, const char *tag, u_int openmode, Attrib *a, u_char **handlep, size_t *handle_lenp) { Attrib junk; u_char *handle; size_t handle_len; struct sshbuf *msg; int r; u_int id; debug2("Sending SSH2_FXP_OPEN \"%s\"", path); *handlep = NULL; *handle_lenp = 0; if (a == NULL) { attrib_clear(&junk); /* Send empty attributes */ a = &junk; } /* Send open request */ if ((msg = sshbuf_new()) == NULL) fatal_f("sshbuf_new failed"); id = conn->msg_id++; if ((r = sshbuf_put_u8(msg, SSH2_FXP_OPEN)) != 0 || (r = sshbuf_put_u32(msg, id)) != 0 || (r = sshbuf_put_cstring(msg, path)) != 0 || (r = sshbuf_put_u32(msg, openmode)) != 0 || (r = encode_attrib(msg, a)) != 0) fatal_fr(r, "compose %s open", tag); send_msg(conn, msg); sshbuf_free(msg); debug3("Sent %s message SSH2_FXP_OPEN I:%u P:%s M:0x%04x", tag, id, path, openmode); if ((handle = get_handle(conn, id, &handle_len, "%s open \"%s\"", tag, path)) == NULL) return -1; /* success */ *handlep = handle; *handle_lenp = handle_len; return 0; } static const char * progress_meter_path(const char *path) { const char *progresspath; if ((progresspath = strrchr(path, '/')) == NULL) return path; progresspath++; if (*progresspath == '\0') return path; return progresspath; } int -do_download(struct sftp_conn *conn, const char *remote_path, +sftp_download(struct sftp_conn *conn, const char *remote_path, const char *local_path, Attrib *a, int preserve_flag, int resume_flag, int fsync_flag, int inplace_flag) { struct sshbuf *msg; u_char *handle; int local_fd = -1, write_error; int read_error, write_errno, lmodified = 0, reordered = 0, r; u_int64_t offset = 0, size, highwater = 0, maxack = 0; u_int mode, id, buflen, num_req, max_req, status = SSH2_FX_OK; off_t progress_counter; size_t handle_len; struct stat st; struct requests requests; struct request *req; u_char type; + Attrib attr; debug2_f("download remote \"%s\" to local \"%s\"", remote_path, local_path); TAILQ_INIT(&requests); - if (a == NULL && (a = do_stat(conn, remote_path, 0)) == NULL) - return -1; + if (a == NULL) { + if (sftp_stat(conn, remote_path, 0, &attr) != 0) + return -1; + a = &attr; + } /* Do not preserve set[ug]id here, as we do not preserve ownership */ if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) mode = a->perm & 0777; else mode = 0666; if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) && (!S_ISREG(a->perm))) { error("download %s: not a regular file", remote_path); return(-1); } if (a->flags & SSH2_FILEXFER_ATTR_SIZE) size = a->size; else size = 0; buflen = conn->download_buflen; /* Send open request */ if (send_open(conn, remote_path, "remote", SSH2_FXF_READ, NULL, &handle, &handle_len) != 0) return -1; local_fd = open(local_path, O_WRONLY | O_CREAT | ((resume_flag || inplace_flag) ? 0 : O_TRUNC), mode | S_IWUSR); if (local_fd == -1) { error("open local \"%s\": %s", local_path, strerror(errno)); goto fail; } if (resume_flag) { if (fstat(local_fd, &st) == -1) { error("stat local \"%s\": %s", local_path, strerror(errno)); goto fail; } if (st.st_size < 0) { error("\"%s\" has negative size", local_path); goto fail; } if ((u_int64_t)st.st_size > size) { error("Unable to resume download of \"%s\": " "local file is larger than remote", local_path); fail: - do_close(conn, handle, handle_len); + sftp_close(conn, handle, handle_len); free(handle); if (local_fd != -1) close(local_fd); return -1; } offset = highwater = maxack = st.st_size; } /* Read from remote and write to local */ write_error = read_error = write_errno = num_req = 0; max_req = 1; progress_counter = offset; if (showprogress && size != 0) { start_progress_meter(progress_meter_path(remote_path), size, &progress_counter); } if ((msg = sshbuf_new()) == NULL) fatal_f("sshbuf_new failed"); while (num_req > 0 || max_req > 0) { u_char *data; size_t len; /* * Simulate EOF on interrupt: stop sending new requests and * allow outstanding requests to drain gracefully */ if (interrupted) { if (num_req == 0) /* If we haven't started yet... */ break; max_req = 0; } /* Send some more requests */ while (num_req < max_req) { debug3("Request range %llu -> %llu (%d/%d)", (unsigned long long)offset, (unsigned long long)offset + buflen - 1, num_req, max_req); req = request_enqueue(&requests, conn->msg_id++, buflen, offset); offset += buflen; num_req++; send_read_request(conn, req->id, req->offset, req->len, handle, handle_len); } sshbuf_reset(msg); get_msg(conn, msg); if ((r = sshbuf_get_u8(msg, &type)) != 0 || (r = sshbuf_get_u32(msg, &id)) != 0) fatal_fr(r, "parse"); debug3("Received reply T:%u I:%u R:%d", type, id, max_req); /* Find the request in our queue */ if ((req = request_find(&requests, id)) == NULL) fatal("Unexpected reply %u", id); switch (type) { case SSH2_FXP_STATUS: if ((r = sshbuf_get_u32(msg, &status)) != 0) fatal_fr(r, "parse status"); if (status != SSH2_FX_EOF) read_error = 1; max_req = 0; TAILQ_REMOVE(&requests, req, tq); free(req); num_req--; break; case SSH2_FXP_DATA: if ((r = sshbuf_get_string(msg, &data, &len)) != 0) fatal_fr(r, "parse data"); debug3("Received data %llu -> %llu", (unsigned long long)req->offset, (unsigned long long)req->offset + len - 1); if (len > req->len) fatal("Received more data than asked for " "%zu > %zu", len, req->len); lmodified = 1; if ((lseek(local_fd, req->offset, SEEK_SET) == -1 || atomicio(vwrite, local_fd, data, len) != len) && !write_error) { write_errno = errno; write_error = 1; max_req = 0; } else { /* * Track both the highest offset acknowledged * and the highest *contiguous* offset * acknowledged. * We'll need the latter for ftruncate()ing * interrupted transfers. */ if (maxack < req->offset + len) maxack = req->offset + len; if (!reordered && req->offset <= highwater) highwater = maxack; else if (!reordered && req->offset > highwater) reordered = 1; } progress_counter += len; free(data); if (len == req->len) { TAILQ_REMOVE(&requests, req, tq); free(req); num_req--; } else { /* Resend the request for the missing data */ debug3("Short data block, re-requesting " "%llu -> %llu (%2d)", (unsigned long long)req->offset + len, (unsigned long long)req->offset + req->len - 1, num_req); req->id = conn->msg_id++; req->len -= len; req->offset += len; send_read_request(conn, req->id, req->offset, req->len, handle, handle_len); /* Reduce the request size */ if (len < buflen) buflen = MAXIMUM(MIN_READ_SIZE, len); } if (max_req > 0) { /* max_req = 0 iff EOF received */ if (size > 0 && offset > size) { /* Only one request at a time * after the expected EOF */ debug3("Finish at %llu (%2d)", (unsigned long long)offset, num_req); max_req = 1; } else if (max_req < conn->num_requests) { ++max_req; } } break; default: fatal("Expected SSH2_FXP_DATA(%u) packet, got %u", SSH2_FXP_DATA, type); } } if (showprogress && size) stop_progress_meter(); /* Sanity check */ if (TAILQ_FIRST(&requests) != NULL) fatal("Transfer complete, but requests still in queue"); if (!read_error && !write_error && !interrupted) { /* we got everything */ highwater = maxack; } /* * Truncate at highest contiguous point to avoid holes on interrupt, * or unconditionally if writing in place. */ if (inplace_flag || read_error || write_error || interrupted) { if (reordered && resume_flag && (read_error || write_error || interrupted)) { error("Unable to resume download of \"%s\": " "server reordered requests", local_path); } debug("truncating at %llu", (unsigned long long)highwater); if (ftruncate(local_fd, highwater) == -1) error("local ftruncate \"%s\": %s", local_path, strerror(errno)); } if (read_error) { error("read remote \"%s\" : %s", remote_path, fx2txt(status)); status = -1; - do_close(conn, handle, handle_len); + sftp_close(conn, handle, handle_len); } else if (write_error) { error("write local \"%s\": %s", local_path, strerror(write_errno)); status = SSH2_FX_FAILURE; - do_close(conn, handle, handle_len); + sftp_close(conn, handle, handle_len); } else { - if (do_close(conn, handle, handle_len) != 0 || interrupted) + if (sftp_close(conn, handle, handle_len) != 0 || interrupted) status = SSH2_FX_FAILURE; else status = SSH2_FX_OK; /* Override umask and utimes if asked */ #ifdef HAVE_FCHMOD if (preserve_flag && fchmod(local_fd, mode) == -1) #else if (preserve_flag && chmod(local_path, mode) == -1) #endif /* HAVE_FCHMOD */ error("local chmod \"%s\": %s", local_path, strerror(errno)); if (preserve_flag && (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME)) { struct timeval tv[2]; tv[0].tv_sec = a->atime; tv[1].tv_sec = a->mtime; tv[0].tv_usec = tv[1].tv_usec = 0; if (utimes(local_path, tv) == -1) error("local set times \"%s\": %s", local_path, strerror(errno)); } if (resume_flag && !lmodified) logit("File \"%s\" was not modified", local_path); else if (fsync_flag) { debug("syncing \"%s\"", local_path); if (fsync(local_fd) == -1) error("local sync \"%s\": %s", local_path, strerror(errno)); } } close(local_fd); sshbuf_free(msg); free(handle); return status == SSH2_FX_OK ? 0 : -1; } static int download_dir_internal(struct sftp_conn *conn, const char *src, const char *dst, int depth, Attrib *dirattrib, int preserve_flag, int print_flag, int resume_flag, int fsync_flag, int follow_link_flag, int inplace_flag) { int i, ret = 0; SFTP_DIRENT **dir_entries; char *filename, *new_src = NULL, *new_dst = NULL; mode_t mode = 0777, tmpmode = mode; + Attrib *a, ldirattrib, lsym; if (depth >= MAX_DIR_DEPTH) { error("Maximum directory depth exceeded: %d levels", depth); return -1; } debug2_f("download dir remote \"%s\" to local \"%s\"", src, dst); - if (dirattrib == NULL && - (dirattrib = do_stat(conn, src, 1)) == NULL) { - error("stat remote \"%s\" directory failed", src); - return -1; + if (dirattrib == NULL) { + if (sftp_stat(conn, src, 1, &ldirattrib) != 0) { + error("stat remote \"%s\" directory failed", src); + return -1; + } + dirattrib = &ldirattrib; } if (!S_ISDIR(dirattrib->perm)) { error("\"%s\" is not a directory", src); return -1; } if (print_flag && print_flag != SFTP_PROGRESS_ONLY) mprintf("Retrieving %s\n", src); if (dirattrib->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { mode = dirattrib->perm & 01777; tmpmode = mode | (S_IWUSR|S_IXUSR); } else { debug("download remote \"%s\": server " "did not send permissions", dst); } if (mkdir(dst, tmpmode) == -1 && errno != EEXIST) { error("mkdir %s: %s", dst, strerror(errno)); return -1; } - if (do_readdir(conn, src, &dir_entries) == -1) { + if (sftp_readdir(conn, src, &dir_entries) == -1) { error("remote readdir \"%s\" failed", src); return -1; } for (i = 0; dir_entries[i] != NULL && !interrupted; i++) { free(new_dst); free(new_src); filename = dir_entries[i]->filename; - new_dst = path_append(dst, filename); - new_src = path_append(src, filename); + new_dst = sftp_path_append(dst, filename); + new_src = sftp_path_append(src, filename); + + a = &dir_entries[i]->a; + if (S_ISLNK(a->perm)) { + if (!follow_link_flag) { + logit("download \"%s\": not a regular file", + new_src); + continue; + } + /* Replace the stat contents with the symlink target */ + if (sftp_stat(conn, new_src, 1, &lsym) != 0) { + logit("remote stat \"%s\" failed", new_src); + ret = -1; + continue; + } + a = &lsym; + } - if (S_ISDIR(dir_entries[i]->a.perm)) { + if (S_ISDIR(a->perm)) { if (strcmp(filename, ".") == 0 || strcmp(filename, "..") == 0) continue; if (download_dir_internal(conn, new_src, new_dst, - depth + 1, &(dir_entries[i]->a), preserve_flag, + depth + 1, a, preserve_flag, print_flag, resume_flag, fsync_flag, follow_link_flag, inplace_flag) == -1) ret = -1; - } else if (S_ISREG(dir_entries[i]->a.perm) || - (follow_link_flag && S_ISLNK(dir_entries[i]->a.perm))) { - /* - * If this is a symlink then don't send the link's - * Attrib. do_download() will do a FXP_STAT operation - * and get the link target's attributes. - */ - if (do_download(conn, new_src, new_dst, - S_ISLNK(dir_entries[i]->a.perm) ? NULL : - &(dir_entries[i]->a), + } else if (S_ISREG(a->perm)) { + if (sftp_download(conn, new_src, new_dst, a, preserve_flag, resume_flag, fsync_flag, inplace_flag) == -1) { error("Download of file %s to %s failed", new_src, new_dst); ret = -1; } } else logit("download \"%s\": not a regular file", new_src); } free(new_dst); free(new_src); if (preserve_flag) { if (dirattrib->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { struct timeval tv[2]; tv[0].tv_sec = dirattrib->atime; tv[1].tv_sec = dirattrib->mtime; tv[0].tv_usec = tv[1].tv_usec = 0; if (utimes(dst, tv) == -1) error("local set times on \"%s\": %s", dst, strerror(errno)); } else debug("Server did not send times for directory " "\"%s\"", dst); } if (mode != tmpmode && chmod(dst, mode) == -1) error("local chmod directory \"%s\": %s", dst, strerror(errno)); - free_sftp_dirents(dir_entries); + sftp_free_dirents(dir_entries); return ret; } int -download_dir(struct sftp_conn *conn, const char *src, const char *dst, +sftp_download_dir(struct sftp_conn *conn, const char *src, const char *dst, Attrib *dirattrib, int preserve_flag, int print_flag, int resume_flag, int fsync_flag, int follow_link_flag, int inplace_flag) { char *src_canon; int ret; - if ((src_canon = do_realpath(conn, src)) == NULL) { + if ((src_canon = sftp_realpath(conn, src)) == NULL) { error("download \"%s\": path canonicalization failed", src); return -1; } ret = download_dir_internal(conn, src_canon, dst, 0, dirattrib, preserve_flag, print_flag, resume_flag, fsync_flag, follow_link_flag, inplace_flag); free(src_canon); return ret; } int -do_upload(struct sftp_conn *conn, const char *local_path, +sftp_upload(struct sftp_conn *conn, const char *local_path, const char *remote_path, int preserve_flag, int resume, int fsync_flag, int inplace_flag) { int r, local_fd; u_int openmode, id, status = SSH2_FX_OK, reordered = 0; off_t offset, progress_counter; u_char type, *handle, *data; struct sshbuf *msg; struct stat sb; - Attrib a, t, *c = NULL; + Attrib a, t, c; u_int32_t startid, ackid; u_int64_t highwater = 0, maxack = 0; struct request *ack = NULL; struct requests acks; size_t handle_len; debug2_f("upload local \"%s\" to remote \"%s\"", local_path, remote_path); TAILQ_INIT(&acks); if ((local_fd = open(local_path, O_RDONLY)) == -1) { error("open local \"%s\": %s", local_path, strerror(errno)); return(-1); } if (fstat(local_fd, &sb) == -1) { error("fstat local \"%s\": %s", local_path, strerror(errno)); close(local_fd); return(-1); } if (!S_ISREG(sb.st_mode)) { error("local \"%s\" is not a regular file", local_path); close(local_fd); return(-1); } stat_to_attrib(&sb, &a); a.flags &= ~SSH2_FILEXFER_ATTR_SIZE; a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID; a.perm &= 0777; if (!preserve_flag) a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME; if (resume) { /* Get remote file size if it exists */ - if ((c = do_stat(conn, remote_path, 0)) == NULL) { + if (sftp_stat(conn, remote_path, 0, &c) != 0) { close(local_fd); return -1; } - if ((off_t)c->size >= sb.st_size) { + if ((off_t)c.size >= sb.st_size) { error("resume \"%s\": destination file " "same size or larger", local_path); close(local_fd); return -1; } - if (lseek(local_fd, (off_t)c->size, SEEK_SET) == -1) { + if (lseek(local_fd, (off_t)c.size, SEEK_SET) == -1) { close(local_fd); return -1; } } openmode = SSH2_FXF_WRITE|SSH2_FXF_CREAT; if (resume) openmode |= SSH2_FXF_APPEND; else if (!inplace_flag) openmode |= SSH2_FXF_TRUNC; /* Send open request */ if (send_open(conn, remote_path, "dest", openmode, &a, &handle, &handle_len) != 0) { close(local_fd); return -1; } id = conn->msg_id; startid = ackid = id + 1; data = xmalloc(conn->upload_buflen); /* Read from local and write to remote */ - offset = progress_counter = (resume ? c->size : 0); + offset = progress_counter = (resume ? c.size : 0); if (showprogress) { start_progress_meter(progress_meter_path(local_path), sb.st_size, &progress_counter); } if ((msg = sshbuf_new()) == NULL) fatal_f("sshbuf_new failed"); for (;;) { int len; /* * Can't use atomicio here because it returns 0 on EOF, * thus losing the last block of the file. * Simulate an EOF on interrupt, allowing ACKs from the * server to drain. */ if (interrupted || status != SSH2_FX_OK) len = 0; else do len = read(local_fd, data, conn->upload_buflen); while ((len == -1) && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)); if (len == -1) { fatal("read local \"%s\": %s", local_path, strerror(errno)); } else if (len != 0) { ack = request_enqueue(&acks, ++id, len, offset); sshbuf_reset(msg); if ((r = sshbuf_put_u8(msg, SSH2_FXP_WRITE)) != 0 || (r = sshbuf_put_u32(msg, ack->id)) != 0 || (r = sshbuf_put_string(msg, handle, handle_len)) != 0 || (r = sshbuf_put_u64(msg, offset)) != 0 || (r = sshbuf_put_string(msg, data, len)) != 0) fatal_fr(r, "compose"); send_msg(conn, msg); debug3("Sent message SSH2_FXP_WRITE I:%u O:%llu S:%u", id, (unsigned long long)offset, len); } else if (TAILQ_FIRST(&acks) == NULL) break; if (ack == NULL) fatal("Unexpected ACK %u", id); if (id == startid || len == 0 || id - ackid >= conn->num_requests) { u_int rid; sshbuf_reset(msg); get_msg(conn, msg); if ((r = sshbuf_get_u8(msg, &type)) != 0 || (r = sshbuf_get_u32(msg, &rid)) != 0) fatal_fr(r, "parse"); if (type != SSH2_FXP_STATUS) fatal("Expected SSH2_FXP_STATUS(%d) packet, " "got %d", SSH2_FXP_STATUS, type); if ((r = sshbuf_get_u32(msg, &status)) != 0) fatal_fr(r, "parse status"); debug3("SSH2_FXP_STATUS %u", status); /* Find the request in our queue */ if ((ack = request_find(&acks, rid)) == NULL) fatal("Can't find request for ID %u", rid); TAILQ_REMOVE(&acks, ack, tq); debug3("In write loop, ack for %u %zu bytes at %lld", ack->id, ack->len, (unsigned long long)ack->offset); ++ackid; progress_counter += ack->len; /* * Track both the highest offset acknowledged and the * highest *contiguous* offset acknowledged. * We'll need the latter for ftruncate()ing * interrupted transfers. */ if (maxack < ack->offset + ack->len) maxack = ack->offset + ack->len; if (!reordered && ack->offset <= highwater) highwater = maxack; else if (!reordered && ack->offset > highwater) { debug3_f("server reordered ACKs"); reordered = 1; } free(ack); } offset += len; if (offset < 0) fatal_f("offset < 0"); } sshbuf_free(msg); if (showprogress) stop_progress_meter(); free(data); if (status == SSH2_FX_OK && !interrupted) { /* we got everything */ highwater = maxack; } if (status != SSH2_FX_OK) { error("write remote \"%s\": %s", remote_path, fx2txt(status)); status = SSH2_FX_FAILURE; } if (inplace_flag || (resume && (status != SSH2_FX_OK || interrupted))) { debug("truncating at %llu", (unsigned long long)highwater); attrib_clear(&t); t.flags = SSH2_FILEXFER_ATTR_SIZE; t.size = highwater; - do_fsetstat(conn, handle, handle_len, &t); + sftp_fsetstat(conn, handle, handle_len, &t); } if (close(local_fd) == -1) { error("close local \"%s\": %s", local_path, strerror(errno)); status = SSH2_FX_FAILURE; } /* Override umask and utimes if asked */ if (preserve_flag) - do_fsetstat(conn, handle, handle_len, &a); + sftp_fsetstat(conn, handle, handle_len, &a); if (fsync_flag) - (void)do_fsync(conn, handle, handle_len); + (void)sftp_fsync(conn, handle, handle_len); - if (do_close(conn, handle, handle_len) != 0) + if (sftp_close(conn, handle, handle_len) != 0) status = SSH2_FX_FAILURE; free(handle); return status == SSH2_FX_OK ? 0 : -1; } static int upload_dir_internal(struct sftp_conn *conn, const char *src, const char *dst, int depth, int preserve_flag, int print_flag, int resume, int fsync_flag, int follow_link_flag, int inplace_flag) { int ret = 0; DIR *dirp; struct dirent *dp; char *filename, *new_src = NULL, *new_dst = NULL; struct stat sb; - Attrib a, *dirattrib; + Attrib a, dirattrib; u_int32_t saved_perm; debug2_f("upload local dir \"%s\" to remote \"%s\"", src, dst); if (depth >= MAX_DIR_DEPTH) { error("Maximum directory depth exceeded: %d levels", depth); return -1; } if (stat(src, &sb) == -1) { error("stat local \"%s\": %s", src, strerror(errno)); return -1; } if (!S_ISDIR(sb.st_mode)) { error("\"%s\" is not a directory", src); return -1; } if (print_flag && print_flag != SFTP_PROGRESS_ONLY) mprintf("Entering %s\n", src); stat_to_attrib(&sb, &a); a.flags &= ~SSH2_FILEXFER_ATTR_SIZE; a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID; a.perm &= 01777; if (!preserve_flag) a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME; /* * sftp lacks a portable status value to match errno EEXIST, * so if we get a failure back then we must check whether * the path already existed and is a directory. Ensure we can * write to the directory we create for the duration of the transfer. */ saved_perm = a.perm; a.perm |= (S_IWUSR|S_IXUSR); - if (do_mkdir(conn, dst, &a, 0) != 0) { - if ((dirattrib = do_stat(conn, dst, 0)) == NULL) + if (sftp_mkdir(conn, dst, &a, 0) != 0) { + if (sftp_stat(conn, dst, 0, &dirattrib) != 0) return -1; - if (!S_ISDIR(dirattrib->perm)) { + if (!S_ISDIR(dirattrib.perm)) { error("\"%s\" exists but is not a directory", dst); return -1; } } a.perm = saved_perm; if ((dirp = opendir(src)) == NULL) { error("local opendir \"%s\": %s", src, strerror(errno)); return -1; } while (((dp = readdir(dirp)) != NULL) && !interrupted) { if (dp->d_ino == 0) continue; free(new_dst); free(new_src); filename = dp->d_name; - new_dst = path_append(dst, filename); - new_src = path_append(src, filename); + new_dst = sftp_path_append(dst, filename); + new_src = sftp_path_append(src, filename); + if (strcmp(filename, ".") == 0 || strcmp(filename, "..") == 0) + continue; if (lstat(new_src, &sb) == -1) { logit("local lstat \"%s\": %s", filename, strerror(errno)); ret = -1; - } else if (S_ISDIR(sb.st_mode)) { - if (strcmp(filename, ".") == 0 || - strcmp(filename, "..") == 0) + continue; + } + if (S_ISLNK(sb.st_mode)) { + if (!follow_link_flag) { + logit("%s: not a regular file", filename); continue; - + } + /* Replace the stat contents with the symlink target */ + if (stat(new_src, &sb) == -1) { + logit("local stat \"%s\": %s", filename, + strerror(errno)); + ret = -1; + continue; + } + } + if (S_ISDIR(sb.st_mode)) { if (upload_dir_internal(conn, new_src, new_dst, depth + 1, preserve_flag, print_flag, resume, fsync_flag, follow_link_flag, inplace_flag) == -1) ret = -1; - } else if (S_ISREG(sb.st_mode) || - (follow_link_flag && S_ISLNK(sb.st_mode))) { - if (do_upload(conn, new_src, new_dst, + } else if (S_ISREG(sb.st_mode)) { + if (sftp_upload(conn, new_src, new_dst, preserve_flag, resume, fsync_flag, inplace_flag) == -1) { error("upload \"%s\" to \"%s\" failed", new_src, new_dst); ret = -1; } } else logit("%s: not a regular file", filename); } free(new_dst); free(new_src); - do_setstat(conn, dst, &a); + sftp_setstat(conn, dst, &a); (void) closedir(dirp); return ret; } int -upload_dir(struct sftp_conn *conn, const char *src, const char *dst, +sftp_upload_dir(struct sftp_conn *conn, const char *src, const char *dst, int preserve_flag, int print_flag, int resume, int fsync_flag, int follow_link_flag, int inplace_flag) { char *dst_canon; int ret; - if ((dst_canon = do_realpath(conn, dst)) == NULL) { + if ((dst_canon = sftp_realpath(conn, dst)) == NULL) { error("upload \"%s\": path canonicalization failed", dst); return -1; } ret = upload_dir_internal(conn, src, dst_canon, 0, preserve_flag, print_flag, resume, fsync_flag, follow_link_flag, inplace_flag); free(dst_canon); return ret; } static void handle_dest_replies(struct sftp_conn *to, const char *to_path, int synchronous, u_int *nreqsp, u_int *write_errorp) { struct sshbuf *msg; u_char type; u_int id, status; int r; struct pollfd pfd; if ((msg = sshbuf_new()) == NULL) fatal_f("sshbuf_new failed"); /* Try to eat replies from the upload side */ while (*nreqsp > 0) { debug3_f("%u outstanding replies", *nreqsp); if (!synchronous) { /* Bail out if no data is ready to be read */ pfd.fd = to->fd_in; pfd.events = POLLIN; if ((r = poll(&pfd, 1, 0)) == -1) { if (errno == EINTR) break; fatal_f("poll: %s", strerror(errno)); } else if (r == 0) break; /* fd not ready */ } sshbuf_reset(msg); get_msg(to, msg); if ((r = sshbuf_get_u8(msg, &type)) != 0 || (r = sshbuf_get_u32(msg, &id)) != 0) fatal_fr(r, "dest parse"); debug3("Received dest reply T:%u I:%u R:%u", type, id, *nreqsp); if (type != SSH2_FXP_STATUS) { fatal_f("Expected SSH2_FXP_STATUS(%d) packet, got %d", SSH2_FXP_STATUS, type); } if ((r = sshbuf_get_u32(msg, &status)) != 0) fatal_fr(r, "parse dest status"); debug3("dest SSH2_FXP_STATUS %u", status); if (status != SSH2_FX_OK) { /* record first error */ if (*write_errorp == 0) *write_errorp = status; } /* - * XXX this doesn't do full reply matching like do_upload and + * XXX this doesn't do full reply matching like sftp_upload and * so cannot gracefully truncate terminated uploads at a * high-water mark. ATM the only caller of this function (scp) * doesn't support transfer resumption, so this doesn't matter * a whole lot. * - * To be safe, do_crossload truncates the destination file to + * To be safe, sftp_crossload truncates the destination file to * zero length on upload failure, since we can't trust the * server not to have reordered replies that could have * inserted holes where none existed in the source file. * * XXX we could get a more accutate progress bar if we updated * the counter based on the reply from the destination... */ (*nreqsp)--; } debug3_f("done: %u outstanding replies", *nreqsp); sshbuf_free(msg); } int -do_crossload(struct sftp_conn *from, struct sftp_conn *to, +sftp_crossload(struct sftp_conn *from, struct sftp_conn *to, const char *from_path, const char *to_path, Attrib *a, int preserve_flag) { struct sshbuf *msg; int write_error, read_error, r; u_int64_t offset = 0, size; u_int id, buflen, num_req, max_req, status = SSH2_FX_OK; u_int num_upload_req; off_t progress_counter; u_char *from_handle, *to_handle; size_t from_handle_len, to_handle_len; struct requests requests; struct request *req; u_char type; + Attrib attr; debug2_f("crossload src \"%s\" to dst \"%s\"", from_path, to_path); TAILQ_INIT(&requests); - if (a == NULL && (a = do_stat(from, from_path, 0)) == NULL) - return -1; + if (a == NULL) { + if (sftp_stat(from, from_path, 0, &attr) != 0) + return -1; + a = &attr; + } if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) && (!S_ISREG(a->perm))) { error("download \"%s\": not a regular file", from_path); return(-1); } if (a->flags & SSH2_FILEXFER_ATTR_SIZE) size = a->size; else size = 0; buflen = from->download_buflen; if (buflen > to->upload_buflen) buflen = to->upload_buflen; /* Send open request to read side */ if (send_open(from, from_path, "origin", SSH2_FXF_READ, NULL, &from_handle, &from_handle_len) != 0) return -1; /* Send open request to write side */ a->flags &= ~SSH2_FILEXFER_ATTR_SIZE; a->flags &= ~SSH2_FILEXFER_ATTR_UIDGID; a->perm &= 0777; if (!preserve_flag) a->flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME; if (send_open(to, to_path, "dest", SSH2_FXF_WRITE|SSH2_FXF_CREAT|SSH2_FXF_TRUNC, a, &to_handle, &to_handle_len) != 0) { - do_close(from, from_handle, from_handle_len); + sftp_close(from, from_handle, from_handle_len); return -1; } /* Read from remote "from" and write to remote "to" */ offset = 0; write_error = read_error = num_req = num_upload_req = 0; max_req = 1; progress_counter = 0; if (showprogress && size != 0) { start_progress_meter(progress_meter_path(from_path), size, &progress_counter); } if ((msg = sshbuf_new()) == NULL) fatal_f("sshbuf_new failed"); while (num_req > 0 || max_req > 0) { u_char *data; size_t len; /* * Simulate EOF on interrupt: stop sending new requests and * allow outstanding requests to drain gracefully */ if (interrupted) { if (num_req == 0) /* If we haven't started yet... */ break; max_req = 0; } /* Send some more requests */ while (num_req < max_req) { debug3("Request range %llu -> %llu (%d/%d)", (unsigned long long)offset, (unsigned long long)offset + buflen - 1, num_req, max_req); req = request_enqueue(&requests, from->msg_id++, buflen, offset); offset += buflen; num_req++; send_read_request(from, req->id, req->offset, req->len, from_handle, from_handle_len); } /* Try to eat replies from the upload side (nonblocking) */ handle_dest_replies(to, to_path, 0, &num_upload_req, &write_error); sshbuf_reset(msg); get_msg(from, msg); if ((r = sshbuf_get_u8(msg, &type)) != 0 || (r = sshbuf_get_u32(msg, &id)) != 0) fatal_fr(r, "parse"); debug3("Received origin reply T:%u I:%u R:%d", type, id, max_req); /* Find the request in our queue */ if ((req = request_find(&requests, id)) == NULL) fatal("Unexpected reply %u", id); switch (type) { case SSH2_FXP_STATUS: if ((r = sshbuf_get_u32(msg, &status)) != 0) fatal_fr(r, "parse status"); if (status != SSH2_FX_EOF) read_error = 1; max_req = 0; TAILQ_REMOVE(&requests, req, tq); free(req); num_req--; break; case SSH2_FXP_DATA: if ((r = sshbuf_get_string(msg, &data, &len)) != 0) fatal_fr(r, "parse data"); debug3("Received data %llu -> %llu", (unsigned long long)req->offset, (unsigned long long)req->offset + len - 1); if (len > req->len) fatal("Received more data than asked for " "%zu > %zu", len, req->len); /* Write this chunk out to the destination */ sshbuf_reset(msg); if ((r = sshbuf_put_u8(msg, SSH2_FXP_WRITE)) != 0 || (r = sshbuf_put_u32(msg, to->msg_id++)) != 0 || (r = sshbuf_put_string(msg, to_handle, to_handle_len)) != 0 || (r = sshbuf_put_u64(msg, req->offset)) != 0 || (r = sshbuf_put_string(msg, data, len)) != 0) fatal_fr(r, "compose write"); send_msg(to, msg); debug3("Sent message SSH2_FXP_WRITE I:%u O:%llu S:%zu", id, (unsigned long long)offset, len); num_upload_req++; progress_counter += len; free(data); if (len == req->len) { TAILQ_REMOVE(&requests, req, tq); free(req); num_req--; } else { /* Resend the request for the missing data */ debug3("Short data block, re-requesting " "%llu -> %llu (%2d)", (unsigned long long)req->offset + len, (unsigned long long)req->offset + req->len - 1, num_req); req->id = from->msg_id++; req->len -= len; req->offset += len; send_read_request(from, req->id, req->offset, req->len, from_handle, from_handle_len); /* Reduce the request size */ if (len < buflen) buflen = MAXIMUM(MIN_READ_SIZE, len); } if (max_req > 0) { /* max_req = 0 iff EOF received */ if (size > 0 && offset > size) { /* Only one request at a time * after the expected EOF */ debug3("Finish at %llu (%2d)", (unsigned long long)offset, num_req); max_req = 1; } else if (max_req < from->num_requests) { ++max_req; } } break; default: fatal("Expected SSH2_FXP_DATA(%u) packet, got %u", SSH2_FXP_DATA, type); } } if (showprogress && size) stop_progress_meter(); /* Drain replies from the server (blocking) */ debug3_f("waiting for %u replies from destination", num_upload_req); handle_dest_replies(to, to_path, 1, &num_upload_req, &write_error); /* Sanity check */ if (TAILQ_FIRST(&requests) != NULL) fatal("Transfer complete, but requests still in queue"); /* Truncate at 0 length on interrupt or error to avoid holes at dest */ if (read_error || write_error || interrupted) { debug("truncating \"%s\" at 0", to_path); - do_close(to, to_handle, to_handle_len); + sftp_close(to, to_handle, to_handle_len); free(to_handle); if (send_open(to, to_path, "dest", SSH2_FXF_WRITE|SSH2_FXF_CREAT|SSH2_FXF_TRUNC, a, &to_handle, &to_handle_len) != 0) { error("dest truncate \"%s\" failed", to_path); to_handle = NULL; } } if (read_error) { error("read origin \"%s\": %s", from_path, fx2txt(status)); status = -1; - do_close(from, from_handle, from_handle_len); + sftp_close(from, from_handle, from_handle_len); if (to_handle != NULL) - do_close(to, to_handle, to_handle_len); + sftp_close(to, to_handle, to_handle_len); } else if (write_error) { error("write dest \"%s\": %s", to_path, fx2txt(write_error)); status = SSH2_FX_FAILURE; - do_close(from, from_handle, from_handle_len); + sftp_close(from, from_handle, from_handle_len); if (to_handle != NULL) - do_close(to, to_handle, to_handle_len); + sftp_close(to, to_handle, to_handle_len); } else { - if (do_close(from, from_handle, from_handle_len) != 0 || + if (sftp_close(from, from_handle, from_handle_len) != 0 || interrupted) status = -1; else status = SSH2_FX_OK; if (to_handle != NULL) { /* Need to resend utimes after write */ if (preserve_flag) - do_fsetstat(to, to_handle, to_handle_len, a); - do_close(to, to_handle, to_handle_len); + sftp_fsetstat(to, to_handle, to_handle_len, a); + sftp_close(to, to_handle, to_handle_len); } } sshbuf_free(msg); free(from_handle); free(to_handle); return status == SSH2_FX_OK ? 0 : -1; } static int crossload_dir_internal(struct sftp_conn *from, struct sftp_conn *to, const char *from_path, const char *to_path, int depth, Attrib *dirattrib, int preserve_flag, int print_flag, int follow_link_flag) { int i, ret = 0; SFTP_DIRENT **dir_entries; char *filename, *new_from_path = NULL, *new_to_path = NULL; mode_t mode = 0777; - Attrib curdir; + Attrib *a, curdir, ldirattrib, newdir, lsym; debug2_f("crossload dir src \"%s\" to dst \"%s\"", from_path, to_path); if (depth >= MAX_DIR_DEPTH) { error("Maximum directory depth exceeded: %d levels", depth); return -1; } - if (dirattrib == NULL && - (dirattrib = do_stat(from, from_path, 1)) == NULL) { - error("stat remote \"%s\" failed", from_path); - return -1; + if (dirattrib == NULL) { + if (sftp_stat(from, from_path, 1, &ldirattrib) != 0) { + error("stat remote \"%s\" failed", from_path); + return -1; + } + dirattrib = &ldirattrib; } if (!S_ISDIR(dirattrib->perm)) { error("\"%s\" is not a directory", from_path); return -1; } if (print_flag && print_flag != SFTP_PROGRESS_ONLY) mprintf("Retrieving %s\n", from_path); curdir = *dirattrib; /* dirattrib will be clobbered */ curdir.flags &= ~SSH2_FILEXFER_ATTR_SIZE; curdir.flags &= ~SSH2_FILEXFER_ATTR_UIDGID; if ((curdir.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) == 0) { debug("Origin did not send permissions for " "directory \"%s\"", to_path); curdir.perm = S_IWUSR|S_IXUSR; curdir.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS; } /* We need to be able to write to the directory while we transfer it */ mode = curdir.perm & 01777; curdir.perm = mode | (S_IWUSR|S_IXUSR); /* * sftp lacks a portable status value to match errno EEXIST, * so if we get a failure back then we must check whether * the path already existed and is a directory. Ensure we can * write to the directory we create for the duration of the transfer. */ - if (do_mkdir(to, to_path, &curdir, 0) != 0) { - if ((dirattrib = do_stat(to, to_path, 0)) == NULL) + if (sftp_mkdir(to, to_path, &curdir, 0) != 0) { + if (sftp_stat(to, to_path, 0, &newdir) != 0) return -1; - if (!S_ISDIR(dirattrib->perm)) { + if (!S_ISDIR(newdir.perm)) { error("\"%s\" exists but is not a directory", to_path); return -1; } } curdir.perm = mode; - if (do_readdir(from, from_path, &dir_entries) == -1) { + if (sftp_readdir(from, from_path, &dir_entries) == -1) { error("origin readdir \"%s\" failed", from_path); return -1; } for (i = 0; dir_entries[i] != NULL && !interrupted; i++) { free(new_from_path); free(new_to_path); filename = dir_entries[i]->filename; - new_from_path = path_append(from_path, filename); - new_to_path = path_append(to_path, filename); + new_from_path = sftp_path_append(from_path, filename); + new_to_path = sftp_path_append(to_path, filename); - if (S_ISDIR(dir_entries[i]->a.perm)) { + a = &dir_entries[i]->a; + if (S_ISLNK(a->perm)) { + if (!follow_link_flag) { + logit("%s: not a regular file", filename); + continue; + } + /* Replace the stat contents with the symlink target */ + if (sftp_stat(from, new_from_path, 1, &lsym) != 0) { + logit("remote stat \"%s\" failed", + new_from_path); + ret = -1; + continue; + } + a = &lsym; + } + if (S_ISDIR(a->perm)) { if (strcmp(filename, ".") == 0 || strcmp(filename, "..") == 0) continue; if (crossload_dir_internal(from, to, new_from_path, new_to_path, - depth + 1, &(dir_entries[i]->a), preserve_flag, + depth + 1, a, preserve_flag, print_flag, follow_link_flag) == -1) ret = -1; - } else if (S_ISREG(dir_entries[i]->a.perm) || - (follow_link_flag && S_ISLNK(dir_entries[i]->a.perm))) { - /* - * If this is a symlink then don't send the link's - * Attrib. do_download() will do a FXP_STAT operation - * and get the link target's attributes. - */ - if (do_crossload(from, to, new_from_path, new_to_path, - S_ISLNK(dir_entries[i]->a.perm) ? NULL : - &(dir_entries[i]->a), preserve_flag) == -1) { + } else if (S_ISREG(a->perm)) { + if (sftp_crossload(from, to, new_from_path, + new_to_path, a, preserve_flag) == -1) { error("crossload \"%s\" to \"%s\" failed", new_from_path, new_to_path); ret = -1; } } else { logit("origin \"%s\": not a regular file", new_from_path); } } free(new_to_path); free(new_from_path); - do_setstat(to, to_path, &curdir); + sftp_setstat(to, to_path, &curdir); - free_sftp_dirents(dir_entries); + sftp_free_dirents(dir_entries); return ret; } int -crossload_dir(struct sftp_conn *from, struct sftp_conn *to, +sftp_crossload_dir(struct sftp_conn *from, struct sftp_conn *to, const char *from_path, const char *to_path, Attrib *dirattrib, int preserve_flag, int print_flag, int follow_link_flag) { char *from_path_canon; int ret; - if ((from_path_canon = do_realpath(from, from_path)) == NULL) { + if ((from_path_canon = sftp_realpath(from, from_path)) == NULL) { error("crossload \"%s\": path canonicalization failed", from_path); return -1; } ret = crossload_dir_internal(from, to, from_path_canon, to_path, 0, dirattrib, preserve_flag, print_flag, follow_link_flag); free(from_path_canon); return ret; } int -can_get_users_groups_by_id(struct sftp_conn *conn) +sftp_can_get_users_groups_by_id(struct sftp_conn *conn) { return (conn->exts & SFTP_EXT_GETUSERSGROUPS_BY_ID) != 0; } int -do_get_users_groups_by_id(struct sftp_conn *conn, +sftp_get_users_groups_by_id(struct sftp_conn *conn, const u_int *uids, u_int nuids, const u_int *gids, u_int ngids, char ***usernamesp, char ***groupnamesp) { struct sshbuf *msg, *uidbuf, *gidbuf; u_int i, expected_id, id; char *name, **usernames = NULL, **groupnames = NULL; u_char type; int r; *usernamesp = *groupnamesp = NULL; - if (!can_get_users_groups_by_id(conn)) + if (!sftp_can_get_users_groups_by_id(conn)) return SSH_ERR_FEATURE_UNSUPPORTED; if ((msg = sshbuf_new()) == NULL || (uidbuf = sshbuf_new()) == NULL || (gidbuf = sshbuf_new()) == NULL) fatal_f("sshbuf_new failed"); expected_id = id = conn->msg_id++; debug2("Sending SSH2_FXP_EXTENDED(users-groups-by-id@openssh.com)"); for (i = 0; i < nuids; i++) { if ((r = sshbuf_put_u32(uidbuf, uids[i])) != 0) fatal_fr(r, "compose uids"); } for (i = 0; i < ngids; i++) { if ((r = sshbuf_put_u32(gidbuf, gids[i])) != 0) fatal_fr(r, "compose gids"); } if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 || (r = sshbuf_put_u32(msg, id)) != 0 || (r = sshbuf_put_cstring(msg, "users-groups-by-id@openssh.com")) != 0 || (r = sshbuf_put_stringb(msg, uidbuf)) != 0 || (r = sshbuf_put_stringb(msg, gidbuf)) != 0) fatal_fr(r, "compose"); send_msg(conn, msg); get_msg(conn, msg); if ((r = sshbuf_get_u8(msg, &type)) != 0 || (r = sshbuf_get_u32(msg, &id)) != 0) fatal_fr(r, "parse"); if (id != expected_id) fatal("ID mismatch (%u != %u)", id, expected_id); if (type == SSH2_FXP_STATUS) { u_int status; char *errmsg; if ((r = sshbuf_get_u32(msg, &status)) != 0 || (r = sshbuf_get_cstring(msg, &errmsg, NULL)) != 0) fatal_fr(r, "parse status"); error("users-groups-by-id %s", *errmsg == '\0' ? fx2txt(status) : errmsg); free(errmsg); sshbuf_free(msg); sshbuf_free(uidbuf); sshbuf_free(gidbuf); return -1; } else if (type != SSH2_FXP_EXTENDED_REPLY) fatal("Expected SSH2_FXP_EXTENDED_REPLY(%u) packet, got %u", SSH2_FXP_EXTENDED_REPLY, type); /* reuse */ sshbuf_free(uidbuf); sshbuf_free(gidbuf); uidbuf = gidbuf = NULL; if ((r = sshbuf_froms(msg, &uidbuf)) != 0 || (r = sshbuf_froms(msg, &gidbuf)) != 0) fatal_fr(r, "parse response"); if (nuids > 0) { usernames = xcalloc(nuids, sizeof(*usernames)); for (i = 0; i < nuids; i++) { if ((r = sshbuf_get_cstring(uidbuf, &name, NULL)) != 0) fatal_fr(r, "parse user name"); /* Handle unresolved names */ if (*name == '\0') { free(name); name = NULL; } usernames[i] = name; } } if (ngids > 0) { groupnames = xcalloc(ngids, sizeof(*groupnames)); for (i = 0; i < ngids; i++) { if ((r = sshbuf_get_cstring(gidbuf, &name, NULL)) != 0) fatal_fr(r, "parse user name"); /* Handle unresolved names */ if (*name == '\0') { free(name); name = NULL; } groupnames[i] = name; } } if (sshbuf_len(uidbuf) != 0) fatal_f("unexpected extra username data"); if (sshbuf_len(gidbuf) != 0) fatal_f("unexpected extra groupname data"); sshbuf_free(uidbuf); sshbuf_free(gidbuf); sshbuf_free(msg); /* success */ *usernamesp = usernames; *groupnamesp = groupnames; return 0; } char * -path_append(const char *p1, const char *p2) +sftp_path_append(const char *p1, const char *p2) { char *ret; size_t len = strlen(p1) + strlen(p2) + 2; ret = xmalloc(len); strlcpy(ret, p1, len); if (p1[0] != '\0' && p1[strlen(p1) - 1] != '/') strlcat(ret, "/", len); strlcat(ret, p2, len); return(ret); } /* * Arg p must be dynamically allocated. It will either be returned or * freed and a replacement allocated. Caller must free returned string. */ char * -make_absolute(char *p, const char *pwd) +sftp_make_absolute(char *p, const char *pwd) { char *abs_str; /* Derelativise */ if (p && !path_absolute(p)) { - abs_str = path_append(pwd, p); + abs_str = sftp_path_append(pwd, p); free(p); return(abs_str); } else return(p); } int -remote_is_dir(struct sftp_conn *conn, const char *path) +sftp_remote_is_dir(struct sftp_conn *conn, const char *path) { - Attrib *a; + Attrib a; /* XXX: report errors? */ - if ((a = do_stat(conn, path, 1)) == NULL) + if (sftp_stat(conn, path, 1, &a) != 0) return(0); - if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) + if (!(a.flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) return(0); - return(S_ISDIR(a->perm)); + return S_ISDIR(a.perm); } -int -local_is_dir(const char *path) -{ - struct stat sb; - - /* XXX: report errors? */ - if (stat(path, &sb) == -1) - return(0); - - return(S_ISDIR(sb.st_mode)); -} - /* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */ int -globpath_is_dir(const char *pathname) +sftp_globpath_is_dir(const char *pathname) { size_t l = strlen(pathname); return l > 0 && pathname[l - 1] == '/'; } diff --git a/sftp-client.h b/sftp-client.h index d7deab17e4cb..74cdae7dc687 100644 --- a/sftp-client.h +++ b/sftp-client.h @@ -1,211 +1,207 @@ -/* $OpenBSD: sftp-client.h,v 1.38 2022/09/19 10:43:12 djm Exp $ */ +/* $OpenBSD: sftp-client.h,v 1.39 2023/09/08 05:56:13 djm Exp $ */ /* * Copyright (c) 2001-2004 Damien Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* Client side of SSH2 filexfer protocol */ #ifndef _SFTP_CLIENT_H #define _SFTP_CLIENT_H #ifdef USE_SYSTEM_GLOB # include #else # include "openbsd-compat/glob.h" #endif typedef struct SFTP_DIRENT SFTP_DIRENT; struct SFTP_DIRENT { char *filename; char *longname; Attrib a; }; /* * Used for statvfs responses on the wire from the server, because the * server's native format may be larger than the client's. */ struct sftp_statvfs { u_int64_t f_bsize; u_int64_t f_frsize; u_int64_t f_blocks; u_int64_t f_bfree; u_int64_t f_bavail; u_int64_t f_files; u_int64_t f_ffree; u_int64_t f_favail; u_int64_t f_fsid; u_int64_t f_flag; u_int64_t f_namemax; }; /* Used for limits response on the wire from the server */ struct sftp_limits { u_int64_t packet_length; u_int64_t read_length; u_int64_t write_length; u_int64_t open_handles; }; /* print flag values */ #define SFTP_QUIET 0 /* be quiet during transfers */ #define SFTP_PRINT 1 /* list files and show progress bar */ #define SFTP_PROGRESS_ONLY 2 /* progress bar only */ /* * Initialise a SSH filexfer connection. Returns NULL on error or * a pointer to a initialized sftp_conn struct on success. */ -struct sftp_conn *do_init(int, int, u_int, u_int, u_int64_t); +struct sftp_conn *sftp_init(int, int, u_int, u_int, u_int64_t); u_int sftp_proto_version(struct sftp_conn *); /* Query server limits */ -int do_limits(struct sftp_conn *, struct sftp_limits *); +int sftp_get_limits(struct sftp_conn *, struct sftp_limits *); /* Close file referred to by 'handle' */ -int do_close(struct sftp_conn *, const u_char *, u_int); +int sftp_close(struct sftp_conn *, const u_char *, u_int); /* Read contents of 'path' to NULL-terminated array 'dir' */ -int do_readdir(struct sftp_conn *, const char *, SFTP_DIRENT ***); +int sftp_readdir(struct sftp_conn *, const char *, SFTP_DIRENT ***); -/* Frees a NULL-terminated array of SFTP_DIRENTs (eg. from do_readdir) */ -void free_sftp_dirents(SFTP_DIRENT **); +/* Frees a NULL-terminated array of SFTP_DIRENTs (eg. from sftp_readdir) */ +void sftp_free_dirents(SFTP_DIRENT **); /* Delete file 'path' */ -int do_rm(struct sftp_conn *, const char *); +int sftp_rm(struct sftp_conn *, const char *); /* Create directory 'path' */ -int do_mkdir(struct sftp_conn *, const char *, Attrib *, int); +int sftp_mkdir(struct sftp_conn *, const char *, Attrib *, int); /* Remove directory 'path' */ -int do_rmdir(struct sftp_conn *, const char *); +int sftp_rmdir(struct sftp_conn *, const char *); /* Get file attributes of 'path' (follows symlinks) */ -Attrib *do_stat(struct sftp_conn *, const char *, int); +int sftp_stat(struct sftp_conn *, const char *, int, Attrib *); /* Get file attributes of 'path' (does not follow symlinks) */ -Attrib *do_lstat(struct sftp_conn *, const char *, int); +int sftp_lstat(struct sftp_conn *, const char *, int, Attrib *); /* Set file attributes of 'path' */ -int do_setstat(struct sftp_conn *, const char *, Attrib *); +int sftp_setstat(struct sftp_conn *, const char *, Attrib *); /* Set file attributes of open file 'handle' */ -int do_fsetstat(struct sftp_conn *, const u_char *, u_int, Attrib *); +int sftp_fsetstat(struct sftp_conn *, const u_char *, u_int, Attrib *); /* Set file attributes of 'path', not following symlinks */ -int do_lsetstat(struct sftp_conn *conn, const char *path, Attrib *a); +int sftp_lsetstat(struct sftp_conn *conn, const char *path, Attrib *a); /* Canonicalise 'path' - caller must free result */ -char *do_realpath(struct sftp_conn *, const char *); +char *sftp_realpath(struct sftp_conn *, const char *); /* Canonicalisation with tilde expansion (requires server extension) */ -char *do_expand_path(struct sftp_conn *, const char *); +char *sftp_expand_path(struct sftp_conn *, const char *); /* Returns non-zero if server can tilde-expand paths */ -int can_expand_path(struct sftp_conn *); +int sftp_can_expand_path(struct sftp_conn *); /* Get statistics for filesystem hosting file at "path" */ -int do_statvfs(struct sftp_conn *, const char *, struct sftp_statvfs *, int); +int sftp_statvfs(struct sftp_conn *, const char *, struct sftp_statvfs *, int); /* Rename 'oldpath' to 'newpath' */ -int do_rename(struct sftp_conn *, const char *, const char *, int); +int sftp_rename(struct sftp_conn *, const char *, const char *, int); /* Copy 'oldpath' to 'newpath' */ -int do_copy(struct sftp_conn *, const char *, const char *); +int sftp_copy(struct sftp_conn *, const char *, const char *); /* Link 'oldpath' to 'newpath' */ -int do_hardlink(struct sftp_conn *, const char *, const char *); +int sftp_hardlink(struct sftp_conn *, const char *, const char *); /* Rename 'oldpath' to 'newpath' */ -int do_symlink(struct sftp_conn *, const char *, const char *); +int sftp_symlink(struct sftp_conn *, const char *, const char *); /* Call fsync() on open file 'handle' */ -int do_fsync(struct sftp_conn *conn, u_char *, u_int); +int sftp_fsync(struct sftp_conn *conn, u_char *, u_int); /* * Download 'remote_path' to 'local_path'. Preserve permissions and times * if 'pflag' is set */ -int do_download(struct sftp_conn *, const char *, const char *, Attrib *, +int sftp_download(struct sftp_conn *, const char *, const char *, Attrib *, int, int, int, int); /* * Recursively download 'remote_directory' to 'local_directory'. Preserve * times if 'pflag' is set */ -int download_dir(struct sftp_conn *, const char *, const char *, Attrib *, +int sftp_download_dir(struct sftp_conn *, const char *, const char *, Attrib *, int, int, int, int, int, int); /* * Upload 'local_path' to 'remote_path'. Preserve permissions and times * if 'pflag' is set */ -int do_upload(struct sftp_conn *, const char *, const char *, +int sftp_upload(struct sftp_conn *, const char *, const char *, int, int, int, int); /* * Recursively upload 'local_directory' to 'remote_directory'. Preserve * times if 'pflag' is set */ -int upload_dir(struct sftp_conn *, const char *, const char *, +int sftp_upload_dir(struct sftp_conn *, const char *, const char *, int, int, int, int, int, int); /* * Download a 'from_path' from the 'from' connection and upload it to * to 'to' connection at 'to_path'. */ -int -do_crossload(struct sftp_conn *from, struct sftp_conn *to, +int sftp_crossload(struct sftp_conn *from, struct sftp_conn *to, const char *from_path, const char *to_path, Attrib *a, int preserve_flag); /* * Recursively download a directory from 'from_path' from the 'from' * connection and upload it to 'to' connection at 'to_path'. */ -int crossload_dir(struct sftp_conn *from, struct sftp_conn *to, +int sftp_crossload_dir(struct sftp_conn *from, struct sftp_conn *to, const char *from_path, const char *to_path, Attrib *dirattrib, int preserve_flag, int print_flag, int follow_link_flag); /* * User/group ID to name translation. */ -int can_get_users_groups_by_id(struct sftp_conn *conn); -int do_get_users_groups_by_id(struct sftp_conn *conn, +int sftp_can_get_users_groups_by_id(struct sftp_conn *conn); +int sftp_get_users_groups_by_id(struct sftp_conn *conn, const u_int *uids, u_int nuids, const u_int *gids, u_int ngids, char ***usernamesp, char ***groupnamesp); /* Concatenate paths, taking care of slashes. Caller must free result. */ -char *path_append(const char *, const char *); +char *sftp_path_append(const char *, const char *); /* Make absolute path if relative path and CWD is given. Does not modify * original if the path is already absolute. */ -char *make_absolute(char *, const char *); +char *sftp_make_absolute(char *, const char *); /* Check if remote path is directory */ -int remote_is_dir(struct sftp_conn *conn, const char *path); - -/* Check if local path is directory */ -int local_is_dir(const char *path); +int sftp_remote_is_dir(struct sftp_conn *conn, const char *path); /* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */ -int globpath_is_dir(const char *pathname); +int sftp_globpath_is_dir(const char *pathname); #endif diff --git a/sftp-glob.c b/sftp-glob.c index afeb15f9ec11..1b82759b04d6 100644 --- a/sftp-glob.c +++ b/sftp-glob.c @@ -1,180 +1,180 @@ -/* $OpenBSD: sftp-glob.c,v 1.31 2022/10/24 21:51:55 djm Exp $ */ +/* $OpenBSD: sftp-glob.c,v 1.33 2023/09/10 23:12:32 djm Exp $ */ /* * Copyright (c) 2001-2004 Damien Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "includes.h" #include #ifdef HAVE_SYS_STAT_H # include #endif #include #include #include #include #include "xmalloc.h" #include "sftp.h" #include "sftp-common.h" #include "sftp-client.h" -int remote_glob(struct sftp_conn *, const char *, int, +int sftp_glob(struct sftp_conn *, const char *, int, int (*)(const char *, int), glob_t *); struct SFTP_OPENDIR { SFTP_DIRENT **dir; int offset; }; static struct { struct sftp_conn *conn; } cur; static void * fudge_opendir(const char *path) { struct SFTP_OPENDIR *r; r = xcalloc(1, sizeof(*r)); - if (do_readdir(cur.conn, path, &r->dir)) { + if (sftp_readdir(cur.conn, path, &r->dir)) { free(r); return(NULL); } r->offset = 0; return((void *)r); } static struct dirent * fudge_readdir(struct SFTP_OPENDIR *od) { /* Solaris needs sizeof(dirent) + path length (see below) */ static char buf[sizeof(struct dirent) + MAXPATHLEN]; struct dirent *ret = (struct dirent *)buf; #ifdef __GNU_LIBRARY__ static int inum = 1; #endif /* __GNU_LIBRARY__ */ if (od->dir[od->offset] == NULL) return(NULL); memset(buf, 0, sizeof(buf)); /* * Solaris defines dirent->d_name as a one byte array and expects * you to hack around it. */ #ifdef BROKEN_ONE_BYTE_DIRENT_D_NAME strlcpy(ret->d_name, od->dir[od->offset++]->filename, MAXPATHLEN); #else strlcpy(ret->d_name, od->dir[od->offset++]->filename, sizeof(ret->d_name)); #endif #ifdef __GNU_LIBRARY__ /* * Idiot glibc uses extensions to struct dirent for readdir with * ALTDIRFUNCs. Not that this is documented anywhere but the * source... Fake an inode number to appease it. */ ret->d_ino = inum++; if (!inum) inum = 1; #endif /* __GNU_LIBRARY__ */ return(ret); } static void fudge_closedir(struct SFTP_OPENDIR *od) { - free_sftp_dirents(od->dir); + sftp_free_dirents(od->dir); free(od); } static int fudge_lstat(const char *path, struct stat *st) { - Attrib *a; + Attrib a; - if (!(a = do_lstat(cur.conn, path, 1))) - return(-1); + if (sftp_lstat(cur.conn, path, 1, &a) != 0) + return -1; - attrib_to_stat(a, st); + attrib_to_stat(&a, st); - return(0); + return 0; } static int fudge_stat(const char *path, struct stat *st) { - Attrib *a; + Attrib a; - if (!(a = do_stat(cur.conn, path, 1))) - return(-1); + if (sftp_stat(cur.conn, path, 1, &a) != 0) + return -1; - attrib_to_stat(a, st); + attrib_to_stat(&a, st); return(0); } int -remote_glob(struct sftp_conn *conn, const char *pattern, int flags, +sftp_glob(struct sftp_conn *conn, const char *pattern, int flags, int (*errfunc)(const char *, int), glob_t *pglob) { int r; size_t l; char *s; struct stat sb; pglob->gl_opendir = fudge_opendir; pglob->gl_readdir = (struct dirent *(*)(void *))fudge_readdir; pglob->gl_closedir = (void (*)(void *))fudge_closedir; pglob->gl_lstat = fudge_lstat; pglob->gl_stat = fudge_stat; memset(&cur, 0, sizeof(cur)); cur.conn = conn; if ((r = glob(pattern, flags | GLOB_ALTDIRFUNC, errfunc, pglob)) != 0) return r; /* * When both GLOB_NOCHECK and GLOB_MARK are active, a single gl_pathv * entry has been returned and that entry has not already been marked, * then check whether it needs a '/' appended as a directory mark. * * This ensures that a NOCHECK result is annotated as a directory. * The glob(3) spec doesn't promise to mark NOCHECK entries, but doing * it simplifies our callers (sftp/scp) considerably. * * XXX doesn't try to handle gl_offs. */ if ((flags & (GLOB_NOCHECK|GLOB_MARK)) == (GLOB_NOCHECK|GLOB_MARK) && pglob->gl_matchc == 0 && pglob->gl_offs == 0 && pglob->gl_pathc == 1 && (s = pglob->gl_pathv[0]) != NULL && (l = strlen(s)) > 0 && s[l-1] != '/') { if (fudge_stat(s, &sb) == 0 && S_ISDIR(sb.st_mode)) { /* NOCHECK on a directory; annotate */ if ((s = realloc(s, l + 2)) != NULL) { memcpy(s + l, "/", 2); pglob->gl_pathv[0] = s; } } } return 0; } diff --git a/sftp-usergroup.c b/sftp-usergroup.c index 083930a4a327..93396ffc63db 100644 --- a/sftp-usergroup.c +++ b/sftp-usergroup.c @@ -1,239 +1,239 @@ /* * Copyright (c) 2022 Damien Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* sftp client user/group lookup and caching */ #include "includes.h" #include #include #include #include #include #include "log.h" #include "xmalloc.h" #include "sftp-common.h" #include "sftp-client.h" #include "sftp-usergroup.h" /* Tree of id, name */ struct idname { u_int id; char *name; RB_ENTRY(idname) entry; /* XXX implement bounded cache as TAILQ */ }; static int idname_cmp(struct idname *a, struct idname *b) { if (a->id == b->id) return 0; return a->id > b->id ? 1 : -1; } RB_HEAD(idname_tree, idname); RB_GENERATE_STATIC(idname_tree, idname, entry, idname_cmp) static struct idname_tree user_idname = RB_INITIALIZER(&user_idname); static struct idname_tree group_idname = RB_INITIALIZER(&group_idname); static void idname_free(struct idname *idname) { if (idname == NULL) return; free(idname->name); free(idname); } static void idname_enter(struct idname_tree *tree, u_int id, const char *name) { struct idname *idname; if ((idname = xcalloc(1, sizeof(*idname))) == NULL) fatal_f("alloc"); idname->id = id; idname->name = xstrdup(name); if (RB_INSERT(idname_tree, tree, idname) != NULL) idname_free(idname); } static const char * idname_lookup(struct idname_tree *tree, u_int id) { struct idname idname, *found; memset(&idname, 0, sizeof(idname)); idname.id = id; if ((found = RB_FIND(idname_tree, tree, &idname)) != NULL) return found->name; return NULL; } static void freenames(char **names, u_int nnames) { u_int i; if (names == NULL) return; for (i = 0; i < nnames; i++) free(names[i]); free(names); } static void lookup_and_record(struct sftp_conn *conn, u_int *uids, u_int nuids, u_int *gids, u_int ngids) { int r; u_int i; char **usernames = NULL, **groupnames = NULL; - if ((r = do_get_users_groups_by_id(conn, uids, nuids, gids, ngids, + if ((r = sftp_get_users_groups_by_id(conn, uids, nuids, gids, ngids, &usernames, &groupnames)) != 0) { - debug_fr(r, "do_get_users_groups_by_id"); + debug_fr(r, "sftp_get_users_groups_by_id"); return; } for (i = 0; i < nuids; i++) { if (usernames[i] == NULL) { debug3_f("uid %u not resolved", uids[i]); continue; } debug3_f("record uid %u => \"%s\"", uids[i], usernames[i]); idname_enter(&user_idname, uids[i], usernames[i]); } for (i = 0; i < ngids; i++) { if (groupnames[i] == NULL) { debug3_f("gid %u not resolved", gids[i]); continue; } debug3_f("record gid %u => \"%s\"", gids[i], groupnames[i]); idname_enter(&group_idname, gids[i], groupnames[i]); } freenames(usernames, nuids); freenames(groupnames, ngids); } static int has_id(u_int id, u_int *ids, u_int nids) { u_int i; if (nids == 0) return 0; /* XXX O(N^2) */ for (i = 0; i < nids; i++) { if (ids[i] == id) break; } return i < nids; } static void collect_ids_from_glob(glob_t *g, int user, u_int **idsp, u_int *nidsp) { u_int id, i, n = 0, *ids = NULL; for (i = 0; g->gl_pathv[i] != NULL; i++) { if (user) { if (ruser_name(g->gl_statv[i]->st_uid) != NULL) continue; /* Already seen */ id = (u_int)g->gl_statv[i]->st_uid; } else { if (rgroup_name(g->gl_statv[i]->st_gid) != NULL) continue; /* Already seen */ id = (u_int)g->gl_statv[i]->st_gid; } if (has_id(id, ids, n)) continue; ids = xrecallocarray(ids, n, n + 1, sizeof(*ids)); ids[n++] = id; } *idsp = ids; *nidsp = n; } void get_remote_user_groups_from_glob(struct sftp_conn *conn, glob_t *g) { u_int *uids = NULL, nuids = 0, *gids = NULL, ngids = 0; - if (!can_get_users_groups_by_id(conn)) + if (!sftp_can_get_users_groups_by_id(conn)) return; collect_ids_from_glob(g, 1, &uids, &nuids); collect_ids_from_glob(g, 0, &gids, &ngids); lookup_and_record(conn, uids, nuids, gids, ngids); free(uids); free(gids); } static void collect_ids_from_dirents(SFTP_DIRENT **d, int user, u_int **idsp, u_int *nidsp) { u_int id, i, n = 0, *ids = NULL; for (i = 0; d[i] != NULL; i++) { if (user) { if (ruser_name((uid_t)(d[i]->a.uid)) != NULL) continue; /* Already seen */ id = d[i]->a.uid; } else { if (rgroup_name((gid_t)(d[i]->a.gid)) != NULL) continue; /* Already seen */ id = d[i]->a.gid; } if (has_id(id, ids, n)) continue; ids = xrecallocarray(ids, n, n + 1, sizeof(*ids)); ids[n++] = id; } *idsp = ids; *nidsp = n; } void get_remote_user_groups_from_dirents(struct sftp_conn *conn, SFTP_DIRENT **d) { u_int *uids = NULL, nuids = 0, *gids = NULL, ngids = 0; - if (!can_get_users_groups_by_id(conn)) + if (!sftp_can_get_users_groups_by_id(conn)) return; collect_ids_from_dirents(d, 1, &uids, &nuids); collect_ids_from_dirents(d, 0, &gids, &ngids); lookup_and_record(conn, uids, nuids, gids, ngids); free(uids); free(gids); } const char * ruser_name(uid_t uid) { return idname_lookup(&user_idname, (u_int)uid); } const char * rgroup_name(uid_t gid) { return idname_lookup(&group_idname, (u_int)gid); } diff --git a/sftp.c b/sftp.c index b113f930973d..c609b4153d79 100644 --- a/sftp.c +++ b/sftp.c @@ -1,2684 +1,2696 @@ -/* $OpenBSD: sftp.c,v 1.234 2023/04/12 08:53:54 jsg Exp $ */ +/* $OpenBSD: sftp.c,v 1.236 2023/09/10 23:12:32 djm Exp $ */ /* * Copyright (c) 2001-2004 Damien Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "includes.h" #include #include #ifdef HAVE_SYS_STAT_H # include #endif #include #include #ifdef HAVE_SYS_STATVFS_H #include #endif #include #include #ifdef HAVE_PATHS_H # include #endif #ifdef HAVE_LIBGEN_H #include #endif #ifdef HAVE_LOCALE_H # include #endif #ifdef USE_LIBEDIT #include #else typedef void EditLine; #endif #include #include #include #include #include #include #include #ifdef HAVE_UTIL_H # include #endif #include "xmalloc.h" #include "log.h" #include "pathnames.h" #include "misc.h" #include "utf8.h" #include "sftp.h" #include "ssherr.h" #include "sshbuf.h" #include "sftp-common.h" #include "sftp-client.h" #include "sftp-usergroup.h" /* File to read commands from */ FILE* infile; /* Are we in batchfile mode? */ int batchmode = 0; /* PID of ssh transport process */ static volatile pid_t sshpid = -1; /* Suppress diagnostic messages */ int quiet = 0; /* This is set to 0 if the progressmeter is not desired. */ int showprogress = 1; /* When this option is set, we always recursively download/upload directories */ int global_rflag = 0; /* When this option is set, we resume download or upload if possible */ int global_aflag = 0; /* When this option is set, the file transfers will always preserve times */ int global_pflag = 0; /* When this option is set, transfers will have fsync() called on each file */ int global_fflag = 0; /* SIGINT received during command processing */ volatile sig_atomic_t interrupted = 0; /* I wish qsort() took a separate ctx for the comparison function...*/ int sort_flag; glob_t *sort_glob; /* Context used for commandline completion */ struct complete_ctx { struct sftp_conn *conn; char **remote_pathp; }; -int remote_glob(struct sftp_conn *, const char *, int, +int sftp_glob(struct sftp_conn *, const char *, int, int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */ extern char *__progname; /* Separators for interactive commands */ #define WHITESPACE " \t\r\n" /* ls flags */ #define LS_LONG_VIEW 0x0001 /* Full view ala ls -l */ #define LS_SHORT_VIEW 0x0002 /* Single row view ala ls -1 */ #define LS_NUMERIC_VIEW 0x0004 /* Long view with numeric uid/gid */ #define LS_NAME_SORT 0x0008 /* Sort by name (default) */ #define LS_TIME_SORT 0x0010 /* Sort by mtime */ #define LS_SIZE_SORT 0x0020 /* Sort by file size */ #define LS_REVERSE_SORT 0x0040 /* Reverse sort order */ #define LS_SHOW_ALL 0x0080 /* Don't skip filenames starting with '.' */ #define LS_SI_UNITS 0x0100 /* Display sizes as K, M, G, etc. */ #define VIEW_FLAGS (LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW|LS_SI_UNITS) #define SORT_FLAGS (LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT) /* Commands for interactive mode */ enum sftp_command { I_CHDIR = 1, I_CHGRP, I_CHMOD, I_CHOWN, I_COPY, I_DF, I_GET, I_HELP, I_LCHDIR, I_LINK, I_LLS, I_LMKDIR, I_LPWD, I_LS, I_LUMASK, I_MKDIR, I_PUT, I_PWD, I_QUIT, I_REGET, I_RENAME, I_REPUT, I_RM, I_RMDIR, I_SHELL, I_SYMLINK, I_VERSION, I_PROGRESS, }; struct CMD { const char *c; const int n; const int t; /* Completion type for the first argument */ const int t2; /* completion type for the optional second argument */ }; /* Type of completion */ #define NOARGS 0 #define REMOTE 1 #define LOCAL 2 static const struct CMD cmds[] = { { "bye", I_QUIT, NOARGS, NOARGS }, { "cd", I_CHDIR, REMOTE, NOARGS }, { "chdir", I_CHDIR, REMOTE, NOARGS }, { "chgrp", I_CHGRP, REMOTE, NOARGS }, { "chmod", I_CHMOD, REMOTE, NOARGS }, { "chown", I_CHOWN, REMOTE, NOARGS }, { "copy", I_COPY, REMOTE, LOCAL }, { "cp", I_COPY, REMOTE, LOCAL }, { "df", I_DF, REMOTE, NOARGS }, { "dir", I_LS, REMOTE, NOARGS }, { "exit", I_QUIT, NOARGS, NOARGS }, { "get", I_GET, REMOTE, LOCAL }, { "help", I_HELP, NOARGS, NOARGS }, { "lcd", I_LCHDIR, LOCAL, NOARGS }, { "lchdir", I_LCHDIR, LOCAL, NOARGS }, { "lls", I_LLS, LOCAL, NOARGS }, { "lmkdir", I_LMKDIR, LOCAL, NOARGS }, { "ln", I_LINK, REMOTE, REMOTE }, { "lpwd", I_LPWD, LOCAL, NOARGS }, { "ls", I_LS, REMOTE, NOARGS }, { "lumask", I_LUMASK, NOARGS, NOARGS }, { "mkdir", I_MKDIR, REMOTE, NOARGS }, { "mget", I_GET, REMOTE, LOCAL }, { "mput", I_PUT, LOCAL, REMOTE }, { "progress", I_PROGRESS, NOARGS, NOARGS }, { "put", I_PUT, LOCAL, REMOTE }, { "pwd", I_PWD, REMOTE, NOARGS }, { "quit", I_QUIT, NOARGS, NOARGS }, { "reget", I_REGET, REMOTE, LOCAL }, { "rename", I_RENAME, REMOTE, REMOTE }, { "reput", I_REPUT, LOCAL, REMOTE }, { "rm", I_RM, REMOTE, NOARGS }, { "rmdir", I_RMDIR, REMOTE, NOARGS }, { "symlink", I_SYMLINK, REMOTE, REMOTE }, { "version", I_VERSION, NOARGS, NOARGS }, { "!", I_SHELL, NOARGS, NOARGS }, { "?", I_HELP, NOARGS, NOARGS }, { NULL, -1, -1, -1 } }; static void killchild(int signo) { pid_t pid; pid = sshpid; if (pid > 1) { kill(pid, SIGTERM); (void)waitpid(pid, NULL, 0); } _exit(1); } static void suspchild(int signo) { if (sshpid > 1) { kill(sshpid, signo); while (waitpid(sshpid, NULL, WUNTRACED) == -1 && errno == EINTR) continue; } kill(getpid(), SIGSTOP); } static void cmd_interrupt(int signo) { const char msg[] = "\rInterrupt \n"; int olderrno = errno; (void)write(STDERR_FILENO, msg, sizeof(msg) - 1); interrupted = 1; errno = olderrno; } static void read_interrupt(int signo) { interrupted = 1; } static void sigchld_handler(int sig) { int save_errno = errno; pid_t pid; const char msg[] = "\rConnection closed. \n"; /* Report if ssh transport process dies. */ while ((pid = waitpid(sshpid, NULL, WNOHANG)) == -1 && errno == EINTR) continue; if (pid == sshpid) { if (!quiet) (void)write(STDERR_FILENO, msg, sizeof(msg) - 1); sshpid = -1; } errno = save_errno; } static void help(void) { printf("Available commands:\n" "bye Quit sftp\n" "cd path Change remote directory to 'path'\n" "chgrp [-h] grp path Change group of file 'path' to 'grp'\n" "chmod [-h] mode path Change permissions of file 'path' to 'mode'\n" "chown [-h] own path Change owner of file 'path' to 'own'\n" "copy oldpath newpath Copy remote file\n" "cp oldpath newpath Copy remote file\n" "df [-hi] [path] Display statistics for current directory or\n" " filesystem containing 'path'\n" "exit Quit sftp\n" "get [-afpR] remote [local] Download file\n" "help Display this help text\n" "lcd path Change local directory to 'path'\n" "lls [ls-options [path]] Display local directory listing\n" "lmkdir path Create local directory\n" "ln [-s] oldpath newpath Link remote file (-s for symlink)\n" "lpwd Print local working directory\n" "ls [-1afhlnrSt] [path] Display remote directory listing\n" "lumask umask Set local umask to 'umask'\n" "mkdir path Create remote directory\n" "progress Toggle display of progress meter\n" "put [-afpR] local [remote] Upload file\n" "pwd Display remote working directory\n" "quit Quit sftp\n" "reget [-fpR] remote [local] Resume download file\n" "rename oldpath newpath Rename remote file\n" "reput [-fpR] local [remote] Resume upload file\n" "rm path Delete remote file\n" "rmdir path Remove remote directory\n" "symlink oldpath newpath Symlink remote file\n" "version Show SFTP version\n" "!command Execute 'command' in local shell\n" "! Escape to local shell\n" "? Synonym for help\n"); } static void local_do_shell(const char *args) { int status; char *shell; pid_t pid; if (!*args) args = NULL; if ((shell = getenv("SHELL")) == NULL || *shell == '\0') shell = _PATH_BSHELL; if ((pid = fork()) == -1) fatal("Couldn't fork: %s", strerror(errno)); if (pid == 0) { /* XXX: child has pipe fds to ssh subproc open - issue? */ if (args) { debug3("Executing %s -c \"%s\"", shell, args); execl(shell, shell, "-c", args, (char *)NULL); } else { debug3("Executing %s", shell); execl(shell, shell, (char *)NULL); } fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell, strerror(errno)); _exit(1); } while (waitpid(pid, &status, 0) == -1) if (errno != EINTR) fatal("Couldn't wait for child: %s", strerror(errno)); if (!WIFEXITED(status)) error("Shell exited abnormally"); else if (WEXITSTATUS(status)) error("Shell exited with status %d", WEXITSTATUS(status)); } static void local_do_ls(const char *args) { if (!args || !*args) local_do_shell(_PATH_LS); else { int len = strlen(_PATH_LS " ") + strlen(args) + 1; char *buf = xmalloc(len); /* XXX: quoting - rip quoting code from ftp? */ snprintf(buf, len, _PATH_LS " %s", args); local_do_shell(buf); free(buf); } } /* Strip one path (usually the pwd) from the start of another */ static char * path_strip(const char *path, const char *strip) { size_t len; if (strip == NULL) return (xstrdup(path)); len = strlen(strip); if (strncmp(path, strip, len) == 0) { if (strip[len - 1] != '/' && path[len] == '/') len++; return (xstrdup(path + len)); } return (xstrdup(path)); } static int parse_getput_flags(const char *cmd, char **argv, int argc, int *aflag, int *fflag, int *pflag, int *rflag) { extern int opterr, optind, optopt, optreset; int ch; optind = optreset = 1; opterr = 0; *aflag = *fflag = *rflag = *pflag = 0; while ((ch = getopt(argc, argv, "afPpRr")) != -1) { switch (ch) { case 'a': *aflag = 1; break; case 'f': *fflag = 1; break; case 'p': case 'P': *pflag = 1; break; case 'r': case 'R': *rflag = 1; break; default: error("%s: Invalid flag -%c", cmd, optopt); return -1; } } return optind; } static int parse_link_flags(const char *cmd, char **argv, int argc, int *sflag) { extern int opterr, optind, optopt, optreset; int ch; optind = optreset = 1; opterr = 0; *sflag = 0; while ((ch = getopt(argc, argv, "s")) != -1) { switch (ch) { case 's': *sflag = 1; break; default: error("%s: Invalid flag -%c", cmd, optopt); return -1; } } return optind; } static int parse_rename_flags(const char *cmd, char **argv, int argc, int *lflag) { extern int opterr, optind, optopt, optreset; int ch; optind = optreset = 1; opterr = 0; *lflag = 0; while ((ch = getopt(argc, argv, "l")) != -1) { switch (ch) { case 'l': *lflag = 1; break; default: error("%s: Invalid flag -%c", cmd, optopt); return -1; } } return optind; } static int parse_ls_flags(char **argv, int argc, int *lflag) { extern int opterr, optind, optopt, optreset; int ch; optind = optreset = 1; opterr = 0; *lflag = LS_NAME_SORT; while ((ch = getopt(argc, argv, "1Safhlnrt")) != -1) { switch (ch) { case '1': *lflag &= ~VIEW_FLAGS; *lflag |= LS_SHORT_VIEW; break; case 'S': *lflag &= ~SORT_FLAGS; *lflag |= LS_SIZE_SORT; break; case 'a': *lflag |= LS_SHOW_ALL; break; case 'f': *lflag &= ~SORT_FLAGS; break; case 'h': *lflag |= LS_SI_UNITS; break; case 'l': *lflag &= ~LS_SHORT_VIEW; *lflag |= LS_LONG_VIEW; break; case 'n': *lflag &= ~LS_SHORT_VIEW; *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW; break; case 'r': *lflag |= LS_REVERSE_SORT; break; case 't': *lflag &= ~SORT_FLAGS; *lflag |= LS_TIME_SORT; break; default: error("ls: Invalid flag -%c", optopt); return -1; } } return optind; } static int parse_df_flags(const char *cmd, char **argv, int argc, int *hflag, int *iflag) { extern int opterr, optind, optopt, optreset; int ch; optind = optreset = 1; opterr = 0; *hflag = *iflag = 0; while ((ch = getopt(argc, argv, "hi")) != -1) { switch (ch) { case 'h': *hflag = 1; break; case 'i': *iflag = 1; break; default: error("%s: Invalid flag -%c", cmd, optopt); return -1; } } return optind; } static int parse_ch_flags(const char *cmd, char **argv, int argc, int *hflag) { extern int opterr, optind, optopt, optreset; int ch; optind = optreset = 1; opterr = 0; *hflag = 0; while ((ch = getopt(argc, argv, "h")) != -1) { switch (ch) { case 'h': *hflag = 1; break; default: error("%s: Invalid flag -%c", cmd, optopt); return -1; } } return optind; } static int parse_no_flags(const char *cmd, char **argv, int argc) { extern int opterr, optind, optopt, optreset; int ch; optind = optreset = 1; opterr = 0; while ((ch = getopt(argc, argv, "")) != -1) { switch (ch) { default: error("%s: Invalid flag -%c", cmd, optopt); return -1; } } return optind; } static char * escape_glob(const char *s) { size_t i, o, len; char *ret; len = strlen(s); ret = xcalloc(2, len + 1); for (i = o = 0; i < len; i++) { if (strchr("[]?*\\", s[i]) != NULL) ret[o++] = '\\'; ret[o++] = s[i]; } ret[o++] = '\0'; return ret; } /* * Arg p must be dynamically allocated. make_absolute will either return it * or free it and allocate a new one. Caller must free returned string. */ static char * make_absolute_pwd_glob(char *p, const char *pwd) { char *ret, *escpwd; escpwd = escape_glob(pwd); if (p == NULL) return escpwd; - ret = make_absolute(p, escpwd); + ret = sftp_make_absolute(p, escpwd); free(escpwd); return ret; } +static int +local_is_dir(const char *path) +{ + struct stat sb; + + if (stat(path, &sb) == -1) + return 0; + return S_ISDIR(sb.st_mode); +} + static int process_get(struct sftp_conn *conn, const char *src, const char *dst, const char *pwd, int pflag, int rflag, int resume, int fflag) { char *filename, *abs_src = NULL, *abs_dst = NULL, *tmp = NULL; glob_t g; int i, r, err = 0; abs_src = make_absolute_pwd_glob(xstrdup(src), pwd); memset(&g, 0, sizeof(g)); debug3("Looking up %s", abs_src); - if ((r = remote_glob(conn, abs_src, GLOB_MARK, NULL, &g)) != 0) { + if ((r = sftp_glob(conn, abs_src, GLOB_MARK, NULL, &g)) != 0) { if (r == GLOB_NOSPACE) { error("Too many matches for \"%s\".", abs_src); } else { error("File \"%s\" not found.", abs_src); } err = -1; goto out; } /* * If multiple matches then dst must be a directory or * unspecified. */ if (g.gl_matchc > 1 && dst != NULL && !local_is_dir(dst)) { error("Multiple source paths, but destination " "\"%s\" is not a directory", dst); err = -1; goto out; } for (i = 0; g.gl_pathv[i] && !interrupted; i++) { tmp = xstrdup(g.gl_pathv[i]); if ((filename = basename(tmp)) == NULL) { error("basename %s: %s", tmp, strerror(errno)); free(tmp); err = -1; goto out; } if (g.gl_matchc == 1 && dst) { if (local_is_dir(dst)) { - abs_dst = path_append(dst, filename); + abs_dst = sftp_path_append(dst, filename); } else { abs_dst = xstrdup(dst); } } else if (dst) { - abs_dst = path_append(dst, filename); + abs_dst = sftp_path_append(dst, filename); } else { abs_dst = xstrdup(filename); } free(tmp); resume |= global_aflag; if (!quiet && resume) mprintf("Resuming %s to %s\n", g.gl_pathv[i], abs_dst); else if (!quiet && !resume) mprintf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst); /* XXX follow link flag */ - if (globpath_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) { - if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL, - pflag || global_pflag, 1, resume, + if (sftp_globpath_is_dir(g.gl_pathv[i]) && + (rflag || global_rflag)) { + if (sftp_download_dir(conn, g.gl_pathv[i], abs_dst, + NULL, pflag || global_pflag, 1, resume, fflag || global_fflag, 0, 0) == -1) err = -1; } else { - if (do_download(conn, g.gl_pathv[i], abs_dst, NULL, + if (sftp_download(conn, g.gl_pathv[i], abs_dst, NULL, pflag || global_pflag, resume, fflag || global_fflag, 0) == -1) err = -1; } free(abs_dst); abs_dst = NULL; } out: free(abs_src); globfree(&g); return(err); } static int process_put(struct sftp_conn *conn, const char *src, const char *dst, const char *pwd, int pflag, int rflag, int resume, int fflag) { char *tmp_dst = NULL; char *abs_dst = NULL; char *tmp = NULL, *filename = NULL; glob_t g; int err = 0; int i, dst_is_dir = 1; struct stat sb; if (dst) { tmp_dst = xstrdup(dst); - tmp_dst = make_absolute(tmp_dst, pwd); + tmp_dst = sftp_make_absolute(tmp_dst, pwd); } memset(&g, 0, sizeof(g)); debug3("Looking up %s", src); if (glob(src, GLOB_NOCHECK | GLOB_MARK, NULL, &g)) { error("File \"%s\" not found.", src); err = -1; goto out; } /* If we aren't fetching to pwd then stash this status for later */ if (tmp_dst != NULL) - dst_is_dir = remote_is_dir(conn, tmp_dst); + dst_is_dir = sftp_remote_is_dir(conn, tmp_dst); /* If multiple matches, dst may be directory or unspecified */ if (g.gl_matchc > 1 && tmp_dst && !dst_is_dir) { error("Multiple paths match, but destination " "\"%s\" is not a directory", tmp_dst); err = -1; goto out; } for (i = 0; g.gl_pathv[i] && !interrupted; i++) { if (stat(g.gl_pathv[i], &sb) == -1) { err = -1; error("stat %s: %s", g.gl_pathv[i], strerror(errno)); continue; } tmp = xstrdup(g.gl_pathv[i]); if ((filename = basename(tmp)) == NULL) { error("basename %s: %s", tmp, strerror(errno)); free(tmp); err = -1; goto out; } free(abs_dst); abs_dst = NULL; if (g.gl_matchc == 1 && tmp_dst) { /* If directory specified, append filename */ if (dst_is_dir) - abs_dst = path_append(tmp_dst, filename); + abs_dst = sftp_path_append(tmp_dst, filename); else abs_dst = xstrdup(tmp_dst); } else if (tmp_dst) { - abs_dst = path_append(tmp_dst, filename); + abs_dst = sftp_path_append(tmp_dst, filename); } else { - abs_dst = make_absolute(xstrdup(filename), pwd); + abs_dst = sftp_make_absolute(xstrdup(filename), pwd); } free(tmp); resume |= global_aflag; if (!quiet && resume) mprintf("Resuming upload of %s to %s\n", g.gl_pathv[i], abs_dst); else if (!quiet && !resume) mprintf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst); /* XXX follow_link_flag */ - if (globpath_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) { - if (upload_dir(conn, g.gl_pathv[i], abs_dst, + if (sftp_globpath_is_dir(g.gl_pathv[i]) && + (rflag || global_rflag)) { + if (sftp_upload_dir(conn, g.gl_pathv[i], abs_dst, pflag || global_pflag, 1, resume, fflag || global_fflag, 0, 0) == -1) err = -1; } else { - if (do_upload(conn, g.gl_pathv[i], abs_dst, + if (sftp_upload(conn, g.gl_pathv[i], abs_dst, pflag || global_pflag, resume, fflag || global_fflag, 0) == -1) err = -1; } } out: free(abs_dst); free(tmp_dst); globfree(&g); return(err); } static int sdirent_comp(const void *aa, const void *bb) { SFTP_DIRENT *a = *(SFTP_DIRENT **)aa; SFTP_DIRENT *b = *(SFTP_DIRENT **)bb; int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1; #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1)) if (sort_flag & LS_NAME_SORT) return (rmul * strcmp(a->filename, b->filename)); else if (sort_flag & LS_TIME_SORT) return (rmul * NCMP(a->a.mtime, b->a.mtime)); else if (sort_flag & LS_SIZE_SORT) return (rmul * NCMP(a->a.size, b->a.size)); fatal("Unknown ls sort type"); } /* sftp ls.1 replacement for directories */ static int do_ls_dir(struct sftp_conn *conn, const char *path, const char *strip_path, int lflag) { int n; u_int c = 1, colspace = 0, columns = 1; SFTP_DIRENT **d; - if ((n = do_readdir(conn, path, &d)) != 0) + if ((n = sftp_readdir(conn, path, &d)) != 0) return (n); if (!(lflag & LS_SHORT_VIEW)) { u_int m = 0, width = 80; struct winsize ws; char *tmp; /* Count entries for sort and find longest filename */ for (n = 0; d[n] != NULL; n++) { if (d[n]->filename[0] != '.' || (lflag & LS_SHOW_ALL)) m = MAXIMUM(m, strlen(d[n]->filename)); } /* Add any subpath that also needs to be counted */ tmp = path_strip(path, strip_path); m += strlen(tmp); free(tmp); if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1) width = ws.ws_col; columns = width / (m + 2); columns = MAXIMUM(columns, 1); colspace = width / columns; colspace = MINIMUM(colspace, width); } if (lflag & SORT_FLAGS) { for (n = 0; d[n] != NULL; n++) ; /* count entries */ sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT); qsort(d, n, sizeof(*d), sdirent_comp); } get_remote_user_groups_from_dirents(conn, d); for (n = 0; d[n] != NULL && !interrupted; n++) { char *tmp, *fname; if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL)) continue; - tmp = path_append(path, d[n]->filename); + tmp = sftp_path_append(path, d[n]->filename); fname = path_strip(tmp, strip_path); free(tmp); if (lflag & LS_LONG_VIEW) { if ((lflag & (LS_NUMERIC_VIEW|LS_SI_UNITS)) != 0 || - can_get_users_groups_by_id(conn)) { + sftp_can_get_users_groups_by_id(conn)) { char *lname; struct stat sb; memset(&sb, 0, sizeof(sb)); attrib_to_stat(&d[n]->a, &sb); lname = ls_file(fname, &sb, 1, (lflag & LS_SI_UNITS), ruser_name(sb.st_uid), rgroup_name(sb.st_gid)); mprintf("%s\n", lname); free(lname); } else mprintf("%s\n", d[n]->longname); } else { mprintf("%-*s", colspace, fname); if (c >= columns) { printf("\n"); c = 1; } else c++; } free(fname); } if (!(lflag & LS_LONG_VIEW) && (c != 1)) printf("\n"); - free_sftp_dirents(d); + sftp_free_dirents(d); return (0); } static int sglob_comp(const void *aa, const void *bb) { u_int a = *(const u_int *)aa; u_int b = *(const u_int *)bb; const char *ap = sort_glob->gl_pathv[a]; const char *bp = sort_glob->gl_pathv[b]; const struct stat *as = sort_glob->gl_statv[a]; const struct stat *bs = sort_glob->gl_statv[b]; int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1; #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1)) if (sort_flag & LS_NAME_SORT) return (rmul * strcmp(ap, bp)); else if (sort_flag & LS_TIME_SORT) { #if defined(HAVE_STRUCT_STAT_ST_MTIM) if (timespeccmp(&as->st_mtim, &bs->st_mtim, ==)) return 0; return timespeccmp(&as->st_mtim, &bs->st_mtim, <) ? rmul : -rmul; #elif defined(HAVE_STRUCT_STAT_ST_MTIME) return (rmul * NCMP(as->st_mtime, bs->st_mtime)); #else return rmul * 1; #endif } else if (sort_flag & LS_SIZE_SORT) return (rmul * NCMP(as->st_size, bs->st_size)); fatal("Unknown ls sort type"); } /* sftp ls.1 replacement which handles path globs */ static int do_globbed_ls(struct sftp_conn *conn, const char *path, const char *strip_path, int lflag) { char *fname, *lname; glob_t g; int err, r; struct winsize ws; u_int i, j, nentries, *indices = NULL, c = 1; u_int colspace = 0, columns = 1, m = 0, width = 80; memset(&g, 0, sizeof(g)); - if ((r = remote_glob(conn, path, + if ((r = sftp_glob(conn, path, GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE|GLOB_KEEPSTAT|GLOB_NOSORT, NULL, &g)) != 0 || (g.gl_pathc && !g.gl_matchc)) { if (g.gl_pathc) globfree(&g); if (r == GLOB_NOSPACE) { error("Can't ls: Too many matches for \"%s\"", path); } else { error("Can't ls: \"%s\" not found", path); } return -1; } if (interrupted) goto out; /* * If the glob returns a single match and it is a directory, * then just list its contents. */ if (g.gl_matchc == 1 && g.gl_statv[0] != NULL && S_ISDIR(g.gl_statv[0]->st_mode)) { err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag); globfree(&g); return err; } if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1) width = ws.ws_col; if (!(lflag & LS_SHORT_VIEW)) { /* Count entries for sort and find longest filename */ for (i = 0; g.gl_pathv[i]; i++) m = MAXIMUM(m, strlen(g.gl_pathv[i])); columns = width / (m + 2); columns = MAXIMUM(columns, 1); colspace = width / columns; } /* * Sorting: rather than mess with the contents of glob_t, prepare * an array of indices into it and sort that. For the usual * unsorted case, the indices are just the identity 1=1, 2=2, etc. */ for (nentries = 0; g.gl_pathv[nentries] != NULL; nentries++) ; /* count entries */ indices = xcalloc(nentries, sizeof(*indices)); for (i = 0; i < nentries; i++) indices[i] = i; if (lflag & SORT_FLAGS) { sort_glob = &g; sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT); qsort(indices, nentries, sizeof(*indices), sglob_comp); sort_glob = NULL; } get_remote_user_groups_from_glob(conn, &g); for (j = 0; j < nentries && !interrupted; j++) { i = indices[j]; fname = path_strip(g.gl_pathv[i], strip_path); if (lflag & LS_LONG_VIEW) { if (g.gl_statv[i] == NULL) { error("no stat information for %s", fname); free(fname); continue; } lname = ls_file(fname, g.gl_statv[i], 1, (lflag & LS_SI_UNITS), ruser_name(g.gl_statv[i]->st_uid), rgroup_name(g.gl_statv[i]->st_gid)); mprintf("%s\n", lname); free(lname); } else { mprintf("%-*s", colspace, fname); if (c >= columns) { printf("\n"); c = 1; } else c++; } free(fname); } if (!(lflag & LS_LONG_VIEW) && (c != 1)) printf("\n"); out: if (g.gl_pathc) globfree(&g); free(indices); return 0; } static int do_df(struct sftp_conn *conn, const char *path, int hflag, int iflag) { struct sftp_statvfs st; char s_used[FMT_SCALED_STRSIZE], s_avail[FMT_SCALED_STRSIZE]; char s_root[FMT_SCALED_STRSIZE], s_total[FMT_SCALED_STRSIZE]; char s_icapacity[16], s_dcapacity[16]; - if (do_statvfs(conn, path, &st, 1) == -1) + if (sftp_statvfs(conn, path, &st, 1) == -1) return -1; if (st.f_files == 0) strlcpy(s_icapacity, "ERR", sizeof(s_icapacity)); else { snprintf(s_icapacity, sizeof(s_icapacity), "%3llu%%", (unsigned long long)(100 * (st.f_files - st.f_ffree) / st.f_files)); } if (st.f_blocks == 0) strlcpy(s_dcapacity, "ERR", sizeof(s_dcapacity)); else { snprintf(s_dcapacity, sizeof(s_dcapacity), "%3llu%%", (unsigned long long)(100 * (st.f_blocks - st.f_bfree) / st.f_blocks)); } if (iflag) { printf(" Inodes Used Avail " "(root) %%Capacity\n"); printf("%11llu %11llu %11llu %11llu %s\n", (unsigned long long)st.f_files, (unsigned long long)(st.f_files - st.f_ffree), (unsigned long long)st.f_favail, (unsigned long long)st.f_ffree, s_icapacity); } else if (hflag) { strlcpy(s_used, "error", sizeof(s_used)); strlcpy(s_avail, "error", sizeof(s_avail)); strlcpy(s_root, "error", sizeof(s_root)); strlcpy(s_total, "error", sizeof(s_total)); fmt_scaled((st.f_blocks - st.f_bfree) * st.f_frsize, s_used); fmt_scaled(st.f_bavail * st.f_frsize, s_avail); fmt_scaled(st.f_bfree * st.f_frsize, s_root); fmt_scaled(st.f_blocks * st.f_frsize, s_total); printf(" Size Used Avail (root) %%Capacity\n"); printf("%7sB %7sB %7sB %7sB %s\n", s_total, s_used, s_avail, s_root, s_dcapacity); } else { printf(" Size Used Avail " "(root) %%Capacity\n"); printf("%12llu %12llu %12llu %12llu %s\n", (unsigned long long)(st.f_frsize * st.f_blocks / 1024), (unsigned long long)(st.f_frsize * (st.f_blocks - st.f_bfree) / 1024), (unsigned long long)(st.f_frsize * st.f_bavail / 1024), (unsigned long long)(st.f_frsize * st.f_bfree / 1024), s_dcapacity); } return 0; } /* * Undo escaping of glob sequences in place. Used to undo extra escaping * applied in makeargv() when the string is destined for a function that * does not glob it. */ static void undo_glob_escape(char *s) { size_t i, j; for (i = j = 0;;) { if (s[i] == '\0') { s[j] = '\0'; return; } if (s[i] != '\\') { s[j++] = s[i++]; continue; } /* s[i] == '\\' */ ++i; switch (s[i]) { case '?': case '[': case '*': case '\\': s[j++] = s[i++]; break; case '\0': s[j++] = '\\'; s[j] = '\0'; return; default: s[j++] = '\\'; s[j++] = s[i++]; break; } } } /* * Split a string into an argument vector using sh(1)-style quoting, * comment and escaping rules, but with some tweaks to handle glob(3) * wildcards. * The "sloppy" flag allows for recovery from missing terminating quote, for * use in parsing incomplete commandlines during tab autocompletion. * * Returns NULL on error or a NULL-terminated array of arguments. * * If "lastquote" is not NULL, the quoting character used for the last * argument is placed in *lastquote ("\0", "'" or "\""). * * If "terminated" is not NULL, *terminated will be set to 1 when the * last argument's quote has been properly terminated or 0 otherwise. * This parameter is only of use if "sloppy" is set. */ #define MAXARGS 128 #define MAXARGLEN 8192 static char ** makeargv(const char *arg, int *argcp, int sloppy, char *lastquote, u_int *terminated) { int argc, quot; size_t i, j; static char argvs[MAXARGLEN]; static char *argv[MAXARGS + 1]; enum { MA_START, MA_SQUOTE, MA_DQUOTE, MA_UNQUOTED } state, q; *argcp = argc = 0; if (strlen(arg) > sizeof(argvs) - 1) { args_too_longs: error("string too long"); return NULL; } if (terminated != NULL) *terminated = 1; if (lastquote != NULL) *lastquote = '\0'; state = MA_START; i = j = 0; for (;;) { if ((size_t)argc >= sizeof(argv) / sizeof(*argv)){ error("Too many arguments."); return NULL; } if (isspace((unsigned char)arg[i])) { if (state == MA_UNQUOTED) { /* Terminate current argument */ argvs[j++] = '\0'; argc++; state = MA_START; } else if (state != MA_START) argvs[j++] = arg[i]; } else if (arg[i] == '"' || arg[i] == '\'') { q = arg[i] == '"' ? MA_DQUOTE : MA_SQUOTE; if (state == MA_START) { argv[argc] = argvs + j; state = q; if (lastquote != NULL) *lastquote = arg[i]; } else if (state == MA_UNQUOTED) state = q; else if (state == q) state = MA_UNQUOTED; else argvs[j++] = arg[i]; } else if (arg[i] == '\\') { if (state == MA_SQUOTE || state == MA_DQUOTE) { quot = state == MA_SQUOTE ? '\'' : '"'; /* Unescape quote we are in */ /* XXX support \n and friends? */ if (arg[i + 1] == quot) { i++; argvs[j++] = arg[i]; } else if (arg[i + 1] == '?' || arg[i + 1] == '[' || arg[i + 1] == '*') { /* * Special case for sftp: append * double-escaped glob sequence - * glob will undo one level of * escaping. NB. string can grow here. */ if (j >= sizeof(argvs) - 5) goto args_too_longs; argvs[j++] = '\\'; argvs[j++] = arg[i++]; argvs[j++] = '\\'; argvs[j++] = arg[i]; } else { argvs[j++] = arg[i++]; argvs[j++] = arg[i]; } } else { if (state == MA_START) { argv[argc] = argvs + j; state = MA_UNQUOTED; if (lastquote != NULL) *lastquote = '\0'; } if (arg[i + 1] == '?' || arg[i + 1] == '[' || arg[i + 1] == '*' || arg[i + 1] == '\\') { /* * Special case for sftp: append * escaped glob sequence - * glob will undo one level of * escaping. */ argvs[j++] = arg[i++]; argvs[j++] = arg[i]; } else { /* Unescape everything */ /* XXX support \n and friends? */ i++; argvs[j++] = arg[i]; } } } else if (arg[i] == '#') { if (state == MA_SQUOTE || state == MA_DQUOTE) argvs[j++] = arg[i]; else goto string_done; } else if (arg[i] == '\0') { if (state == MA_SQUOTE || state == MA_DQUOTE) { if (sloppy) { state = MA_UNQUOTED; if (terminated != NULL) *terminated = 0; goto string_done; } error("Unterminated quoted argument"); return NULL; } string_done: if (state == MA_UNQUOTED) { argvs[j++] = '\0'; argc++; } break; } else { if (state == MA_START) { argv[argc] = argvs + j; state = MA_UNQUOTED; if (lastquote != NULL) *lastquote = '\0'; } if ((state == MA_SQUOTE || state == MA_DQUOTE) && (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) { /* * Special case for sftp: escape quoted * glob(3) wildcards. NB. string can grow * here. */ if (j >= sizeof(argvs) - 3) goto args_too_longs; argvs[j++] = '\\'; argvs[j++] = arg[i]; } else argvs[j++] = arg[i]; } i++; } *argcp = argc; return argv; } static int parse_args(const char **cpp, int *ignore_errors, int *disable_echo, int *aflag, int *fflag, int *hflag, int *iflag, int *lflag, int *pflag, int *rflag, int *sflag, unsigned long *n_arg, char **path1, char **path2) { const char *cmd, *cp = *cpp; char *cp2, **argv; int base = 0; long long ll; int path1_mandatory = 0, i, cmdnum, optidx, argc; /* Skip leading whitespace */ cp = cp + strspn(cp, WHITESPACE); /* * Check for leading '-' (disable error processing) and '@' (suppress * command echo) */ *ignore_errors = 0; *disable_echo = 0; for (;*cp != '\0'; cp++) { if (*cp == '-') { *ignore_errors = 1; } else if (*cp == '@') { *disable_echo = 1; } else { /* all other characters terminate prefix processing */ break; } } cp = cp + strspn(cp, WHITESPACE); /* Ignore blank lines and lines which begin with comment '#' char */ if (*cp == '\0' || *cp == '#') return (0); if ((argv = makeargv(cp, &argc, 0, NULL, NULL)) == NULL) return -1; /* Figure out which command we have */ for (i = 0; cmds[i].c != NULL; i++) { if (argv[0] != NULL && strcasecmp(cmds[i].c, argv[0]) == 0) break; } cmdnum = cmds[i].n; cmd = cmds[i].c; /* Special case */ if (*cp == '!') { cp++; cmdnum = I_SHELL; } else if (cmdnum == -1) { error("Invalid command."); return -1; } /* Get arguments and parse flags */ *aflag = *fflag = *hflag = *iflag = *lflag = *pflag = 0; *rflag = *sflag = 0; *path1 = *path2 = NULL; optidx = 1; switch (cmdnum) { case I_GET: case I_REGET: case I_REPUT: case I_PUT: if ((optidx = parse_getput_flags(cmd, argv, argc, aflag, fflag, pflag, rflag)) == -1) return -1; /* Get first pathname (mandatory) */ if (argc - optidx < 1) { error("You must specify at least one path after a " "%s command.", cmd); return -1; } *path1 = xstrdup(argv[optidx]); /* Get second pathname (optional) */ if (argc - optidx > 1) { *path2 = xstrdup(argv[optidx + 1]); /* Destination is not globbed */ undo_glob_escape(*path2); } break; case I_LINK: if ((optidx = parse_link_flags(cmd, argv, argc, sflag)) == -1) return -1; goto parse_two_paths; case I_COPY: if ((optidx = parse_no_flags(cmd, argv, argc)) == -1) return -1; goto parse_two_paths; case I_RENAME: if ((optidx = parse_rename_flags(cmd, argv, argc, lflag)) == -1) return -1; goto parse_two_paths; case I_SYMLINK: if ((optidx = parse_no_flags(cmd, argv, argc)) == -1) return -1; parse_two_paths: if (argc - optidx < 2) { error("You must specify two paths after a %s " "command.", cmd); return -1; } *path1 = xstrdup(argv[optidx]); *path2 = xstrdup(argv[optidx + 1]); /* Paths are not globbed */ undo_glob_escape(*path1); undo_glob_escape(*path2); break; case I_RM: case I_MKDIR: case I_RMDIR: case I_LMKDIR: path1_mandatory = 1; /* FALLTHROUGH */ case I_CHDIR: case I_LCHDIR: if ((optidx = parse_no_flags(cmd, argv, argc)) == -1) return -1; /* Get pathname (mandatory) */ if (argc - optidx < 1) { if (!path1_mandatory) break; /* return a NULL path1 */ error("You must specify a path after a %s command.", cmd); return -1; } *path1 = xstrdup(argv[optidx]); /* Only "rm" globs */ if (cmdnum != I_RM) undo_glob_escape(*path1); break; case I_DF: if ((optidx = parse_df_flags(cmd, argv, argc, hflag, iflag)) == -1) return -1; /* Default to current directory if no path specified */ if (argc - optidx < 1) *path1 = NULL; else { *path1 = xstrdup(argv[optidx]); undo_glob_escape(*path1); } break; case I_LS: if ((optidx = parse_ls_flags(argv, argc, lflag)) == -1) return(-1); /* Path is optional */ if (argc - optidx > 0) *path1 = xstrdup(argv[optidx]); break; case I_LLS: /* Skip ls command and following whitespace */ cp = cp + strlen(cmd) + strspn(cp, WHITESPACE); case I_SHELL: /* Uses the rest of the line */ break; case I_LUMASK: case I_CHMOD: base = 8; /* FALLTHROUGH */ case I_CHOWN: case I_CHGRP: if ((optidx = parse_ch_flags(cmd, argv, argc, hflag)) == -1) return -1; /* Get numeric arg (mandatory) */ if (argc - optidx < 1) goto need_num_arg; errno = 0; ll = strtoll(argv[optidx], &cp2, base); if (cp2 == argv[optidx] || *cp2 != '\0' || ((ll == LLONG_MIN || ll == LLONG_MAX) && errno == ERANGE) || ll < 0 || ll > UINT32_MAX) { need_num_arg: error("You must supply a numeric argument " "to the %s command.", cmd); return -1; } *n_arg = ll; if (cmdnum == I_LUMASK) break; /* Get pathname (mandatory) */ if (argc - optidx < 2) { error("You must specify a path after a %s command.", cmd); return -1; } *path1 = xstrdup(argv[optidx + 1]); break; case I_QUIT: case I_PWD: case I_LPWD: case I_HELP: case I_VERSION: case I_PROGRESS: if ((optidx = parse_no_flags(cmd, argv, argc)) == -1) return -1; break; default: fatal("Command not implemented"); } *cpp = cp; return(cmdnum); } static int parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd, const char *startdir, int err_abort, int echo_command) { const char *ocmd = cmd; char *path1, *path2, *tmp; int ignore_errors = 0, disable_echo = 1; int aflag = 0, fflag = 0, hflag = 0, iflag = 0; int lflag = 0, pflag = 0, rflag = 0, sflag = 0; int cmdnum, i; unsigned long n_arg = 0; - Attrib a, *aa; + Attrib a, aa; char path_buf[PATH_MAX]; int err = 0; glob_t g; path1 = path2 = NULL; cmdnum = parse_args(&cmd, &ignore_errors, &disable_echo, &aflag, &fflag, &hflag, &iflag, &lflag, &pflag, &rflag, &sflag, &n_arg, &path1, &path2); if (ignore_errors != 0) err_abort = 0; if (echo_command && !disable_echo) mprintf("sftp> %s\n", ocmd); memset(&g, 0, sizeof(g)); /* Perform command */ switch (cmdnum) { case 0: /* Blank line */ break; case -1: /* Unrecognized command */ err = -1; break; case I_REGET: aflag = 1; /* FALLTHROUGH */ case I_GET: err = process_get(conn, path1, path2, *pwd, pflag, rflag, aflag, fflag); break; case I_REPUT: aflag = 1; /* FALLTHROUGH */ case I_PUT: err = process_put(conn, path1, path2, *pwd, pflag, rflag, aflag, fflag); break; case I_COPY: - path1 = make_absolute(path1, *pwd); - path2 = make_absolute(path2, *pwd); - err = do_copy(conn, path1, path2); + path1 = sftp_make_absolute(path1, *pwd); + path2 = sftp_make_absolute(path2, *pwd); + err = sftp_copy(conn, path1, path2); break; case I_RENAME: - path1 = make_absolute(path1, *pwd); - path2 = make_absolute(path2, *pwd); - err = do_rename(conn, path1, path2, lflag); + path1 = sftp_make_absolute(path1, *pwd); + path2 = sftp_make_absolute(path2, *pwd); + err = sftp_rename(conn, path1, path2, lflag); break; case I_SYMLINK: sflag = 1; /* FALLTHROUGH */ case I_LINK: if (!sflag) - path1 = make_absolute(path1, *pwd); - path2 = make_absolute(path2, *pwd); - err = (sflag ? do_symlink : do_hardlink)(conn, path1, path2); + path1 = sftp_make_absolute(path1, *pwd); + path2 = sftp_make_absolute(path2, *pwd); + err = (sflag ? sftp_symlink : sftp_hardlink)(conn, + path1, path2); break; case I_RM: path1 = make_absolute_pwd_glob(path1, *pwd); - remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g); + sftp_glob(conn, path1, GLOB_NOCHECK, NULL, &g); for (i = 0; g.gl_pathv[i] && !interrupted; i++) { if (!quiet) mprintf("Removing %s\n", g.gl_pathv[i]); - err = do_rm(conn, g.gl_pathv[i]); + err = sftp_rm(conn, g.gl_pathv[i]); if (err != 0 && err_abort) break; } break; case I_MKDIR: - path1 = make_absolute(path1, *pwd); + path1 = sftp_make_absolute(path1, *pwd); attrib_clear(&a); a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS; a.perm = 0777; - err = do_mkdir(conn, path1, &a, 1); + err = sftp_mkdir(conn, path1, &a, 1); break; case I_RMDIR: - path1 = make_absolute(path1, *pwd); - err = do_rmdir(conn, path1); + path1 = sftp_make_absolute(path1, *pwd); + err = sftp_rmdir(conn, path1); break; case I_CHDIR: if (path1 == NULL || *path1 == '\0') path1 = xstrdup(startdir); - path1 = make_absolute(path1, *pwd); - if ((tmp = do_realpath(conn, path1)) == NULL) { + path1 = sftp_make_absolute(path1, *pwd); + if ((tmp = sftp_realpath(conn, path1)) == NULL) { err = 1; break; } - if ((aa = do_stat(conn, tmp, 0)) == NULL) { + if (sftp_stat(conn, tmp, 0, &aa) != 0) { free(tmp); err = 1; break; } - if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) { + if (!(aa.flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) { error("Can't change directory: Can't check target"); free(tmp); err = 1; break; } - if (!S_ISDIR(aa->perm)) { + if (!S_ISDIR(aa.perm)) { error("Can't change directory: \"%s\" is not " "a directory", tmp); free(tmp); err = 1; break; } free(*pwd); *pwd = tmp; break; case I_LS: if (!path1) { do_ls_dir(conn, *pwd, *pwd, lflag); break; } /* Strip pwd off beginning of non-absolute paths */ tmp = NULL; if (!path_absolute(path1)) tmp = *pwd; path1 = make_absolute_pwd_glob(path1, *pwd); err = do_globbed_ls(conn, path1, tmp, lflag); break; case I_DF: /* Default to current directory if no path specified */ if (path1 == NULL) path1 = xstrdup(*pwd); - path1 = make_absolute(path1, *pwd); + path1 = sftp_make_absolute(path1, *pwd); err = do_df(conn, path1, hflag, iflag); break; case I_LCHDIR: if (path1 == NULL || *path1 == '\0') path1 = xstrdup("~"); tmp = tilde_expand_filename(path1, getuid()); free(path1); path1 = tmp; if (chdir(path1) == -1) { error("Couldn't change local directory to " "\"%s\": %s", path1, strerror(errno)); err = 1; } break; case I_LMKDIR: if (mkdir(path1, 0777) == -1) { error("Couldn't create local directory " "\"%s\": %s", path1, strerror(errno)); err = 1; } break; case I_LLS: local_do_ls(cmd); break; case I_SHELL: local_do_shell(cmd); break; case I_LUMASK: umask(n_arg); printf("Local umask: %03lo\n", n_arg); break; case I_CHMOD: path1 = make_absolute_pwd_glob(path1, *pwd); attrib_clear(&a); a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS; a.perm = n_arg; - remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g); + sftp_glob(conn, path1, GLOB_NOCHECK, NULL, &g); for (i = 0; g.gl_pathv[i] && !interrupted; i++) { if (!quiet) mprintf("Changing mode on %s\n", g.gl_pathv[i]); - err = (hflag ? do_lsetstat : do_setstat)(conn, + err = (hflag ? sftp_lsetstat : sftp_setstat)(conn, g.gl_pathv[i], &a); if (err != 0 && err_abort) break; } break; case I_CHOWN: case I_CHGRP: path1 = make_absolute_pwd_glob(path1, *pwd); - remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g); + sftp_glob(conn, path1, GLOB_NOCHECK, NULL, &g); for (i = 0; g.gl_pathv[i] && !interrupted; i++) { - if (!(aa = (hflag ? do_lstat : do_stat)(conn, - g.gl_pathv[i], 0))) { + if ((hflag ? sftp_lstat : sftp_stat)(conn, + g.gl_pathv[i], 0, &aa) != 0) { if (err_abort) { err = -1; break; } else continue; } - if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) { + if (!(aa.flags & SSH2_FILEXFER_ATTR_UIDGID)) { error("Can't get current ownership of " "remote file \"%s\"", g.gl_pathv[i]); if (err_abort) { err = -1; break; } else continue; } - aa->flags &= SSH2_FILEXFER_ATTR_UIDGID; + aa.flags &= SSH2_FILEXFER_ATTR_UIDGID; if (cmdnum == I_CHOWN) { if (!quiet) mprintf("Changing owner on %s\n", g.gl_pathv[i]); - aa->uid = n_arg; + aa.uid = n_arg; } else { if (!quiet) mprintf("Changing group on %s\n", g.gl_pathv[i]); - aa->gid = n_arg; + aa.gid = n_arg; } - err = (hflag ? do_lsetstat : do_setstat)(conn, - g.gl_pathv[i], aa); + err = (hflag ? sftp_lsetstat : sftp_setstat)(conn, + g.gl_pathv[i], &aa); if (err != 0 && err_abort) break; } break; case I_PWD: mprintf("Remote working directory: %s\n", *pwd); break; case I_LPWD: if (!getcwd(path_buf, sizeof(path_buf))) { error("Couldn't get local cwd: %s", strerror(errno)); err = -1; break; } mprintf("Local working directory: %s\n", path_buf); break; case I_QUIT: /* Processed below */ break; case I_HELP: help(); break; case I_VERSION: printf("SFTP protocol version %u\n", sftp_proto_version(conn)); break; case I_PROGRESS: showprogress = !showprogress; if (showprogress) printf("Progress meter enabled\n"); else printf("Progress meter disabled\n"); break; default: fatal("%d is not implemented", cmdnum); } if (g.gl_pathc) globfree(&g); free(path1); free(path2); /* If an unignored error occurs in batch mode we should abort. */ if (err_abort && err != 0) return (-1); else if (cmdnum == I_QUIT) return (1); return (0); } #ifdef USE_LIBEDIT static char * prompt(EditLine *el) { return ("sftp> "); } /* Display entries in 'list' after skipping the first 'len' chars */ static void complete_display(char **list, u_int len) { u_int y, m = 0, width = 80, columns = 1, colspace = 0, llen; struct winsize ws; char *tmp; /* Count entries for sort and find longest */ for (y = 0; list[y]; y++) m = MAXIMUM(m, strlen(list[y])); if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1) width = ws.ws_col; m = m > len ? m - len : 0; columns = width / (m + 2); columns = MAXIMUM(columns, 1); colspace = width / columns; colspace = MINIMUM(colspace, width); printf("\n"); m = 1; for (y = 0; list[y]; y++) { llen = strlen(list[y]); tmp = llen > len ? list[y] + len : ""; mprintf("%-*s", colspace, tmp); if (m >= columns) { printf("\n"); m = 1; } else m++; } printf("\n"); } /* * Given a "list" of words that begin with a common prefix of "word", * attempt to find an autocompletion to extends "word" by the next * characters common to all entries in "list". */ static char * complete_ambiguous(const char *word, char **list, size_t count) { if (word == NULL) return NULL; if (count > 0) { u_int y, matchlen = strlen(list[0]); /* Find length of common stem */ for (y = 1; list[y]; y++) { u_int x; for (x = 0; x < matchlen; x++) if (list[0][x] != list[y][x]) break; matchlen = x; } if (matchlen > strlen(word)) { char *tmp = xstrdup(list[0]); tmp[matchlen] = '\0'; return tmp; } } return xstrdup(word); } /* Autocomplete a sftp command */ static int complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote, int terminated) { u_int y, count = 0, cmdlen, tmplen; char *tmp, **list, argterm[3]; const LineInfo *lf; list = xcalloc((sizeof(cmds) / sizeof(*cmds)) + 1, sizeof(char *)); /* No command specified: display all available commands */ if (cmd == NULL) { for (y = 0; cmds[y].c; y++) list[count++] = xstrdup(cmds[y].c); list[count] = NULL; complete_display(list, 0); for (y = 0; list[y] != NULL; y++) free(list[y]); free(list); return count; } /* Prepare subset of commands that start with "cmd" */ cmdlen = strlen(cmd); for (y = 0; cmds[y].c; y++) { if (!strncasecmp(cmd, cmds[y].c, cmdlen)) list[count++] = xstrdup(cmds[y].c); } list[count] = NULL; if (count == 0) { free(list); return 0; } /* Complete ambiguous command */ tmp = complete_ambiguous(cmd, list, count); if (count > 1) complete_display(list, 0); for (y = 0; list[y]; y++) free(list[y]); free(list); if (tmp != NULL) { tmplen = strlen(tmp); cmdlen = strlen(cmd); /* If cmd may be extended then do so */ if (tmplen > cmdlen) if (el_insertstr(el, tmp + cmdlen) == -1) fatal("el_insertstr failed."); lf = el_line(el); /* Terminate argument cleanly */ if (count == 1) { y = 0; if (!terminated) argterm[y++] = quote; if (lastarg || *(lf->cursor) != ' ') argterm[y++] = ' '; argterm[y] = '\0'; if (y > 0 && el_insertstr(el, argterm) == -1) fatal("el_insertstr failed."); } free(tmp); } return count; } /* * Determine whether a particular sftp command's arguments (if any) represent * local or remote files. The "cmdarg" argument specifies the actual argument * and accepts values 1 or 2. */ static int complete_is_remote(char *cmd, int cmdarg) { int i; if (cmd == NULL) return -1; for (i = 0; cmds[i].c; i++) { if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c))) { if (cmdarg == 1) return cmds[i].t; else if (cmdarg == 2) return cmds[i].t2; break; } } return -1; } /* Autocomplete a filename "file" */ static int complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path, char *file, int remote, int lastarg, char quote, int terminated) { glob_t g; char *tmp, *tmp2, ins[8]; u_int i, hadglob, pwdlen, len, tmplen, filelen, cesc, isesc, isabs; int clen; const LineInfo *lf; /* Glob from "file" location */ if (file == NULL) tmp = xstrdup("*"); else xasprintf(&tmp, "%s*", file); /* Check if the path is absolute. */ isabs = path_absolute(tmp); memset(&g, 0, sizeof(g)); if (remote != LOCAL) { tmp = make_absolute_pwd_glob(tmp, remote_path); - remote_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g); + sftp_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g); } else (void)glob(tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g); /* Determine length of pwd so we can trim completion display */ for (hadglob = tmplen = pwdlen = 0; tmp[tmplen] != 0; tmplen++) { /* Terminate counting on first unescaped glob metacharacter */ if (tmp[tmplen] == '*' || tmp[tmplen] == '?') { if (tmp[tmplen] != '*' || tmp[tmplen + 1] != '\0') hadglob = 1; break; } if (tmp[tmplen] == '\\' && tmp[tmplen + 1] != '\0') tmplen++; if (tmp[tmplen] == '/') pwdlen = tmplen + 1; /* track last seen '/' */ } free(tmp); tmp = NULL; if (g.gl_matchc == 0) goto out; if (g.gl_matchc > 1) complete_display(g.gl_pathv, pwdlen); /* Don't try to extend globs */ if (file == NULL || hadglob) goto out; tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc); tmp = path_strip(tmp2, isabs ? NULL : remote_path); free(tmp2); if (tmp == NULL) goto out; tmplen = strlen(tmp); filelen = strlen(file); /* Count the number of escaped characters in the input string. */ cesc = isesc = 0; for (i = 0; i < filelen; i++) { if (!isesc && file[i] == '\\' && i + 1 < filelen){ isesc = 1; cesc++; } else isesc = 0; } if (tmplen > (filelen - cesc)) { tmp2 = tmp + filelen - cesc; len = strlen(tmp2); /* quote argument on way out */ for (i = 0; i < len; i += clen) { if ((clen = mblen(tmp2 + i, len - i)) < 0 || (size_t)clen > sizeof(ins) - 2) fatal("invalid multibyte character"); ins[0] = '\\'; memcpy(ins + 1, tmp2 + i, clen); ins[clen + 1] = '\0'; switch (tmp2[i]) { case '\'': case '"': case '\\': case '\t': case '[': case ' ': case '#': case '*': if (quote == '\0' || tmp2[i] == quote) { if (el_insertstr(el, ins) == -1) fatal("el_insertstr " "failed."); break; } /* FALLTHROUGH */ default: if (el_insertstr(el, ins + 1) == -1) fatal("el_insertstr failed."); break; } } } lf = el_line(el); if (g.gl_matchc == 1) { i = 0; if (!terminated && quote != '\0') ins[i++] = quote; if (*(lf->cursor - 1) != '/' && (lastarg || *(lf->cursor) != ' ')) ins[i++] = ' '; ins[i] = '\0'; if (i > 0 && el_insertstr(el, ins) == -1) fatal("el_insertstr failed."); } free(tmp); out: globfree(&g); return g.gl_matchc; } /* tab-completion hook function, called via libedit */ static unsigned char complete(EditLine *el, int ch) { char **argv, *line, quote; int argc, carg; u_int cursor, len, terminated, ret = CC_ERROR; const LineInfo *lf; struct complete_ctx *complete_ctx; lf = el_line(el); if (el_get(el, EL_CLIENTDATA, (void**)&complete_ctx) != 0) fatal_f("el_get failed"); /* Figure out which argument the cursor points to */ cursor = lf->cursor - lf->buffer; line = xmalloc(cursor + 1); memcpy(line, lf->buffer, cursor); line[cursor] = '\0'; argv = makeargv(line, &carg, 1, "e, &terminated); free(line); /* Get all the arguments on the line */ len = lf->lastchar - lf->buffer; line = xmalloc(len + 1); memcpy(line, lf->buffer, len); line[len] = '\0'; argv = makeargv(line, &argc, 1, NULL, NULL); /* Ensure cursor is at EOL or a argument boundary */ if (line[cursor] != ' ' && line[cursor] != '\0' && line[cursor] != '\n') { free(line); return ret; } if (carg == 0) { /* Show all available commands */ complete_cmd_parse(el, NULL, argc == carg, '\0', 1); ret = CC_REDISPLAY; } else if (carg == 1 && cursor > 0 && line[cursor - 1] != ' ') { /* Handle the command parsing */ if (complete_cmd_parse(el, argv[0], argc == carg, quote, terminated) != 0) ret = CC_REDISPLAY; } else if (carg >= 1) { /* Handle file parsing */ int remote = 0; int i = 0, cmdarg = 0; char *filematch = NULL; if (carg > 1 && line[cursor-1] != ' ') filematch = argv[carg - 1]; for (i = 1; i < carg; i++) { /* Skip flags */ if (argv[i][0] != '-') cmdarg++; } /* * If previous argument is complete, then offer completion * on the next one. */ if (line[cursor - 1] == ' ') cmdarg++; remote = complete_is_remote(argv[0], cmdarg); if ((remote == REMOTE || remote == LOCAL) && complete_match(el, complete_ctx->conn, *complete_ctx->remote_pathp, filematch, remote, carg == argc, quote, terminated) != 0) ret = CC_REDISPLAY; } free(line); return ret; } #endif /* USE_LIBEDIT */ static int interactive_loop(struct sftp_conn *conn, char *file1, char *file2) { char *remote_path; char *dir = NULL, *startdir = NULL; char cmd[2048]; int err, interactive; EditLine *el = NULL; #ifdef USE_LIBEDIT History *hl = NULL; HistEvent hev; extern char *__progname; struct complete_ctx complete_ctx; if (!batchmode && isatty(STDIN_FILENO)) { if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL) fatal("Couldn't initialise editline"); if ((hl = history_init()) == NULL) fatal("Couldn't initialise editline history"); history(hl, &hev, H_SETSIZE, 100); el_set(el, EL_HIST, history, hl); el_set(el, EL_PROMPT, prompt); el_set(el, EL_EDITOR, "emacs"); el_set(el, EL_TERMINAL, NULL); el_set(el, EL_SIGNAL, 1); el_source(el, NULL); /* Tab Completion */ el_set(el, EL_ADDFN, "ftp-complete", "Context sensitive argument completion", complete); complete_ctx.conn = conn; complete_ctx.remote_pathp = &remote_path; el_set(el, EL_CLIENTDATA, (void*)&complete_ctx); el_set(el, EL_BIND, "^I", "ftp-complete", NULL); /* enable ctrl-left-arrow and ctrl-right-arrow */ el_set(el, EL_BIND, "\\e[1;5C", "em-next-word", NULL); el_set(el, EL_BIND, "\\e\\e[C", "em-next-word", NULL); el_set(el, EL_BIND, "\\e[1;5D", "ed-prev-word", NULL); el_set(el, EL_BIND, "\\e\\e[D", "ed-prev-word", NULL); /* make ^w match ksh behaviour */ el_set(el, EL_BIND, "^w", "ed-delete-prev-word", NULL); } #endif /* USE_LIBEDIT */ - remote_path = do_realpath(conn, "."); - if (remote_path == NULL) + if ((remote_path = sftp_realpath(conn, ".")) == NULL) fatal("Need cwd"); startdir = xstrdup(remote_path); if (file1 != NULL) { dir = xstrdup(file1); - dir = make_absolute(dir, remote_path); + dir = sftp_make_absolute(dir, remote_path); - if (remote_is_dir(conn, dir) && file2 == NULL) { + if (sftp_remote_is_dir(conn, dir) && file2 == NULL) { if (!quiet) mprintf("Changing to: %s\n", dir); snprintf(cmd, sizeof cmd, "cd \"%s\"", dir); if (parse_dispatch_command(conn, cmd, &remote_path, startdir, 1, 0) != 0) { free(dir); free(startdir); free(remote_path); free(conn); return (-1); } } else { /* XXX this is wrong wrt quoting */ snprintf(cmd, sizeof cmd, "get%s %s%s%s", global_aflag ? " -a" : "", dir, file2 == NULL ? "" : " ", file2 == NULL ? "" : file2); err = parse_dispatch_command(conn, cmd, &remote_path, startdir, 1, 0); free(dir); free(startdir); free(remote_path); free(conn); return (err); } free(dir); } setvbuf(stdout, NULL, _IOLBF, 0); setvbuf(infile, NULL, _IOLBF, 0); interactive = !batchmode && isatty(STDIN_FILENO); err = 0; for (;;) { struct sigaction sa; interrupted = 0; memset(&sa, 0, sizeof(sa)); sa.sa_handler = interactive ? read_interrupt : killchild; if (sigaction(SIGINT, &sa, NULL) == -1) { debug3("sigaction(%s): %s", strsignal(SIGINT), strerror(errno)); break; } if (el == NULL) { if (interactive) printf("sftp> "); if (fgets(cmd, sizeof(cmd), infile) == NULL) { if (interactive) printf("\n"); if (interrupted) continue; break; } } else { #ifdef USE_LIBEDIT const char *line; int count = 0; if ((line = el_gets(el, &count)) == NULL || count <= 0) { printf("\n"); if (interrupted) continue; break; } history(hl, &hev, H_ENTER, line); if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) { fprintf(stderr, "Error: input line too long\n"); continue; } #endif /* USE_LIBEDIT */ } cmd[strcspn(cmd, "\n")] = '\0'; /* Handle user interrupts gracefully during commands */ interrupted = 0; ssh_signal(SIGINT, cmd_interrupt); err = parse_dispatch_command(conn, cmd, &remote_path, startdir, batchmode, !interactive && el == NULL); if (err != 0) break; } ssh_signal(SIGCHLD, SIG_DFL); free(remote_path); free(startdir); free(conn); #ifdef USE_LIBEDIT if (el != NULL) el_end(el); #endif /* USE_LIBEDIT */ /* err == 1 signifies normal "quit" exit */ return (err >= 0 ? 0 : -1); } static void connect_to_server(char *path, char **args, int *in, int *out) { int c_in, c_out; #ifdef USE_PIPES int pin[2], pout[2]; if ((pipe(pin) == -1) || (pipe(pout) == -1)) fatal("pipe: %s", strerror(errno)); *in = pin[0]; *out = pout[1]; c_in = pout[0]; c_out = pin[1]; #else /* USE_PIPES */ int inout[2]; if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1) fatal("socketpair: %s", strerror(errno)); *in = *out = inout[0]; c_in = c_out = inout[1]; #endif /* USE_PIPES */ if ((sshpid = fork()) == -1) fatal("fork: %s", strerror(errno)); else if (sshpid == 0) { if ((dup2(c_in, STDIN_FILENO) == -1) || (dup2(c_out, STDOUT_FILENO) == -1)) { fprintf(stderr, "dup2: %s\n", strerror(errno)); _exit(1); } close(*in); close(*out); close(c_in); close(c_out); /* * The underlying ssh is in the same process group, so we must * ignore SIGINT if we want to gracefully abort commands, * otherwise the signal will make it to the ssh process and * kill it too. Contrawise, since sftp sends SIGTERMs to the * underlying ssh, it must *not* ignore that signal. */ ssh_signal(SIGINT, SIG_IGN); ssh_signal(SIGTERM, SIG_DFL); execvp(path, args); fprintf(stderr, "exec: %s: %s\n", path, strerror(errno)); _exit(1); } ssh_signal(SIGTERM, killchild); ssh_signal(SIGINT, killchild); ssh_signal(SIGHUP, killchild); ssh_signal(SIGTSTP, suspchild); ssh_signal(SIGTTIN, suspchild); ssh_signal(SIGTTOU, suspchild); ssh_signal(SIGCHLD, sigchld_handler); close(c_in); close(c_out); } static void usage(void) { extern char *__progname; fprintf(stderr, "usage: %s [-46AaCfNpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n" " [-D sftp_server_command] [-F ssh_config] [-i identity_file]\n" " [-J destination] [-l limit] [-o ssh_option] [-P port]\n" " [-R num_requests] [-S program] [-s subsystem | sftp_server]\n" " [-X sftp_option] destination\n", __progname); exit(1); } int main(int argc, char **argv) { int r, in, out, ch, err, tmp, port = -1, noisy = 0; char *host = NULL, *user, *cp, **cpp, *file2 = NULL; int debug_level = 0; char *file1 = NULL, *sftp_server = NULL; char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL; const char *errstr; LogLevel ll = SYSLOG_LEVEL_INFO; arglist args; extern int optind; extern char *optarg; struct sftp_conn *conn; size_t copy_buffer_len = 0; size_t num_requests = 0; long long llv, limit_kbps = 0; /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ sanitise_stdfd(); msetlocale(); __progname = ssh_get_progname(argv[0]); memset(&args, '\0', sizeof(args)); args.list = NULL; addargs(&args, "%s", ssh_program); addargs(&args, "-oForwardX11 no"); addargs(&args, "-oPermitLocalCommand no"); addargs(&args, "-oClearAllForwardings yes"); ll = SYSLOG_LEVEL_INFO; infile = stdin; while ((ch = getopt(argc, argv, "1246AafhNpqrvCc:D:i:l:o:s:S:b:B:F:J:P:R:X:")) != -1) { switch (ch) { /* Passed through to ssh(1) */ case 'A': case '4': case '6': case 'C': addargs(&args, "-%c", ch); break; /* Passed through to ssh(1) with argument */ case 'F': case 'J': case 'c': case 'i': case 'o': addargs(&args, "-%c", ch); addargs(&args, "%s", optarg); break; case 'q': ll = SYSLOG_LEVEL_ERROR; quiet = 1; showprogress = 0; addargs(&args, "-%c", ch); break; case 'P': port = a2port(optarg); if (port <= 0) fatal("Bad port \"%s\"\n", optarg); break; case 'v': if (debug_level < 3) { addargs(&args, "-v"); ll = SYSLOG_LEVEL_DEBUG1 + debug_level; } debug_level++; break; case '1': fatal("SSH protocol v.1 is no longer supported"); break; case '2': /* accept silently */ break; case 'a': global_aflag = 1; break; case 'B': copy_buffer_len = strtol(optarg, &cp, 10); if (copy_buffer_len == 0 || *cp != '\0') fatal("Invalid buffer size \"%s\"", optarg); break; case 'b': if (batchmode) fatal("Batch file already specified."); /* Allow "-" as stdin */ if (strcmp(optarg, "-") != 0 && (infile = fopen(optarg, "r")) == NULL) fatal("%s (%s).", strerror(errno), optarg); showprogress = 0; quiet = batchmode = 1; addargs(&args, "-obatchmode yes"); break; case 'f': global_fflag = 1; break; case 'N': noisy = 1; /* Used to clear quiet mode after getopt */ break; case 'p': global_pflag = 1; break; case 'D': sftp_direct = optarg; break; case 'l': limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024, &errstr); if (errstr != NULL) usage(); limit_kbps *= 1024; /* kbps */ break; case 'r': global_rflag = 1; break; case 'R': num_requests = strtol(optarg, &cp, 10); if (num_requests == 0 || *cp != '\0') fatal("Invalid number of requests \"%s\"", optarg); break; case 's': sftp_server = optarg; break; case 'S': ssh_program = optarg; replacearg(&args, 0, "%s", ssh_program); break; case 'X': /* Please keep in sync with ssh.c -X */ if (strncmp(optarg, "buffer=", 7) == 0) { r = scan_scaled(optarg + 7, &llv); if (r == 0 && (llv <= 0 || llv > 256 * 1024)) { r = -1; errno = EINVAL; } if (r == -1) { fatal("Invalid buffer size \"%s\": %s", optarg + 7, strerror(errno)); } copy_buffer_len = (size_t)llv; } else if (strncmp(optarg, "nrequests=", 10) == 0) { llv = strtonum(optarg + 10, 1, 256 * 1024, &errstr); if (errstr != NULL) { fatal("Invalid number of requests " "\"%s\": %s", optarg + 10, errstr); } num_requests = (size_t)llv; } else { fatal("Invalid -X option"); } break; case 'h': default: usage(); } } /* Do this last because we want the user to be able to override it */ addargs(&args, "-oForwardAgent no"); if (!isatty(STDERR_FILENO)) showprogress = 0; if (noisy) quiet = 0; log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1); if (sftp_direct == NULL) { if (optind == argc || argc > (optind + 2)) usage(); argv += optind; switch (parse_uri("sftp", *argv, &user, &host, &tmp, &file1)) { case -1: usage(); break; case 0: if (tmp != -1) port = tmp; break; default: /* Try with user, host and path. */ if (parse_user_host_path(*argv, &user, &host, &file1) == 0) break; /* Try with user and host. */ if (parse_user_host_port(*argv, &user, &host, NULL) == 0) break; /* Treat as a plain hostname. */ host = xstrdup(*argv); host = cleanhostname(host); break; } file2 = *(argv + 1); if (!*host) { fprintf(stderr, "Missing hostname\n"); usage(); } if (port != -1) addargs(&args, "-oPort %d", port); if (user != NULL) { addargs(&args, "-l"); addargs(&args, "%s", user); } /* no subsystem if the server-spec contains a '/' */ if (sftp_server == NULL || strchr(sftp_server, '/') == NULL) addargs(&args, "-s"); addargs(&args, "--"); addargs(&args, "%s", host); addargs(&args, "%s", (sftp_server != NULL ? sftp_server : "sftp")); connect_to_server(ssh_program, args.list, &in, &out); } else { if ((r = argv_split(sftp_direct, &tmp, &cpp, 1)) != 0) fatal_r(r, "Parse -D arguments"); if (cpp[0] == 0) fatal("No sftp server specified via -D"); connect_to_server(cpp[0], cpp, &in, &out); argv_free(cpp, tmp); } freeargs(&args); - conn = do_init(in, out, copy_buffer_len, num_requests, limit_kbps); + conn = sftp_init(in, out, copy_buffer_len, num_requests, limit_kbps); if (conn == NULL) fatal("Couldn't initialise connection to server"); if (!quiet) { if (sftp_direct == NULL) fprintf(stderr, "Connected to %s.\n", host); else fprintf(stderr, "Attached to %s.\n", sftp_direct); } err = interactive_loop(conn, file1, file2); #if !defined(USE_PIPES) shutdown(in, SHUT_RDWR); shutdown(out, SHUT_RDWR); #endif close(in); close(out); if (batchmode) fclose(infile); while (waitpid(sshpid, NULL, 0) == -1 && sshpid > 1) if (errno != EINTR) fatal("Couldn't wait for ssh process: %s", strerror(errno)); exit(err == 0 ? 0 : 1); } diff --git a/ssh-agent.0 b/ssh-agent.0 index dbd4f17b76b5..9be740d30e63 100644 --- a/ssh-agent.0 +++ b/ssh-agent.0 @@ -1,140 +1,140 @@ SSH-AGENT(1) General Commands Manual SSH-AGENT(1) NAME ssh-agent M-bM-^@M-^S OpenSSH authentication agent SYNOPSIS ssh-agent [-c | -s] [-Dd] [-a bind_address] [-E fingerprint_hash] [-O option] [-P allowed_providers] [-t life] ssh-agent [-a bind_address] [-E fingerprint_hash] [-O option] [-P allowed_providers] [-t life] command [arg ...] ssh-agent [-c | -s] -k DESCRIPTION ssh-agent is a program to hold private keys used for public key authentication. Through use of environment variables the agent can be located and automatically used for authentication when logging in to other machines using ssh(1). The options are as follows: -a bind_address Bind the agent to the UNIX-domain socket bind_address. The default is $TMPDIR/ssh-XXXXXXXXXX/agent.. -c Generate C-shell commands on stdout. This is the default if SHELL looks like it's a csh style of shell. -D Foreground mode. When this option is specified, ssh-agent will not fork. -d Debug mode. When this option is specified, ssh-agent will not fork and will write debug information to standard error. -E fingerprint_hash Specifies the hash algorithm used when displaying key fingerprints. Valid options are: M-bM-^@M-^\md5M-bM-^@M-^] and M-bM-^@M-^\sha256M-bM-^@M-^]. The default is M-bM-^@M-^\sha256M-bM-^@M-^]. -k Kill the current agent (given by the SSH_AGENT_PID environment variable). -O option Specify an option when starting ssh-agent. Currently two options are supported: allow-remote-pkcs11 and no-restrict-websafe. The allow-remote-pkcs11 option allows clients of a forwarded ssh-agent to load PKCS#11 or FIDO provider libraries. By default only local clients may perform this operation. Note that signalling that an ssh-agent client is remote is performed by ssh(1), and use of other tools to forward access to the agent - socket, may circumvent this restriction. + socket may circumvent this restriction. The no-restrict-websafe option instructs ssh-agent to permit signatures using FIDO keys that might be web authentication requests. By default, ssh-agent refuses signature requests for FIDO keys where the key application string does not start with M-bM-^@M-^\ssh:M-bM-^@M-^] and when the data to be signed does not appear to be a ssh(1) user authentication request or a ssh-keygen(1) signature. The default behaviour prevents forwarded access to a FIDO key from also implicitly forwarding the ability to authenticate to websites. -P allowed_providers Specify a pattern-list of acceptable paths for PKCS#11 provider and FIDO authenticator middleware shared libraries that may be used with the -S or -s options to ssh-add(1). Libraries that do not match the pattern list will be refused. See PATTERNS in ssh_config(5) for a description of pattern-list syntax. The - default list is M-bM-^@M-^\/usr/lib/*,/usr/local/lib/*M-bM-^@M-^]. + default list is M-bM-^@M-^\usr/lib*/*,/usr/local/lib*/*M-bM-^@M-^]. -s Generate Bourne shell commands on stdout. This is the default if SHELL does not look like it's a csh style of shell. -t life Set a default value for the maximum lifetime of identities added to the agent. The lifetime may be specified in seconds or in a time format specified in sshd_config(5). A lifetime specified for an identity with ssh-add(1) overrides this value. Without this option the default maximum lifetime is forever. command [arg ...] If a command (and optional arguments) is given, this is executed as a subprocess of the agent. The agent exits automatically when the command given on the command line terminates. There are two main ways to get an agent set up. The first is at the start of an X session, where all other windows or programs are started as children of the ssh-agent program. The agent starts a command under which its environment variables are exported, for example ssh-agent xterm &. When the command terminates, so does the agent. The second method is used for a login session. When ssh-agent is started, it prints the shell commands required to set its environment variables, which in turn can be evaluated in the calling shell, for example eval `ssh-agent -s`. In both cases, ssh(1) looks at these environment variables and uses them to establish a connection to the agent. The agent initially does not have any private keys. Keys are added using ssh-add(1) or by ssh(1) when AddKeysToAgent is set in ssh_config(5). Multiple identities may be stored in ssh-agent concurrently and ssh(1) will automatically use them if present. ssh-add(1) is also used to remove keys from ssh-agent and to query the keys that are held in one. Connections to ssh-agent may be forwarded from further remote hosts using the -A option to ssh(1) (but see the caveats documented therein), avoiding the need for authentication data to be stored on other machines. Authentication passphrases and private keys never go over the network: the connection to the agent is forwarded over SSH remote connections and the result is returned to the requester, allowing the user access to their identities anywhere in the network in a secure fashion. ENVIRONMENT SSH_AGENT_PID When ssh-agent starts, it stores the name of the agent's process ID (PID) in this variable. SSH_AUTH_SOCK When ssh-agent starts, it creates a UNIX-domain socket and stores its pathname in this variable. It is accessible only to the current user, but is easily abused by root or another instance of the same user. FILES $TMPDIR/ssh-XXXXXXXXXX/agent. UNIX-domain sockets used to contain the connection to the authentication agent. These sockets should only be readable by the owner. The sockets should get automatically removed when the agent exits. SEE ALSO ssh(1), ssh-add(1), ssh-keygen(1), ssh_config(5), sshd(8) AUTHORS OpenSSH is a derivative of the original and free ssh 1.2.12 release by Tatu Ylonen. Aaron Campbell, Bob Beck, Markus Friedl, Niels Provos, Theo de Raadt and Dug Song removed many bugs, re-added newer features and created OpenSSH. Markus Friedl contributed the support for SSH protocol versions 1.5 and 2.0. -OpenBSD 7.3 July 23, 2023 OpenBSD 7.3 +OpenBSD 7.3 August 10, 2023 OpenBSD 7.3 diff --git a/ssh-agent.1 b/ssh-agent.1 index 327f0e196644..0b93d03a3d2e 100644 --- a/ssh-agent.1 +++ b/ssh-agent.1 @@ -1,274 +1,274 @@ -.\" $OpenBSD: ssh-agent.1,v 1.78 2023/07/23 20:04:45 naddy Exp $ +.\" $OpenBSD: ssh-agent.1,v 1.79 2023/08/10 14:37:32 naddy Exp $ .\" .\" Author: Tatu Ylonen .\" Copyright (c) 1995 Tatu Ylonen , Espoo, Finland .\" All rights reserved .\" .\" As far as I am concerned, the code I have written for this software .\" can be used freely for any purpose. Any derived versions of this .\" software must be clearly marked as such, and if the derived work is .\" incompatible with the protocol description in the RFC file, it must be .\" called by a name other than "ssh" or "Secure Shell". .\" .\" Copyright (c) 1999,2000 Markus Friedl. All rights reserved. .\" Copyright (c) 1999 Aaron Campbell. All rights reserved. .\" Copyright (c) 1999 Theo de Raadt. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR .\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES .\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. .\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, .\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT .\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, .\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY .\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.Dd $Mdocdate: July 23 2023 $ +.Dd $Mdocdate: August 10 2023 $ .Dt SSH-AGENT 1 .Os .Sh NAME .Nm ssh-agent .Nd OpenSSH authentication agent .Sh SYNOPSIS .Nm ssh-agent .Op Fl c | s .Op Fl \&Dd .Op Fl a Ar bind_address .Op Fl E Ar fingerprint_hash .Op Fl O Ar option .Op Fl P Ar allowed_providers .Op Fl t Ar life .Nm ssh-agent .Op Fl a Ar bind_address .Op Fl E Ar fingerprint_hash .Op Fl O Ar option .Op Fl P Ar allowed_providers .Op Fl t Ar life .Ar command Op Ar arg ... .Nm ssh-agent .Op Fl c | s .Fl k .Sh DESCRIPTION .Nm is a program to hold private keys used for public key authentication. Through use of environment variables the agent can be located and automatically used for authentication when logging in to other machines using .Xr ssh 1 . .Pp The options are as follows: .Bl -tag -width Ds .It Fl a Ar bind_address Bind the agent to the .Ux Ns -domain socket .Ar bind_address . The default is .Pa $TMPDIR/ssh-XXXXXXXXXX/agent.\*(Ltppid\*(Gt . .It Fl c Generate C-shell commands on .Dv stdout . This is the default if .Ev SHELL looks like it's a csh style of shell. .It Fl D Foreground mode. When this option is specified, .Nm will not fork. .It Fl d Debug mode. When this option is specified, .Nm will not fork and will write debug information to standard error. .It Fl E Ar fingerprint_hash Specifies the hash algorithm used when displaying key fingerprints. Valid options are: .Dq md5 and .Dq sha256 . The default is .Dq sha256 . .It Fl k Kill the current agent (given by the .Ev SSH_AGENT_PID environment variable). .It Fl O Ar option Specify an option when starting .Nm . Currently two options are supported: .Cm allow-remote-pkcs11 and .Cm no-restrict-websafe . .Pp The .Cm allow-remote-pkcs11 option allows clients of a forwarded .Nm to load PKCS#11 or FIDO provider libraries. By default only local clients may perform this operation. Note that signalling that an .Nm client is remote is performed by .Xr ssh 1 , -and use of other tools to forward access to the agent socket, may circumvent +and use of other tools to forward access to the agent socket may circumvent this restriction. .Pp The .Cm no-restrict-websafe option instructs .Nm to permit signatures using FIDO keys that might be web authentication requests. By default, .Nm refuses signature requests for FIDO keys where the key application string does not start with .Dq ssh: and when the data to be signed does not appear to be a .Xr ssh 1 user authentication request or a .Xr ssh-keygen 1 signature. The default behaviour prevents forwarded access to a FIDO key from also implicitly forwarding the ability to authenticate to websites. .It Fl P Ar allowed_providers Specify a pattern-list of acceptable paths for PKCS#11 provider and FIDO authenticator middleware shared libraries that may be used with the .Fl S or .Fl s options to .Xr ssh-add 1 . Libraries that do not match the pattern list will be refused. See PATTERNS in .Xr ssh_config 5 for a description of pattern-list syntax. The default list is -.Dq /usr/lib/*,/usr/local/lib/* . +.Dq usr/lib*/*,/usr/local/lib*/* . .It Fl s Generate Bourne shell commands on .Dv stdout . This is the default if .Ev SHELL does not look like it's a csh style of shell. .It Fl t Ar life Set a default value for the maximum lifetime of identities added to the agent. The lifetime may be specified in seconds or in a time format specified in .Xr sshd_config 5 . A lifetime specified for an identity with .Xr ssh-add 1 overrides this value. Without this option the default maximum lifetime is forever. .It Ar command Op Ar arg ... If a command (and optional arguments) is given, this is executed as a subprocess of the agent. The agent exits automatically when the command given on the command line terminates. .El .Pp There are two main ways to get an agent set up. The first is at the start of an X session, where all other windows or programs are started as children of the .Nm program. The agent starts a command under which its environment variables are exported, for example .Cm ssh-agent xterm & . When the command terminates, so does the agent. .Pp The second method is used for a login session. When .Nm is started, it prints the shell commands required to set its environment variables, which in turn can be evaluated in the calling shell, for example .Cm eval `ssh-agent -s` . .Pp In both cases, .Xr ssh 1 looks at these environment variables and uses them to establish a connection to the agent. .Pp The agent initially does not have any private keys. Keys are added using .Xr ssh-add 1 or by .Xr ssh 1 when .Cm AddKeysToAgent is set in .Xr ssh_config 5 . Multiple identities may be stored in .Nm concurrently and .Xr ssh 1 will automatically use them if present. .Xr ssh-add 1 is also used to remove keys from .Nm and to query the keys that are held in one. .Pp Connections to .Nm may be forwarded from further remote hosts using the .Fl A option to .Xr ssh 1 (but see the caveats documented therein), avoiding the need for authentication data to be stored on other machines. Authentication passphrases and private keys never go over the network: the connection to the agent is forwarded over SSH remote connections and the result is returned to the requester, allowing the user access to their identities anywhere in the network in a secure fashion. .Sh ENVIRONMENT .Bl -tag -width "SSH_AGENT_PID" .It Ev SSH_AGENT_PID When .Nm starts, it stores the name of the agent's process ID (PID) in this variable. .It Ev SSH_AUTH_SOCK When .Nm starts, it creates a .Ux Ns -domain socket and stores its pathname in this variable. It is accessible only to the current user, but is easily abused by root or another instance of the same user. .El .Sh FILES .Bl -tag -width Ds .It Pa $TMPDIR/ssh-XXXXXXXXXX/agent. .Ux Ns -domain sockets used to contain the connection to the authentication agent. These sockets should only be readable by the owner. The sockets should get automatically removed when the agent exits. .El .Sh SEE ALSO .Xr ssh 1 , .Xr ssh-add 1 , .Xr ssh-keygen 1 , .Xr ssh_config 5 , .Xr sshd 8 .Sh AUTHORS .An -nosplit OpenSSH is a derivative of the original and free ssh 1.2.12 release by .An Tatu Ylonen . .An Aaron Campbell , Bob Beck , Markus Friedl , Niels Provos , Theo de Raadt and .An Dug Song removed many bugs, re-added newer features and created OpenSSH. .An Markus Friedl contributed the support for SSH protocol versions 1.5 and 2.0. diff --git a/ssh-keygen.0 b/ssh-keygen.0 index fbd389cf0e1b..95e4aa364ad1 100644 --- a/ssh-keygen.0 +++ b/ssh-keygen.0 @@ -1,910 +1,910 @@ SSH-KEYGEN(1) General Commands Manual SSH-KEYGEN(1) NAME ssh-keygen M-bM-^@M-^S OpenSSH authentication key utility SYNOPSIS ssh-keygen [-q] [-a rounds] [-b bits] [-C comment] [-f output_keyfile] [-m format] [-N new_passphrase] [-O option] [-t dsa | ecdsa | ecdsa-sk | ed25519 | ed25519-sk | rsa] [-w provider] [-Z cipher] ssh-keygen -p [-a rounds] [-f keyfile] [-m format] [-N new_passphrase] [-P old_passphrase] [-Z cipher] ssh-keygen -i [-f input_keyfile] [-m key_format] ssh-keygen -e [-f input_keyfile] [-m key_format] ssh-keygen -y [-f input_keyfile] ssh-keygen -c [-a rounds] [-C comment] [-f keyfile] [-P passphrase] ssh-keygen -l [-v] [-E fingerprint_hash] [-f input_keyfile] ssh-keygen -B [-f input_keyfile] ssh-keygen -D pkcs11 ssh-keygen -F hostname [-lv] [-f known_hosts_file] ssh-keygen -H [-f known_hosts_file] ssh-keygen -K [-a rounds] [-w provider] ssh-keygen -R hostname [-f known_hosts_file] ssh-keygen -r hostname [-g] [-f input_keyfile] ssh-keygen -M generate [-O option] output_file ssh-keygen -M screen [-f input_file] [-O option] output_file ssh-keygen -I certificate_identity -s ca_key [-hU] [-D pkcs11_provider] [-n principals] [-O option] [-V validity_interval] [-z serial_number] file ... ssh-keygen -L [-f input_keyfile] ssh-keygen -A [-a rounds] [-f prefix_path] ssh-keygen -k -f krl_file [-u] [-s ca_public] [-z version_number] file ... ssh-keygen -Q [-l] -f krl_file file ... ssh-keygen -Y find-principals [-O option] -s signature_file -f allowed_signers_file ssh-keygen -Y match-principals -I signer_identity -f allowed_signers_file ssh-keygen -Y check-novalidate [-O option] -n namespace -s signature_file ssh-keygen -Y sign [-O option] -f key_file -n namespace file ... ssh-keygen -Y verify [-O option] -f allowed_signers_file -I signer_identity -n namespace -s signature_file [-r revocation_file] DESCRIPTION ssh-keygen generates, manages and converts authentication keys for ssh(1). ssh-keygen can create keys for use by SSH protocol version 2. The type of key to be generated is specified with the -t option. If - invoked without any arguments, ssh-keygen will generate an RSA key. + invoked without any arguments, ssh-keygen will generate an Ed25519 key. ssh-keygen is also used to generate groups for use in Diffie-Hellman group exchange (DH-GEX). See the MODULI GENERATION section for details. Finally, ssh-keygen can be used to generate and update Key Revocation Lists, and to test whether given keys have been revoked by one. See the KEY REVOCATION LISTS section for details. Normally each user wishing to use SSH with public key authentication runs this once to create the authentication key in ~/.ssh/id_dsa, ~/.ssh/id_ecdsa, ~/.ssh/id_ecdsa_sk, ~/.ssh/id_ed25519, ~/.ssh/id_ed25519_sk or ~/.ssh/id_rsa. Additionally, the system administrator may use this to generate host keys, as seen in /etc/rc. Normally this program generates the key and asks for a file in which to store the private key. The public key is stored in a file with the same name but M-bM-^@M-^\.pubM-bM-^@M-^] appended. The program also asks for a passphrase. The passphrase may be empty to indicate no passphrase (host keys must have an empty passphrase), or it may be a string of arbitrary length. A passphrase is similar to a password, except it can be a phrase with a series of words, punctuation, numbers, whitespace, or any string of characters you want. Good passphrases are 10-30 characters long, are not simple sentences or otherwise easily guessable (English prose has only 1-2 bits of entropy per character, and provides very bad passphrases), and contain a mix of upper and lowercase letters, numbers, and non- alphanumeric characters. The passphrase can be changed later by using the -p option. There is no way to recover a lost passphrase. If the passphrase is lost or forgotten, a new key must be generated and the corresponding public key copied to other machines. ssh-keygen will by default write keys in an OpenSSH-specific format. This format is preferred as it offers better protection for keys at rest as well as allowing storage of key comments within the private key file itself. The key comment may be useful to help identify the key. The comment is initialized to M-bM-^@M-^\user@hostM-bM-^@M-^] when the key is created, but can be changed using the -c option. It is still possible for ssh-keygen to write the previously-used PEM format private keys using the -m flag. This may be used when generating new keys, and existing new-format keys may be converted using this option in conjunction with the -p (change passphrase) flag. After a key is generated, ssh-keygen will ask where the keys should be placed to be activated. The options are as follows: -A Generate host keys of all default key types (rsa, ecdsa, and ed25519) if they do not already exist. The host keys are generated with the default key file path, an empty passphrase, default bits for the key type, and default comment. If -f has also been specified, its argument is used as a prefix to the default path for the resulting host key files. This is used by /etc/rc to generate new host keys. -a rounds When saving a private key, this option specifies the number of KDF (key derivation function, currently bcrypt_pbkdf(3)) rounds used. Higher numbers result in slower passphrase verification and increased resistance to brute-force password cracking (should the keys be stolen). The default is 16 rounds. -B Show the bubblebabble digest of specified private or public key file. -b bits Specifies the number of bits in the key to create. For RSA keys, the minimum size is 1024 bits and the default is 3072 bits. Generally, 3072 bits is considered sufficient. DSA keys must be exactly 1024 bits as specified by FIPS 186-2. For ECDSA keys, the -b flag determines the key length by selecting from one of three elliptic curve sizes: 256, 384 or 521 bits. Attempting to use bit lengths other than these three values for ECDSA keys will fail. ECDSA-SK, Ed25519 and Ed25519-SK keys have a fixed length and the -b flag will be ignored. -C comment Provides a new comment. -c Requests changing the comment in the private and public key files. The program will prompt for the file containing the private keys, for the passphrase if the key has one, and for the new comment. -D pkcs11 Download the public keys provided by the PKCS#11 shared library pkcs11. When used in combination with -s, this option indicates that a CA key resides in a PKCS#11 token (see the CERTIFICATES section for details). -E fingerprint_hash Specifies the hash algorithm used when displaying key fingerprints. Valid options are: M-bM-^@M-^\md5M-bM-^@M-^] and M-bM-^@M-^\sha256M-bM-^@M-^]. The default is M-bM-^@M-^\sha256M-bM-^@M-^]. -e This option will read a private or public OpenSSH key file and print to stdout a public key in one of the formats specified by the -m option. The default export format is M-bM-^@M-^\RFC4716M-bM-^@M-^]. This option allows exporting OpenSSH keys for use by other programs, including several commercial SSH implementations. -F hostname | [hostname]:port Search for the specified hostname (with optional port number) in a known_hosts file, listing any occurrences found. This option is useful to find hashed host names or addresses and may also be used in conjunction with the -H option to print found keys in a hashed format. -f filename Specifies the filename of the key file. -g Use generic DNS format when printing fingerprint resource records using the -r command. -H Hash a known_hosts file. This replaces all hostnames and addresses with hashed representations within the specified file; the original content is moved to a file with a .old suffix. These hashes may be used normally by ssh and sshd, but they do not reveal identifying information should the file's contents be disclosed. This option will not modify existing hashed hostnames and is therefore safe to use on files that mix hashed and non- hashed names. -h When signing a key, create a host certificate instead of a user certificate. See the CERTIFICATES section for details. -I certificate_identity Specify the key identity when signing a public key. See the CERTIFICATES section for details. -i This option will read an unencrypted private (or public) key file in the format specified by the -m option and print an OpenSSH compatible private (or public) key to stdout. This option allows importing keys from other software, including several commercial SSH implementations. The default import format is M-bM-^@M-^\RFC4716M-bM-^@M-^]. -K Download resident keys from a FIDO authenticator. Public and private key files will be written to the current directory for each downloaded key. If multiple FIDO authenticators are attached, keys will be downloaded from the first touched authenticator. See the FIDO AUTHENTICATOR section for more information. -k Generate a KRL file. In this mode, ssh-keygen will generate a KRL file at the location specified via the -f flag that revokes every key or certificate presented on the command line. Keys/certificates to be revoked may be specified by public key file or using the format described in the KEY REVOCATION LISTS section. -L Prints the contents of one or more certificates. -l Show fingerprint of specified public key file. For RSA and DSA keys ssh-keygen tries to find the matching public key file and prints its fingerprint. If combined with -v, a visual ASCII art representation of the key is supplied with the fingerprint. -M generate Generate candidate Diffie-Hellman Group Exchange (DH-GEX) parameters for eventual use by the M-bM-^@M-^Xdiffie-hellman-group-exchange-*M-bM-^@M-^Y key exchange methods. The numbers generated by this operation must be further screened before use. See the MODULI GENERATION section for more information. -M screen Screen candidate parameters for Diffie-Hellman Group Exchange. This will accept a list of candidate numbers and test that they are safe (Sophie Germain) primes with acceptable group generators. The results of this operation may be added to the /etc/moduli file. See the MODULI GENERATION section for more information. -m key_format Specify a key format for key generation, the -i (import), -e (export) conversion options, and the -p change passphrase operation. The latter may be used to convert between OpenSSH private key and PEM private key formats. The supported key formats are: M-bM-^@M-^\RFC4716M-bM-^@M-^] (RFC 4716/SSH2 public or private key), M-bM-^@M-^\PKCS8M-bM-^@M-^] (PKCS8 public or private key) or M-bM-^@M-^\PEMM-bM-^@M-^] (PEM public key). By default OpenSSH will write newly-generated private keys in its own format, but when converting public keys for export the default format is M-bM-^@M-^\RFC4716M-bM-^@M-^]. Setting a format of M-bM-^@M-^\PEMM-bM-^@M-^] when generating or updating a supported private key type will cause the key to be stored in the legacy PEM private key format. -N new_passphrase Provides the new passphrase. -n principals Specify one or more principals (user or host names) to be included in a certificate when signing a key. Multiple principals may be specified, separated by commas. See the CERTIFICATES section for details. -O option Specify a key/value option. These are specific to the operation that ssh-keygen has been requested to perform. When signing certificates, one of the options listed in the CERTIFICATES section may be specified here. When performing moduli generation or screening, one of the options listed in the MODULI GENERATION section may be specified. When generating FIDO authenticator-backed keys, the options listed in the FIDO AUTHENTICATOR section may be specified. When performing signature-related options using the -Y flag, the following options are accepted: hashalg=algorithm Selects the hash algorithm to use for hashing the message to be signed. Valid algorithms are M-bM-^@M-^\sha256M-bM-^@M-^] and M-bM-^@M-^\sha512.M-bM-^@M-^] The default is M-bM-^@M-^\sha512.M-bM-^@M-^] print-pubkey Print the full public key to standard output after signature verification. verify-time=timestamp Specifies a time to use when validating signatures instead of the current time. The time may be specified as a date or time in the YYYYMMDD[Z] or in YYYYMMDDHHMM[SS][Z] formats. Dates and times will be interpreted in the current system time zone unless suffixed with a Z character, which causes them to be interpreted in the UTC time zone. When generating SSHFP DNS records from public keys using the -r flag, the following options are accepted: hashalg=algorithm Selects a hash algorithm to use when printing SSHFP records using the -D flag. Valid algorithms are M-bM-^@M-^\sha1M-bM-^@M-^] and M-bM-^@M-^\sha256M-bM-^@M-^]. The default is to print both. The -O option may be specified multiple times. -P passphrase Provides the (old) passphrase. -p Requests changing the passphrase of a private key file instead of creating a new private key. The program will prompt for the file containing the private key, for the old passphrase, and twice for the new passphrase. -Q Test whether keys have been revoked in a KRL. If the -l option is also specified then the contents of the KRL will be printed. -q Silence ssh-keygen. -R hostname | [hostname]:port Removes all keys belonging to the specified hostname (with optional port number) from a known_hosts file. This option is useful to delete hashed hosts (see the -H option above). -r hostname Print the SSHFP fingerprint resource record named hostname for the specified public key file. -s ca_key Certify (sign) a public key using the specified CA key. See the CERTIFICATES section for details. When generating a KRL, -s specifies a path to a CA public key file used to revoke certificates directly by key ID or serial number. See the KEY REVOCATION LISTS section for details. -t dsa | ecdsa | ecdsa-sk | ed25519 | ed25519-sk | rsa Specifies the type of key to create. The possible values are M-bM-^@M-^\dsaM-bM-^@M-^], M-bM-^@M-^\ecdsaM-bM-^@M-^], M-bM-^@M-^\ecdsa-skM-bM-^@M-^], M-bM-^@M-^\ed25519M-bM-^@M-^], M-bM-^@M-^\ed25519-skM-bM-^@M-^], or M-bM-^@M-^\rsaM-bM-^@M-^]. This flag may also be used to specify the desired signature type when signing certificates using an RSA CA key. The available RSA signature variants are M-bM-^@M-^\ssh-rsaM-bM-^@M-^] (SHA1 signatures, not recommended), M-bM-^@M-^\rsa-sha2-256M-bM-^@M-^], and M-bM-^@M-^\rsa-sha2-512M-bM-^@M-^] (the default). -U When used in combination with -s or -Y sign, this option indicates that a CA key resides in a ssh-agent(1). See the CERTIFICATES section for more information. -u Update a KRL. When specified with -k, keys listed via the command line are added to the existing KRL rather than a new KRL being created. -V validity_interval Specify a validity interval when signing a certificate. A validity interval may consist of a single time, indicating that the certificate is valid beginning now and expiring at that time, or may consist of two times separated by a colon to indicate an explicit time interval. The start time may be specified as: M-bM-^@M-M-bM-^@M-" The string M-bM-^@M-^\alwaysM-bM-^@M-^] to indicate the certificate has no specified start time. M-bM-^@M-M-bM-^@M-" A date or time in the system time zone formatted as YYYYMMDD or YYYYMMDDHHMM[SS]. M-bM-^@M-M-bM-^@M-" A date or time in the UTC time zone as YYYYMMDDZ or YYYYMMDDHHMM[SS]Z. M-bM-^@M-M-bM-^@M-" A relative time before the current system time consisting of a minus sign followed by an interval in the format described in the TIME FORMATS section of sshd_config(5). M-bM-^@M-M-bM-^@M-" A raw seconds since epoch (Jan 1 1970 00:00:00 UTC) as a hexadecimal number beginning with M-bM-^@M-^\0xM-bM-^@M-^]. The end time may be specified similarly to the start time: M-bM-^@M-M-bM-^@M-" The string M-bM-^@M-^\foreverM-bM-^@M-^] to indicate the certificate has no specified end time. M-bM-^@M-M-bM-^@M-" A date or time in the system time zone formatted as YYYYMMDD or YYYYMMDDHHMM[SS]. M-bM-^@M-M-bM-^@M-" A date or time in the UTC time zone as YYYYMMDDZ or YYYYMMDDHHMM[SS]Z. M-bM-^@M-M-bM-^@M-" A relative time after the current system time consisting of a plus sign followed by an interval in the format described in the TIME FORMATS section of sshd_config(5). M-bM-^@M-M-bM-^@M-" A raw seconds since epoch (Jan 1 1970 00:00:00 UTC) as a hexadecimal number beginning with M-bM-^@M-^\0xM-bM-^@M-^]. For example: +52w1d Valid from now to 52 weeks and one day from now. -4w:+4w Valid from four weeks ago to four weeks from now. 20100101123000:20110101123000 Valid from 12:30 PM, January 1st, 2010 to 12:30 PM, January 1st, 2011. 20100101123000Z:20110101123000Z Similar, but interpreted in the UTC time zone rather than the system time zone. -1d:20110101 Valid from yesterday to midnight, January 1st, 2011. 0x1:0x2000000000 Valid from roughly early 1970 to May 2033. -1m:forever Valid from one minute ago and never expiring. -v Verbose mode. Causes ssh-keygen to print debugging messages about its progress. This is helpful for debugging moduli generation. Multiple -v options increase the verbosity. The maximum is 3. -w provider Specifies a path to a library that will be used when creating FIDO authenticator-hosted keys, overriding the default of using the internal USB HID support. -Y find-principals Find the principal(s) associated with the public key of a signature, provided using the -s flag in an authorized signers file provided using the -f flag. The format of the allowed signers file is documented in the ALLOWED SIGNERS section below. If one or more matching principals are found, they are returned on standard output. -Y match-principals Find principal matching the principal name provided using the -I flag in the authorized signers file specified using the -f flag. If one or more matching principals are found, they are returned on standard output. -Y check-novalidate Checks that a signature generated using ssh-keygen -Y sign has a valid structure. This does not validate if a signature comes from an authorized signer. When testing a signature, ssh-keygen accepts a message on standard input and a signature namespace using -n. A file containing the corresponding signature must also be supplied using the -s flag. Successful testing of the signature is signalled by ssh-keygen returning a zero exit status. -Y sign Cryptographically sign a file or some data using an SSH key. When signing, ssh-keygen accepts zero or more files to sign on the command-line - if no files are specified then ssh-keygen will sign data presented on standard input. Signatures are written to the path of the input file with M-bM-^@M-^\.sigM-bM-^@M-^] appended, or to standard output if the message to be signed was read from standard input. The key used for signing is specified using the -f option and may refer to either a private key, or a public key with the private half available via ssh-agent(1). An additional signature namespace, used to prevent signature confusion across different domains of use (e.g. file signing vs email signing) must be provided via the -n flag. Namespaces are arbitrary strings, and may include: M-bM-^@M-^\fileM-bM-^@M-^] for file signing, M-bM-^@M-^\emailM-bM-^@M-^] for email signing. For custom uses, it is recommended to use names following a NAMESPACE@YOUR.DOMAIN pattern to generate unambiguous namespaces. -Y verify Request to verify a signature generated using ssh-keygen -Y sign as described above. When verifying a signature, ssh-keygen accepts a message on standard input and a signature namespace using -n. A file containing the corresponding signature must also be supplied using the -s flag, along with the identity of the signer using -I and a list of allowed signers via the -f flag. The format of the allowed signers file is documented in the ALLOWED SIGNERS section below. A file containing revoked keys can be passed using the -r flag. The revocation file may be a KRL or a one-per-line list of public keys. Successful verification by an authorized signer is signalled by ssh-keygen returning a zero exit status. -y This option will read a private OpenSSH format file and print an OpenSSH public key to stdout. -Z cipher Specifies the cipher to use for encryption when writing an OpenSSH-format private key file. The list of available ciphers may be obtained using "ssh -Q cipher". The default is M-bM-^@M-^\aes256-ctrM-bM-^@M-^]. -z serial_number Specifies a serial number to be embedded in the certificate to distinguish this certificate from others from the same CA. If the serial_number is prefixed with a M-bM-^@M-^X+M-bM-^@M-^Y character, then the serial number will be incremented for each certificate signed on a single command-line. The default serial number is zero. When generating a KRL, the -z flag is used to specify a KRL version number. MODULI GENERATION ssh-keygen may be used to generate groups for the Diffie-Hellman Group Exchange (DH-GEX) protocol. Generating these groups is a two-step process: first, candidate primes are generated using a fast, but memory intensive process. These candidate primes are then tested for suitability (a CPU-intensive process). Generation of primes is performed using the -M generate option. The desired length of the primes may be specified by the -O bits option. For example: # ssh-keygen -M generate -O bits=2048 moduli-2048.candidates By default, the search for primes begins at a random point in the desired length range. This may be overridden using the -O start option, which specifies a different start point (in hex). Once a set of candidates have been generated, they must be screened for suitability. This may be performed using the -M screen option. In this mode ssh-keygen will read candidates from standard input (or a file specified using the -f option). For example: # ssh-keygen -M screen -f moduli-2048.candidates moduli-2048 By default, each candidate will be subjected to 100 primality tests. This may be overridden using the -O prime-tests option. The DH generator value will be chosen automatically for the prime under consideration. If a specific generator is desired, it may be requested using the -O generator option. Valid generator values are 2, 3, and 5. Screened DH groups may be installed in /etc/moduli. It is important that this file contains moduli of a range of bit lengths. A number of options are available for moduli generation and screening via the -O flag: lines=number Exit after screening the specified number of lines while performing DH candidate screening. start-line=line-number Start screening at the specified line number while performing DH candidate screening. checkpoint=filename Write the last line processed to the specified file while performing DH candidate screening. This will be used to skip lines in the input file that have already been processed if the job is restarted. memory=mbytes Specify the amount of memory to use (in megabytes) when generating candidate moduli for DH-GEX. start=hex-value Specify start point (in hex) when generating candidate moduli for DH-GEX. generator=value Specify desired generator (in decimal) when testing candidate moduli for DH-GEX. CERTIFICATES ssh-keygen supports signing of keys to produce certificates that may be used for user or host authentication. Certificates consist of a public key, some identity information, zero or more principal (user or host) names and a set of options that are signed by a Certification Authority (CA) key. Clients or servers may then trust only the CA key and verify its signature on a certificate rather than trusting many user/host keys. Note that OpenSSH certificates are a different, and much simpler, format to the X.509 certificates used in ssl(8). ssh-keygen supports two types of certificates: user and host. User certificates authenticate users to servers, whereas host certificates authenticate server hosts to users. To generate a user certificate: $ ssh-keygen -s /path/to/ca_key -I key_id /path/to/user_key.pub The resultant certificate will be placed in /path/to/user_key-cert.pub. A host certificate requires the -h option: $ ssh-keygen -s /path/to/ca_key -I key_id -h /path/to/host_key.pub The host certificate will be output to /path/to/host_key-cert.pub. It is possible to sign using a CA key stored in a PKCS#11 token by providing the token library using -D and identifying the CA key by providing its public half as an argument to -s: $ ssh-keygen -s ca_key.pub -D libpkcs11.so -I key_id user_key.pub Similarly, it is possible for the CA key to be hosted in a ssh-agent(1). This is indicated by the -U flag and, again, the CA key must be identified by its public half. $ ssh-keygen -Us ca_key.pub -I key_id user_key.pub In all cases, key_id is a "key identifier" that is logged by the server when the certificate is used for authentication. Certificates may be limited to be valid for a set of principal (user/host) names. By default, generated certificates are valid for all users or hosts. To generate a certificate for a specified set of principals: $ ssh-keygen -s ca_key -I key_id -n user1,user2 user_key.pub $ ssh-keygen -s ca_key -I key_id -h -n host.domain host_key.pub Additional limitations on the validity and use of user certificates may be specified through certificate options. A certificate option may disable features of the SSH session, may be valid only when presented from particular source addresses or may force the use of a specific command. The options that are valid for user certificates are: clear Clear all enabled permissions. This is useful for clearing the default set of permissions so permissions may be added individually. critical:name[=contents] extension:name[=contents] Includes an arbitrary certificate critical option or extension. The specified name should include a domain suffix, e.g. M-bM-^@M-^\name@example.comM-bM-^@M-^]. If contents is specified then it is included as the contents of the extension/option encoded as a string, otherwise the extension/option is created with no contents (usually indicating a flag). Extensions may be ignored by a client or server that does not recognise them, whereas unknown critical options will cause the certificate to be refused. force-command=command Forces the execution of command instead of any shell or command specified by the user when the certificate is used for authentication. no-agent-forwarding Disable ssh-agent(1) forwarding (permitted by default). no-port-forwarding Disable port forwarding (permitted by default). no-pty Disable PTY allocation (permitted by default). no-user-rc Disable execution of ~/.ssh/rc by sshd(8) (permitted by default). no-x11-forwarding Disable X11 forwarding (permitted by default). permit-agent-forwarding Allows ssh-agent(1) forwarding. permit-port-forwarding Allows port forwarding. permit-pty Allows PTY allocation. permit-user-rc Allows execution of ~/.ssh/rc by sshd(8). permit-X11-forwarding Allows X11 forwarding. no-touch-required Do not require signatures made using this key include demonstration of user presence (e.g. by having the user touch the authenticator). This option only makes sense for the FIDO authenticator algorithms ecdsa-sk and ed25519-sk. source-address=address_list Restrict the source addresses from which the certificate is considered valid. The address_list is a comma-separated list of one or more address/netmask pairs in CIDR format. verify-required Require signatures made using this key indicate that the user was first verified. This option only makes sense for the FIDO authenticator algorithms ecdsa-sk and ed25519-sk. Currently PIN authentication is the only supported verification method, but other methods may be supported in the future. At present, no standard options are valid for host keys. Finally, certificates may be defined with a validity lifetime. The -V option allows specification of certificate start and end times. A certificate that is presented at a time outside this range will not be considered valid. By default, certificates are valid from the UNIX Epoch to the distant future. For certificates to be used for user or host authentication, the CA public key must be trusted by sshd(8) or ssh(1). Refer to those manual pages for details. FIDO AUTHENTICATOR ssh-keygen is able to generate FIDO authenticator-backed keys, after which they may be used much like any other key type supported by OpenSSH, so long as the hardware authenticator is attached when the keys are used. FIDO authenticators generally require the user to explicitly authorise operations by touching or tapping them. FIDO keys consist of two parts: a key handle part stored in the private key file on disk, and a per- device private key that is unique to each FIDO authenticator and that cannot be exported from the authenticator hardware. These are combined by the hardware at authentication time to derive the real key that is used to sign authentication challenges. Supported key types are ecdsa-sk and ed25519-sk. The options that are valid for FIDO keys are: application Override the default FIDO application/origin string of M-bM-^@M-^\ssh:M-bM-^@M-^]. This may be useful when generating host or domain-specific resident keys. The specified application string must begin with M-bM-^@M-^\ssh:M-bM-^@M-^]. challenge=path Specifies a path to a challenge string that will be passed to the FIDO authenticator during key generation. The challenge string may be used as part of an out-of-band protocol for key enrollment (a random challenge is used by default). device Explicitly specify a fido(4) device to use, rather than letting the authenticator middleware select one. no-touch-required Indicate that the generated private key should not require touch events (user presence) when making signatures. Note that sshd(8) will refuse such signatures by default, unless overridden via an authorized_keys option. resident Indicate that the key handle should be stored on the FIDO authenticator itself. This makes it easier to use the authenticator on multiple computers. Resident keys may be supported on FIDO2 authenticators and typically require that a PIN be set on the authenticator prior to generation. Resident keys may be loaded off the authenticator using ssh-add(1). Storing both parts of a key on a FIDO authenticator increases the likelihood of an attacker being able to use a stolen authenticator device. user A username to be associated with a resident key, overriding the empty default username. Specifying a username may be useful when generating multiple resident keys for the same application name. verify-required Indicate that this private key should require user verification for each signature. Not all FIDO authenticators support this option. Currently PIN authentication is the only supported verification method, but other methods may be supported in the future. write-attestation=path May be used at key generation time to record the attestation data returned from FIDO authenticators during key generation. This information is potentially sensitive. By default, this information is discarded. KEY REVOCATION LISTS ssh-keygen is able to manage OpenSSH format Key Revocation Lists (KRLs). These binary files specify keys or certificates to be revoked using a compact format, taking as little as one bit per certificate if they are being revoked by serial number. KRLs may be generated using the -k flag. This option reads one or more files from the command line and generates a new KRL. The files may either contain a KRL specification (see below) or public keys, listed one per line. Plain public keys are revoked by listing their hash or contents in the KRL and certificates revoked by serial number or key ID (if the serial is zero or not available). Revoking keys using a KRL specification offers explicit control over the types of record used to revoke keys and may be used to directly revoke certificates by serial number or key ID without having the complete original certificate on hand. A KRL specification consists of lines containing one of the following directives followed by a colon and some directive-specific information. serial: serial_number[-serial_number] Revokes a certificate with the specified serial number. Serial numbers are 64-bit values, not including zero and may be expressed in decimal, hex or octal. If two serial numbers are specified separated by a hyphen, then the range of serial numbers including and between each is revoked. The CA key must have been specified on the ssh-keygen command line using the -s option. id: key_id Revokes a certificate with the specified key ID string. The CA key must have been specified on the ssh-keygen command line using the -s option. key: public_key Revokes the specified key. If a certificate is listed, then it is revoked as a plain public key. sha1: public_key Revokes the specified key by including its SHA1 hash in the KRL. sha256: public_key Revokes the specified key by including its SHA256 hash in the KRL. KRLs that revoke keys by SHA256 hash are not supported by OpenSSH versions prior to 7.9. hash: fingerprint Revokes a key using a fingerprint hash, as obtained from a sshd(8) authentication log message or the ssh-keygen -l flag. Only SHA256 fingerprints are supported here and resultant KRLs are not supported by OpenSSH versions prior to 7.9. KRLs may be updated using the -u flag in addition to -k. When this option is specified, keys listed via the command line are merged into the KRL, adding to those already there. It is also possible, given a KRL, to test whether it revokes a particular key (or keys). The -Q flag will query an existing KRL, testing each key specified on the command line. If any key listed on the command line has been revoked (or an error encountered) then ssh-keygen will exit with a non-zero exit status. A zero exit status will only be returned if no key was revoked. ALLOWED SIGNERS When verifying signatures, ssh-keygen uses a simple list of identities and keys to determine whether a signature comes from an authorized source. This "allowed signers" file uses a format patterned after the AUTHORIZED_KEYS FILE FORMAT described in sshd(8). Each line of the file contains the following space-separated fields: principals, options, keytype, base64-encoded key. Empty lines and lines starting with a M-bM-^@M-^X#M-bM-^@M-^Y are ignored as comments. The principals field is a pattern-list (see PATTERNS in ssh_config(5)) consisting of one or more comma-separated USER@DOMAIN identity patterns that are accepted for signing. When verifying, the identity presented via the -I option must match a principals pattern in order for the corresponding key to be considered acceptable for verification. The options (if present) consist of comma-separated option specifications. No spaces are permitted, except within double quotes. The following option specifications are supported (note that option keywords are case-insensitive): cert-authority Indicates that this key is accepted as a certificate authority (CA) and that certificates signed by this CA may be accepted for verification. namespaces=namespace-list Specifies a pattern-list of namespaces that are accepted for this key. If this option is present, the signature namespace embedded in the signature object and presented on the verification command-line must match the specified list before the key will be considered acceptable. valid-after=timestamp Indicates that the key is valid for use at or after the specified timestamp, which may be a date or time in the YYYYMMDD[Z] or YYYYMMDDHHMM[SS][Z] formats. Dates and times will be interpreted in the current system time zone unless suffixed with a Z character, which causes them to be interpreted in the UTC time zone. valid-before=timestamp Indicates that the key is valid for use at or before the specified timestamp. When verifying signatures made by certificates, the expected principal name must match both the principals pattern in the allowed signers file and the principals embedded in the certificate itself. An example allowed signers file: # Comments allowed at start of line user1@example.com,user2@example.com ssh-rsa AAAAX1... # A certificate authority, trusted for all principals in a domain. *@example.com cert-authority ssh-ed25519 AAAB4... # A key that is accepted only for file signing. user2@example.com namespaces="file" ssh-ed25519 AAA41... ENVIRONMENT SSH_SK_PROVIDER Specifies a path to a library that will be used when loading any FIDO authenticator-hosted keys, overriding the default of using the built-in USB HID support. FILES ~/.ssh/id_dsa ~/.ssh/id_ecdsa ~/.ssh/id_ecdsa_sk ~/.ssh/id_ed25519 ~/.ssh/id_ed25519_sk ~/.ssh/id_rsa Contains the DSA, ECDSA, authenticator-hosted ECDSA, Ed25519, authenticator-hosted Ed25519 or RSA authentication identity of the user. This file should not be readable by anyone but the user. It is possible to specify a passphrase when generating the key; that passphrase will be used to encrypt the private part of this file using 128-bit AES. This file is not automatically accessed by ssh-keygen but it is offered as the default file for the private key. ssh(1) will read this file when a login attempt is made. ~/.ssh/id_dsa.pub ~/.ssh/id_ecdsa.pub ~/.ssh/id_ecdsa_sk.pub ~/.ssh/id_ed25519.pub ~/.ssh/id_ed25519_sk.pub ~/.ssh/id_rsa.pub Contains the DSA, ECDSA, authenticator-hosted ECDSA, Ed25519, authenticator-hosted Ed25519 or RSA public key for authentication. The contents of this file should be added to ~/.ssh/authorized_keys on all machines where the user wishes to log in using public key authentication. There is no need to keep the contents of this file secret. /etc/moduli Contains Diffie-Hellman groups used for DH-GEX. The file format is described in moduli(5). SEE ALSO ssh(1), ssh-add(1), ssh-agent(1), moduli(5), sshd(8) The Secure Shell (SSH) Public Key File Format, RFC 4716, 2006. AUTHORS OpenSSH is a derivative of the original and free ssh 1.2.12 release by Tatu Ylonen. Aaron Campbell, Bob Beck, Markus Friedl, Niels Provos, Theo de Raadt and Dug Song removed many bugs, re-added newer features and created OpenSSH. Markus Friedl contributed the support for SSH protocol versions 1.5 and 2.0. -OpenBSD 7.3 July 23, 2023 OpenBSD 7.3 +OpenBSD 7.3 September 4, 2023 OpenBSD 7.3 diff --git a/ssh-keygen.1 b/ssh-keygen.1 index c760f91be14f..c392141ea127 100644 --- a/ssh-keygen.1 +++ b/ssh-keygen.1 @@ -1,1349 +1,1349 @@ -.\" $OpenBSD: ssh-keygen.1,v 1.229 2023/07/23 20:04:45 naddy Exp $ +.\" $OpenBSD: ssh-keygen.1,v 1.230 2023/09/04 10:29:58 job Exp $ .\" .\" Author: Tatu Ylonen .\" Copyright (c) 1995 Tatu Ylonen , Espoo, Finland .\" All rights reserved .\" .\" As far as I am concerned, the code I have written for this software .\" can be used freely for any purpose. Any derived versions of this .\" software must be clearly marked as such, and if the derived work is .\" incompatible with the protocol description in the RFC file, it must be .\" called by a name other than "ssh" or "Secure Shell". .\" .\" .\" Copyright (c) 1999,2000 Markus Friedl. All rights reserved. .\" Copyright (c) 1999 Aaron Campbell. All rights reserved. .\" Copyright (c) 1999 Theo de Raadt. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR .\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES .\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. .\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, .\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT .\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, .\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY .\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.Dd $Mdocdate: July 23 2023 $ +.Dd $Mdocdate: September 4 2023 $ .Dt SSH-KEYGEN 1 .Os .Sh NAME .Nm ssh-keygen .Nd OpenSSH authentication key utility .Sh SYNOPSIS .Nm ssh-keygen .Op Fl q .Op Fl a Ar rounds .Op Fl b Ar bits .Op Fl C Ar comment .Op Fl f Ar output_keyfile .Op Fl m Ar format .Op Fl N Ar new_passphrase .Op Fl O Ar option .Op Fl t Cm dsa | ecdsa | ecdsa-sk | ed25519 | ed25519-sk | rsa .Op Fl w Ar provider .Op Fl Z Ar cipher .Nm ssh-keygen .Fl p .Op Fl a Ar rounds .Op Fl f Ar keyfile .Op Fl m Ar format .Op Fl N Ar new_passphrase .Op Fl P Ar old_passphrase .Op Fl Z Ar cipher .Nm ssh-keygen .Fl i .Op Fl f Ar input_keyfile .Op Fl m Ar key_format .Nm ssh-keygen .Fl e .Op Fl f Ar input_keyfile .Op Fl m Ar key_format .Nm ssh-keygen .Fl y .Op Fl f Ar input_keyfile .Nm ssh-keygen .Fl c .Op Fl a Ar rounds .Op Fl C Ar comment .Op Fl f Ar keyfile .Op Fl P Ar passphrase .Nm ssh-keygen .Fl l .Op Fl v .Op Fl E Ar fingerprint_hash .Op Fl f Ar input_keyfile .Nm ssh-keygen .Fl B .Op Fl f Ar input_keyfile .Nm ssh-keygen .Fl D Ar pkcs11 .Nm ssh-keygen .Fl F Ar hostname .Op Fl lv .Op Fl f Ar known_hosts_file .Nm ssh-keygen .Fl H .Op Fl f Ar known_hosts_file .Nm ssh-keygen .Fl K .Op Fl a Ar rounds .Op Fl w Ar provider .Nm ssh-keygen .Fl R Ar hostname .Op Fl f Ar known_hosts_file .Nm ssh-keygen .Fl r Ar hostname .Op Fl g .Op Fl f Ar input_keyfile .Nm ssh-keygen .Fl M Cm generate .Op Fl O Ar option .Ar output_file .Nm ssh-keygen .Fl M Cm screen .Op Fl f Ar input_file .Op Fl O Ar option .Ar output_file .Nm ssh-keygen .Fl I Ar certificate_identity .Fl s Ar ca_key .Op Fl hU .Op Fl D Ar pkcs11_provider .Op Fl n Ar principals .Op Fl O Ar option .Op Fl V Ar validity_interval .Op Fl z Ar serial_number .Ar .Nm ssh-keygen .Fl L .Op Fl f Ar input_keyfile .Nm ssh-keygen .Fl A .Op Fl a Ar rounds .Op Fl f Ar prefix_path .Nm ssh-keygen .Fl k .Fl f Ar krl_file .Op Fl u .Op Fl s Ar ca_public .Op Fl z Ar version_number .Ar .Nm ssh-keygen .Fl Q .Op Fl l .Fl f Ar krl_file .Ar .Nm ssh-keygen .Fl Y Cm find-principals .Op Fl O Ar option .Fl s Ar signature_file .Fl f Ar allowed_signers_file .Nm ssh-keygen .Fl Y Cm match-principals .Fl I Ar signer_identity .Fl f Ar allowed_signers_file .Nm ssh-keygen .Fl Y Cm check-novalidate .Op Fl O Ar option .Fl n Ar namespace .Fl s Ar signature_file .Nm ssh-keygen .Fl Y Cm sign .Op Fl O Ar option .Fl f Ar key_file .Fl n Ar namespace .Ar .Nm ssh-keygen .Fl Y Cm verify .Op Fl O Ar option .Fl f Ar allowed_signers_file .Fl I Ar signer_identity .Fl n Ar namespace .Fl s Ar signature_file .Op Fl r Ar revocation_file .Sh DESCRIPTION .Nm generates, manages and converts authentication keys for .Xr ssh 1 . .Nm can create keys for use by SSH protocol version 2. .Pp The type of key to be generated is specified with the .Fl t option. If invoked without any arguments, .Nm -will generate an RSA key. +will generate an Ed25519 key. .Pp .Nm is also used to generate groups for use in Diffie-Hellman group exchange (DH-GEX). See the .Sx MODULI GENERATION section for details. .Pp Finally, .Nm can be used to generate and update Key Revocation Lists, and to test whether given keys have been revoked by one. See the .Sx KEY REVOCATION LISTS section for details. .Pp Normally each user wishing to use SSH with public key authentication runs this once to create the authentication key in .Pa ~/.ssh/id_dsa , .Pa ~/.ssh/id_ecdsa , .Pa ~/.ssh/id_ecdsa_sk , .Pa ~/.ssh/id_ed25519 , .Pa ~/.ssh/id_ed25519_sk or .Pa ~/.ssh/id_rsa . Additionally, the system administrator may use this to generate host keys, as seen in .Pa /etc/rc . .Pp Normally this program generates the key and asks for a file in which to store the private key. The public key is stored in a file with the same name but .Dq .pub appended. The program also asks for a passphrase. The passphrase may be empty to indicate no passphrase (host keys must have an empty passphrase), or it may be a string of arbitrary length. A passphrase is similar to a password, except it can be a phrase with a series of words, punctuation, numbers, whitespace, or any string of characters you want. Good passphrases are 10-30 characters long, are not simple sentences or otherwise easily guessable (English prose has only 1-2 bits of entropy per character, and provides very bad passphrases), and contain a mix of upper and lowercase letters, numbers, and non-alphanumeric characters. The passphrase can be changed later by using the .Fl p option. .Pp There is no way to recover a lost passphrase. If the passphrase is lost or forgotten, a new key must be generated and the corresponding public key copied to other machines. .Pp .Nm will by default write keys in an OpenSSH-specific format. This format is preferred as it offers better protection for keys at rest as well as allowing storage of key comments within the private key file itself. The key comment may be useful to help identify the key. The comment is initialized to .Dq user@host when the key is created, but can be changed using the .Fl c option. .Pp It is still possible for .Nm to write the previously-used PEM format private keys using the .Fl m flag. This may be used when generating new keys, and existing new-format keys may be converted using this option in conjunction with the .Fl p (change passphrase) flag. .Pp After a key is generated, .Nm will ask where the keys should be placed to be activated. .Pp The options are as follows: .Bl -tag -width Ds .It Fl A Generate host keys of all default key types (rsa, ecdsa, and ed25519) if they do not already exist. The host keys are generated with the default key file path, an empty passphrase, default bits for the key type, and default comment. If .Fl f has also been specified, its argument is used as a prefix to the default path for the resulting host key files. This is used by .Pa /etc/rc to generate new host keys. .It Fl a Ar rounds When saving a private key, this option specifies the number of KDF (key derivation function, currently .Xr bcrypt_pbkdf 3 ) rounds used. Higher numbers result in slower passphrase verification and increased resistance to brute-force password cracking (should the keys be stolen). The default is 16 rounds. .It Fl B Show the bubblebabble digest of specified private or public key file. .It Fl b Ar bits Specifies the number of bits in the key to create. For RSA keys, the minimum size is 1024 bits and the default is 3072 bits. Generally, 3072 bits is considered sufficient. DSA keys must be exactly 1024 bits as specified by FIPS 186-2. For ECDSA keys, the .Fl b flag determines the key length by selecting from one of three elliptic curve sizes: 256, 384 or 521 bits. Attempting to use bit lengths other than these three values for ECDSA keys will fail. ECDSA-SK, Ed25519 and Ed25519-SK keys have a fixed length and the .Fl b flag will be ignored. .It Fl C Ar comment Provides a new comment. .It Fl c Requests changing the comment in the private and public key files. The program will prompt for the file containing the private keys, for the passphrase if the key has one, and for the new comment. .It Fl D Ar pkcs11 Download the public keys provided by the PKCS#11 shared library .Ar pkcs11 . When used in combination with .Fl s , this option indicates that a CA key resides in a PKCS#11 token (see the .Sx CERTIFICATES section for details). .It Fl E Ar fingerprint_hash Specifies the hash algorithm used when displaying key fingerprints. Valid options are: .Dq md5 and .Dq sha256 . The default is .Dq sha256 . .It Fl e This option will read a private or public OpenSSH key file and print to stdout a public key in one of the formats specified by the .Fl m option. The default export format is .Dq RFC4716 . This option allows exporting OpenSSH keys for use by other programs, including several commercial SSH implementations. .It Fl F Ar hostname | [hostname]:port Search for the specified .Ar hostname (with optional port number) in a .Pa known_hosts file, listing any occurrences found. This option is useful to find hashed host names or addresses and may also be used in conjunction with the .Fl H option to print found keys in a hashed format. .It Fl f Ar filename Specifies the filename of the key file. .It Fl g Use generic DNS format when printing fingerprint resource records using the .Fl r command. .It Fl H Hash a .Pa known_hosts file. This replaces all hostnames and addresses with hashed representations within the specified file; the original content is moved to a file with a .old suffix. These hashes may be used normally by .Nm ssh and .Nm sshd , but they do not reveal identifying information should the file's contents be disclosed. This option will not modify existing hashed hostnames and is therefore safe to use on files that mix hashed and non-hashed names. .It Fl h When signing a key, create a host certificate instead of a user certificate. See the .Sx CERTIFICATES section for details. .It Fl I Ar certificate_identity Specify the key identity when signing a public key. See the .Sx CERTIFICATES section for details. .It Fl i This option will read an unencrypted private (or public) key file in the format specified by the .Fl m option and print an OpenSSH compatible private (or public) key to stdout. This option allows importing keys from other software, including several commercial SSH implementations. The default import format is .Dq RFC4716 . .It Fl K Download resident keys from a FIDO authenticator. Public and private key files will be written to the current directory for each downloaded key. If multiple FIDO authenticators are attached, keys will be downloaded from the first touched authenticator. See the .Sx FIDO AUTHENTICATOR section for more information. .It Fl k Generate a KRL file. In this mode, .Nm will generate a KRL file at the location specified via the .Fl f flag that revokes every key or certificate presented on the command line. Keys/certificates to be revoked may be specified by public key file or using the format described in the .Sx KEY REVOCATION LISTS section. .It Fl L Prints the contents of one or more certificates. .It Fl l Show fingerprint of specified public key file. For RSA and DSA keys .Nm tries to find the matching public key file and prints its fingerprint. If combined with .Fl v , a visual ASCII art representation of the key is supplied with the fingerprint. .It Fl M Cm generate Generate candidate Diffie-Hellman Group Exchange (DH-GEX) parameters for eventual use by the .Sq diffie-hellman-group-exchange-* key exchange methods. The numbers generated by this operation must be further screened before use. See the .Sx MODULI GENERATION section for more information. .It Fl M Cm screen Screen candidate parameters for Diffie-Hellman Group Exchange. This will accept a list of candidate numbers and test that they are safe (Sophie Germain) primes with acceptable group generators. The results of this operation may be added to the .Pa /etc/moduli file. See the .Sx MODULI GENERATION section for more information. .It Fl m Ar key_format Specify a key format for key generation, the .Fl i (import), .Fl e (export) conversion options, and the .Fl p change passphrase operation. The latter may be used to convert between OpenSSH private key and PEM private key formats. The supported key formats are: .Dq RFC4716 (RFC 4716/SSH2 public or private key), .Dq PKCS8 (PKCS8 public or private key) or .Dq PEM (PEM public key). By default OpenSSH will write newly-generated private keys in its own format, but when converting public keys for export the default format is .Dq RFC4716 . Setting a format of .Dq PEM when generating or updating a supported private key type will cause the key to be stored in the legacy PEM private key format. .It Fl N Ar new_passphrase Provides the new passphrase. .It Fl n Ar principals Specify one or more principals (user or host names) to be included in a certificate when signing a key. Multiple principals may be specified, separated by commas. See the .Sx CERTIFICATES section for details. .It Fl O Ar option Specify a key/value option. These are specific to the operation that .Nm has been requested to perform. .Pp When signing certificates, one of the options listed in the .Sx CERTIFICATES section may be specified here. .Pp When performing moduli generation or screening, one of the options listed in the .Sx MODULI GENERATION section may be specified. .Pp When generating FIDO authenticator-backed keys, the options listed in the .Sx FIDO AUTHENTICATOR section may be specified. .Pp When performing signature-related options using the .Fl Y flag, the following options are accepted: .Bl -tag -width Ds .It Cm hashalg Ns = Ns Ar algorithm Selects the hash algorithm to use for hashing the message to be signed. Valid algorithms are .Dq sha256 and .Dq sha512. The default is .Dq sha512. .It Cm print-pubkey Print the full public key to standard output after signature verification. .It Cm verify-time Ns = Ns Ar timestamp Specifies a time to use when validating signatures instead of the current time. The time may be specified as a date or time in the YYYYMMDD[Z] or in YYYYMMDDHHMM[SS][Z] formats. Dates and times will be interpreted in the current system time zone unless suffixed with a Z character, which causes them to be interpreted in the UTC time zone. .El .Pp When generating SSHFP DNS records from public keys using the .Fl r flag, the following options are accepted: .Bl -tag -width Ds .It Cm hashalg Ns = Ns Ar algorithm Selects a hash algorithm to use when printing SSHFP records using the .Fl D flag. Valid algorithms are .Dq sha1 and .Dq sha256 . The default is to print both. .El .Pp The .Fl O option may be specified multiple times. .It Fl P Ar passphrase Provides the (old) passphrase. .It Fl p Requests changing the passphrase of a private key file instead of creating a new private key. The program will prompt for the file containing the private key, for the old passphrase, and twice for the new passphrase. .It Fl Q Test whether keys have been revoked in a KRL. If the .Fl l option is also specified then the contents of the KRL will be printed. .It Fl q Silence .Nm ssh-keygen . .It Fl R Ar hostname | [hostname]:port Removes all keys belonging to the specified .Ar hostname (with optional port number) from a .Pa known_hosts file. This option is useful to delete hashed hosts (see the .Fl H option above). .It Fl r Ar hostname Print the SSHFP fingerprint resource record named .Ar hostname for the specified public key file. .It Fl s Ar ca_key Certify (sign) a public key using the specified CA key. See the .Sx CERTIFICATES section for details. .Pp When generating a KRL, .Fl s specifies a path to a CA public key file used to revoke certificates directly by key ID or serial number. See the .Sx KEY REVOCATION LISTS section for details. .It Fl t Cm dsa | ecdsa | ecdsa-sk | ed25519 | ed25519-sk | rsa Specifies the type of key to create. The possible values are .Dq dsa , .Dq ecdsa , .Dq ecdsa-sk , .Dq ed25519 , .Dq ed25519-sk , or .Dq rsa . .Pp This flag may also be used to specify the desired signature type when signing certificates using an RSA CA key. The available RSA signature variants are .Dq ssh-rsa (SHA1 signatures, not recommended), .Dq rsa-sha2-256 , and .Dq rsa-sha2-512 (the default). .It Fl U When used in combination with .Fl s or .Fl Y Cm sign , this option indicates that a CA key resides in a .Xr ssh-agent 1 . See the .Sx CERTIFICATES section for more information. .It Fl u Update a KRL. When specified with .Fl k , keys listed via the command line are added to the existing KRL rather than a new KRL being created. .It Fl V Ar validity_interval Specify a validity interval when signing a certificate. A validity interval may consist of a single time, indicating that the certificate is valid beginning now and expiring at that time, or may consist of two times separated by a colon to indicate an explicit time interval. .Pp The start time may be specified as: .Bl -bullet -compact .It The string .Dq always to indicate the certificate has no specified start time. .It A date or time in the system time zone formatted as YYYYMMDD or YYYYMMDDHHMM[SS]. .It A date or time in the UTC time zone as YYYYMMDDZ or YYYYMMDDHHMM[SS]Z. .It A relative time before the current system time consisting of a minus sign followed by an interval in the format described in the TIME FORMATS section of .Xr sshd_config 5 . .It A raw seconds since epoch (Jan 1 1970 00:00:00 UTC) as a hexadecimal number beginning with .Dq 0x . .El .Pp The end time may be specified similarly to the start time: .Bl -bullet -compact .It The string .Dq forever to indicate the certificate has no specified end time. .It A date or time in the system time zone formatted as YYYYMMDD or YYYYMMDDHHMM[SS]. .It A date or time in the UTC time zone as YYYYMMDDZ or YYYYMMDDHHMM[SS]Z. .It A relative time after the current system time consisting of a plus sign followed by an interval in the format described in the TIME FORMATS section of .Xr sshd_config 5 . .It A raw seconds since epoch (Jan 1 1970 00:00:00 UTC) as a hexadecimal number beginning with .Dq 0x . .El .Pp For example: .Bl -tag -width Ds .It +52w1d Valid from now to 52 weeks and one day from now. .It -4w:+4w Valid from four weeks ago to four weeks from now. .It 20100101123000:20110101123000 Valid from 12:30 PM, January 1st, 2010 to 12:30 PM, January 1st, 2011. .It 20100101123000Z:20110101123000Z Similar, but interpreted in the UTC time zone rather than the system time zone. .It -1d:20110101 Valid from yesterday to midnight, January 1st, 2011. .It 0x1:0x2000000000 Valid from roughly early 1970 to May 2033. .It -1m:forever Valid from one minute ago and never expiring. .El .It Fl v Verbose mode. Causes .Nm to print debugging messages about its progress. This is helpful for debugging moduli generation. Multiple .Fl v options increase the verbosity. The maximum is 3. .It Fl w Ar provider Specifies a path to a library that will be used when creating FIDO authenticator-hosted keys, overriding the default of using the internal USB HID support. .It Fl Y Cm find-principals Find the principal(s) associated with the public key of a signature, provided using the .Fl s flag in an authorized signers file provided using the .Fl f flag. The format of the allowed signers file is documented in the .Sx ALLOWED SIGNERS section below. If one or more matching principals are found, they are returned on standard output. .It Fl Y Cm match-principals Find principal matching the principal name provided using the .Fl I flag in the authorized signers file specified using the .Fl f flag. If one or more matching principals are found, they are returned on standard output. .It Fl Y Cm check-novalidate Checks that a signature generated using .Nm .Fl Y Cm sign has a valid structure. This does not validate if a signature comes from an authorized signer. When testing a signature, .Nm accepts a message on standard input and a signature namespace using .Fl n . A file containing the corresponding signature must also be supplied using the .Fl s flag. Successful testing of the signature is signalled by .Nm returning a zero exit status. .It Fl Y Cm sign Cryptographically sign a file or some data using an SSH key. When signing, .Nm accepts zero or more files to sign on the command-line - if no files are specified then .Nm will sign data presented on standard input. Signatures are written to the path of the input file with .Dq .sig appended, or to standard output if the message to be signed was read from standard input. .Pp The key used for signing is specified using the .Fl f option and may refer to either a private key, or a public key with the private half available via .Xr ssh-agent 1 . An additional signature namespace, used to prevent signature confusion across different domains of use (e.g. file signing vs email signing) must be provided via the .Fl n flag. Namespaces are arbitrary strings, and may include: .Dq file for file signing, .Dq email for email signing. For custom uses, it is recommended to use names following a NAMESPACE@YOUR.DOMAIN pattern to generate unambiguous namespaces. .It Fl Y Cm verify Request to verify a signature generated using .Nm .Fl Y Cm sign as described above. When verifying a signature, .Nm accepts a message on standard input and a signature namespace using .Fl n . A file containing the corresponding signature must also be supplied using the .Fl s flag, along with the identity of the signer using .Fl I and a list of allowed signers via the .Fl f flag. The format of the allowed signers file is documented in the .Sx ALLOWED SIGNERS section below. A file containing revoked keys can be passed using the .Fl r flag. The revocation file may be a KRL or a one-per-line list of public keys. Successful verification by an authorized signer is signalled by .Nm returning a zero exit status. .It Fl y This option will read a private OpenSSH format file and print an OpenSSH public key to stdout. .It Fl Z Ar cipher Specifies the cipher to use for encryption when writing an OpenSSH-format private key file. The list of available ciphers may be obtained using .Qq ssh -Q cipher . The default is .Dq aes256-ctr . .It Fl z Ar serial_number Specifies a serial number to be embedded in the certificate to distinguish this certificate from others from the same CA. If the .Ar serial_number is prefixed with a .Sq + character, then the serial number will be incremented for each certificate signed on a single command-line. The default serial number is zero. .Pp When generating a KRL, the .Fl z flag is used to specify a KRL version number. .El .Sh MODULI GENERATION .Nm may be used to generate groups for the Diffie-Hellman Group Exchange (DH-GEX) protocol. Generating these groups is a two-step process: first, candidate primes are generated using a fast, but memory intensive process. These candidate primes are then tested for suitability (a CPU-intensive process). .Pp Generation of primes is performed using the .Fl M Cm generate option. The desired length of the primes may be specified by the .Fl O Cm bits option. For example: .Pp .Dl # ssh-keygen -M generate -O bits=2048 moduli-2048.candidates .Pp By default, the search for primes begins at a random point in the desired length range. This may be overridden using the .Fl O Cm start option, which specifies a different start point (in hex). .Pp Once a set of candidates have been generated, they must be screened for suitability. This may be performed using the .Fl M Cm screen option. In this mode .Nm will read candidates from standard input (or a file specified using the .Fl f option). For example: .Pp .Dl # ssh-keygen -M screen -f moduli-2048.candidates moduli-2048 .Pp By default, each candidate will be subjected to 100 primality tests. This may be overridden using the .Fl O Cm prime-tests option. The DH generator value will be chosen automatically for the prime under consideration. If a specific generator is desired, it may be requested using the .Fl O Cm generator option. Valid generator values are 2, 3, and 5. .Pp Screened DH groups may be installed in .Pa /etc/moduli . It is important that this file contains moduli of a range of bit lengths. .Pp A number of options are available for moduli generation and screening via the .Fl O flag: .Bl -tag -width Ds .It Ic lines Ns = Ns Ar number Exit after screening the specified number of lines while performing DH candidate screening. .It Ic start-line Ns = Ns Ar line-number Start screening at the specified line number while performing DH candidate screening. .It Ic checkpoint Ns = Ns Ar filename Write the last line processed to the specified file while performing DH candidate screening. This will be used to skip lines in the input file that have already been processed if the job is restarted. .It Ic memory Ns = Ns Ar mbytes Specify the amount of memory to use (in megabytes) when generating candidate moduli for DH-GEX. .It Ic start Ns = Ns Ar hex-value Specify start point (in hex) when generating candidate moduli for DH-GEX. .It Ic generator Ns = Ns Ar value Specify desired generator (in decimal) when testing candidate moduli for DH-GEX. .El .Sh CERTIFICATES .Nm supports signing of keys to produce certificates that may be used for user or host authentication. Certificates consist of a public key, some identity information, zero or more principal (user or host) names and a set of options that are signed by a Certification Authority (CA) key. Clients or servers may then trust only the CA key and verify its signature on a certificate rather than trusting many user/host keys. Note that OpenSSH certificates are a different, and much simpler, format to the X.509 certificates used in .Xr ssl 8 . .Pp .Nm supports two types of certificates: user and host. User certificates authenticate users to servers, whereas host certificates authenticate server hosts to users. To generate a user certificate: .Pp .Dl $ ssh-keygen -s /path/to/ca_key -I key_id /path/to/user_key.pub .Pp The resultant certificate will be placed in .Pa /path/to/user_key-cert.pub . A host certificate requires the .Fl h option: .Pp .Dl $ ssh-keygen -s /path/to/ca_key -I key_id -h /path/to/host_key.pub .Pp The host certificate will be output to .Pa /path/to/host_key-cert.pub . .Pp It is possible to sign using a CA key stored in a PKCS#11 token by providing the token library using .Fl D and identifying the CA key by providing its public half as an argument to .Fl s : .Pp .Dl $ ssh-keygen -s ca_key.pub -D libpkcs11.so -I key_id user_key.pub .Pp Similarly, it is possible for the CA key to be hosted in a .Xr ssh-agent 1 . This is indicated by the .Fl U flag and, again, the CA key must be identified by its public half. .Pp .Dl $ ssh-keygen -Us ca_key.pub -I key_id user_key.pub .Pp In all cases, .Ar key_id is a "key identifier" that is logged by the server when the certificate is used for authentication. .Pp Certificates may be limited to be valid for a set of principal (user/host) names. By default, generated certificates are valid for all users or hosts. To generate a certificate for a specified set of principals: .Pp .Dl $ ssh-keygen -s ca_key -I key_id -n user1,user2 user_key.pub .Dl "$ ssh-keygen -s ca_key -I key_id -h -n host.domain host_key.pub" .Pp Additional limitations on the validity and use of user certificates may be specified through certificate options. A certificate option may disable features of the SSH session, may be valid only when presented from particular source addresses or may force the use of a specific command. .Pp The options that are valid for user certificates are: .Pp .Bl -tag -width Ds -compact .It Ic clear Clear all enabled permissions. This is useful for clearing the default set of permissions so permissions may be added individually. .Pp .It Ic critical : Ns Ar name Ns Op Ns = Ns Ar contents .It Ic extension : Ns Ar name Ns Op Ns = Ns Ar contents Includes an arbitrary certificate critical option or extension. The specified .Ar name should include a domain suffix, e.g.\& .Dq name@example.com . If .Ar contents is specified then it is included as the contents of the extension/option encoded as a string, otherwise the extension/option is created with no contents (usually indicating a flag). Extensions may be ignored by a client or server that does not recognise them, whereas unknown critical options will cause the certificate to be refused. .Pp .It Ic force-command Ns = Ns Ar command Forces the execution of .Ar command instead of any shell or command specified by the user when the certificate is used for authentication. .Pp .It Ic no-agent-forwarding Disable .Xr ssh-agent 1 forwarding (permitted by default). .Pp .It Ic no-port-forwarding Disable port forwarding (permitted by default). .Pp .It Ic no-pty Disable PTY allocation (permitted by default). .Pp .It Ic no-user-rc Disable execution of .Pa ~/.ssh/rc by .Xr sshd 8 (permitted by default). .Pp .It Ic no-x11-forwarding Disable X11 forwarding (permitted by default). .Pp .It Ic permit-agent-forwarding Allows .Xr ssh-agent 1 forwarding. .Pp .It Ic permit-port-forwarding Allows port forwarding. .Pp .It Ic permit-pty Allows PTY allocation. .Pp .It Ic permit-user-rc Allows execution of .Pa ~/.ssh/rc by .Xr sshd 8 . .Pp .It Ic permit-X11-forwarding Allows X11 forwarding. .Pp .It Ic no-touch-required Do not require signatures made using this key include demonstration of user presence (e.g. by having the user touch the authenticator). This option only makes sense for the FIDO authenticator algorithms .Cm ecdsa-sk and .Cm ed25519-sk . .Pp .It Ic source-address Ns = Ns Ar address_list Restrict the source addresses from which the certificate is considered valid. The .Ar address_list is a comma-separated list of one or more address/netmask pairs in CIDR format. .Pp .It Ic verify-required Require signatures made using this key indicate that the user was first verified. This option only makes sense for the FIDO authenticator algorithms .Cm ecdsa-sk and .Cm ed25519-sk . Currently PIN authentication is the only supported verification method, but other methods may be supported in the future. .El .Pp At present, no standard options are valid for host keys. .Pp Finally, certificates may be defined with a validity lifetime. The .Fl V option allows specification of certificate start and end times. A certificate that is presented at a time outside this range will not be considered valid. By default, certificates are valid from the .Ux Epoch to the distant future. .Pp For certificates to be used for user or host authentication, the CA public key must be trusted by .Xr sshd 8 or .Xr ssh 1 . Refer to those manual pages for details. .Sh FIDO AUTHENTICATOR .Nm is able to generate FIDO authenticator-backed keys, after which they may be used much like any other key type supported by OpenSSH, so long as the hardware authenticator is attached when the keys are used. FIDO authenticators generally require the user to explicitly authorise operations by touching or tapping them. FIDO keys consist of two parts: a key handle part stored in the private key file on disk, and a per-device private key that is unique to each FIDO authenticator and that cannot be exported from the authenticator hardware. These are combined by the hardware at authentication time to derive the real key that is used to sign authentication challenges. Supported key types are .Cm ecdsa-sk and .Cm ed25519-sk . .Pp The options that are valid for FIDO keys are: .Bl -tag -width Ds .It Cm application Override the default FIDO application/origin string of .Dq ssh: . This may be useful when generating host or domain-specific resident keys. The specified application string must begin with .Dq ssh: . .It Cm challenge Ns = Ns Ar path Specifies a path to a challenge string that will be passed to the FIDO authenticator during key generation. The challenge string may be used as part of an out-of-band protocol for key enrollment (a random challenge is used by default). .It Cm device Explicitly specify a .Xr fido 4 device to use, rather than letting the authenticator middleware select one. .It Cm no-touch-required Indicate that the generated private key should not require touch events (user presence) when making signatures. Note that .Xr sshd 8 will refuse such signatures by default, unless overridden via an authorized_keys option. .It Cm resident Indicate that the key handle should be stored on the FIDO authenticator itself. This makes it easier to use the authenticator on multiple computers. Resident keys may be supported on FIDO2 authenticators and typically require that a PIN be set on the authenticator prior to generation. Resident keys may be loaded off the authenticator using .Xr ssh-add 1 . Storing both parts of a key on a FIDO authenticator increases the likelihood of an attacker being able to use a stolen authenticator device. .It Cm user A username to be associated with a resident key, overriding the empty default username. Specifying a username may be useful when generating multiple resident keys for the same application name. .It Cm verify-required Indicate that this private key should require user verification for each signature. Not all FIDO authenticators support this option. Currently PIN authentication is the only supported verification method, but other methods may be supported in the future. .It Cm write-attestation Ns = Ns Ar path May be used at key generation time to record the attestation data returned from FIDO authenticators during key generation. This information is potentially sensitive. By default, this information is discarded. .El .Sh KEY REVOCATION LISTS .Nm is able to manage OpenSSH format Key Revocation Lists (KRLs). These binary files specify keys or certificates to be revoked using a compact format, taking as little as one bit per certificate if they are being revoked by serial number. .Pp KRLs may be generated using the .Fl k flag. This option reads one or more files from the command line and generates a new KRL. The files may either contain a KRL specification (see below) or public keys, listed one per line. Plain public keys are revoked by listing their hash or contents in the KRL and certificates revoked by serial number or key ID (if the serial is zero or not available). .Pp Revoking keys using a KRL specification offers explicit control over the types of record used to revoke keys and may be used to directly revoke certificates by serial number or key ID without having the complete original certificate on hand. A KRL specification consists of lines containing one of the following directives followed by a colon and some directive-specific information. .Bl -tag -width Ds .It Cm serial : Ar serial_number Ns Op - Ns Ar serial_number Revokes a certificate with the specified serial number. Serial numbers are 64-bit values, not including zero and may be expressed in decimal, hex or octal. If two serial numbers are specified separated by a hyphen, then the range of serial numbers including and between each is revoked. The CA key must have been specified on the .Nm command line using the .Fl s option. .It Cm id : Ar key_id Revokes a certificate with the specified key ID string. The CA key must have been specified on the .Nm command line using the .Fl s option. .It Cm key : Ar public_key Revokes the specified key. If a certificate is listed, then it is revoked as a plain public key. .It Cm sha1 : Ar public_key Revokes the specified key by including its SHA1 hash in the KRL. .It Cm sha256 : Ar public_key Revokes the specified key by including its SHA256 hash in the KRL. KRLs that revoke keys by SHA256 hash are not supported by OpenSSH versions prior to 7.9. .It Cm hash : Ar fingerprint Revokes a key using a fingerprint hash, as obtained from a .Xr sshd 8 authentication log message or the .Nm .Fl l flag. Only SHA256 fingerprints are supported here and resultant KRLs are not supported by OpenSSH versions prior to 7.9. .El .Pp KRLs may be updated using the .Fl u flag in addition to .Fl k . When this option is specified, keys listed via the command line are merged into the KRL, adding to those already there. .Pp It is also possible, given a KRL, to test whether it revokes a particular key (or keys). The .Fl Q flag will query an existing KRL, testing each key specified on the command line. If any key listed on the command line has been revoked (or an error encountered) then .Nm will exit with a non-zero exit status. A zero exit status will only be returned if no key was revoked. .Sh ALLOWED SIGNERS When verifying signatures, .Nm uses a simple list of identities and keys to determine whether a signature comes from an authorized source. This "allowed signers" file uses a format patterned after the AUTHORIZED_KEYS FILE FORMAT described in .Xr sshd 8 . Each line of the file contains the following space-separated fields: principals, options, keytype, base64-encoded key. Empty lines and lines starting with a .Ql # are ignored as comments. .Pp The principals field is a pattern-list (see PATTERNS in .Xr ssh_config 5 ) consisting of one or more comma-separated USER@DOMAIN identity patterns that are accepted for signing. When verifying, the identity presented via the .Fl I option must match a principals pattern in order for the corresponding key to be considered acceptable for verification. .Pp The options (if present) consist of comma-separated option specifications. No spaces are permitted, except within double quotes. The following option specifications are supported (note that option keywords are case-insensitive): .Bl -tag -width Ds .It Cm cert-authority Indicates that this key is accepted as a certificate authority (CA) and that certificates signed by this CA may be accepted for verification. .It Cm namespaces Ns = Ns "namespace-list" Specifies a pattern-list of namespaces that are accepted for this key. If this option is present, the signature namespace embedded in the signature object and presented on the verification command-line must match the specified list before the key will be considered acceptable. .It Cm valid-after Ns = Ns "timestamp" Indicates that the key is valid for use at or after the specified timestamp, which may be a date or time in the YYYYMMDD[Z] or YYYYMMDDHHMM[SS][Z] formats. Dates and times will be interpreted in the current system time zone unless suffixed with a Z character, which causes them to be interpreted in the UTC time zone. .It Cm valid-before Ns = Ns "timestamp" Indicates that the key is valid for use at or before the specified timestamp. .El .Pp When verifying signatures made by certificates, the expected principal name must match both the principals pattern in the allowed signers file and the principals embedded in the certificate itself. .Pp An example allowed signers file: .Bd -literal -offset 3n # Comments allowed at start of line user1@example.com,user2@example.com ssh-rsa AAAAX1... # A certificate authority, trusted for all principals in a domain. *@example.com cert-authority ssh-ed25519 AAAB4... # A key that is accepted only for file signing. user2@example.com namespaces="file" ssh-ed25519 AAA41... .Ed .Sh ENVIRONMENT .Bl -tag -width Ds .It Ev SSH_SK_PROVIDER Specifies a path to a library that will be used when loading any FIDO authenticator-hosted keys, overriding the default of using the built-in USB HID support. .El .Sh FILES .Bl -tag -width Ds -compact .It Pa ~/.ssh/id_dsa .It Pa ~/.ssh/id_ecdsa .It Pa ~/.ssh/id_ecdsa_sk .It Pa ~/.ssh/id_ed25519 .It Pa ~/.ssh/id_ed25519_sk .It Pa ~/.ssh/id_rsa Contains the DSA, ECDSA, authenticator-hosted ECDSA, Ed25519, authenticator-hosted Ed25519 or RSA authentication identity of the user. This file should not be readable by anyone but the user. It is possible to specify a passphrase when generating the key; that passphrase will be used to encrypt the private part of this file using 128-bit AES. This file is not automatically accessed by .Nm but it is offered as the default file for the private key. .Xr ssh 1 will read this file when a login attempt is made. .Pp .It Pa ~/.ssh/id_dsa.pub .It Pa ~/.ssh/id_ecdsa.pub .It Pa ~/.ssh/id_ecdsa_sk.pub .It Pa ~/.ssh/id_ed25519.pub .It Pa ~/.ssh/id_ed25519_sk.pub .It Pa ~/.ssh/id_rsa.pub Contains the DSA, ECDSA, authenticator-hosted ECDSA, Ed25519, authenticator-hosted Ed25519 or RSA public key for authentication. The contents of this file should be added to .Pa ~/.ssh/authorized_keys on all machines where the user wishes to log in using public key authentication. There is no need to keep the contents of this file secret. .Pp .It Pa /etc/moduli Contains Diffie-Hellman groups used for DH-GEX. The file format is described in .Xr moduli 5 . .El .Sh SEE ALSO .Xr ssh 1 , .Xr ssh-add 1 , .Xr ssh-agent 1 , .Xr moduli 5 , .Xr sshd 8 .Rs .%R RFC 4716 .%T "The Secure Shell (SSH) Public Key File Format" .%D 2006 .Re .Sh AUTHORS OpenSSH is a derivative of the original and free ssh 1.2.12 release by Tatu Ylonen. Aaron Campbell, Bob Beck, Markus Friedl, Niels Provos, Theo de Raadt and Dug Song removed many bugs, re-added newer features and created OpenSSH. Markus Friedl contributed the support for SSH protocol versions 1.5 and 2.0. diff --git a/ssh-keygen.c b/ssh-keygen.c index 9ccea624cd90..5b945a849202 100644 --- a/ssh-keygen.c +++ b/ssh-keygen.c @@ -1,3953 +1,3949 @@ -/* $OpenBSD: ssh-keygen.c,v 1.470 2023/07/17 04:01:10 djm Exp $ */ +/* $OpenBSD: ssh-keygen.c,v 1.471 2023/09/04 10:29:58 job Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1994 Tatu Ylonen , Espoo, Finland * All rights reserved * Identity and host key generation and maintenance. * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". */ #include "includes.h" #include #include #include #ifdef WITH_OPENSSL #include #include #include "openbsd-compat/openssl-compat.h" #endif #ifdef HAVE_STDINT_H # include #endif #include #include #include #ifdef HAVE_PATHS_H # include #endif #include #include #include #include #include #include #include #include #include #include "xmalloc.h" #include "sshkey.h" #include "authfile.h" #include "sshbuf.h" #include "pathnames.h" #include "log.h" #include "misc.h" #include "match.h" #include "hostfile.h" #include "dns.h" #include "ssh.h" #include "ssh2.h" #include "ssherr.h" #include "ssh-pkcs11.h" #include "atomicio.h" #include "krl.h" #include "digest.h" #include "utf8.h" #include "authfd.h" #include "sshsig.h" #include "ssh-sk.h" #include "sk-api.h" /* XXX for SSH_SK_USER_PRESENCE_REQD; remove */ #include "cipher.h" -#ifdef WITH_OPENSSL -# define DEFAULT_KEY_TYPE_NAME "rsa" -#else -# define DEFAULT_KEY_TYPE_NAME "ed25519" -#endif +#define DEFAULT_KEY_TYPE_NAME "ed25519" /* * Default number of bits in the RSA, DSA and ECDSA keys. These value can be * overridden on the command line. * * These values, with the exception of DSA, provide security equivalent to at * least 128 bits of security according to NIST Special Publication 800-57: * Recommendation for Key Management Part 1 rev 4 section 5.6.1. * For DSA it (and FIPS-186-4 section 4.2) specifies that the only size for * which a 160bit hash is acceptable is 1kbit, and since ssh-dss specifies only * SHA1 we limit the DSA key size 1k bits. */ #define DEFAULT_BITS 3072 #define DEFAULT_BITS_DSA 1024 #define DEFAULT_BITS_ECDSA 256 static int quiet = 0; /* Flag indicating that we just want to see the key fingerprint */ static int print_fingerprint = 0; static int print_bubblebabble = 0; /* Hash algorithm to use for fingerprints. */ static int fingerprint_hash = SSH_FP_HASH_DEFAULT; /* The identity file name, given on the command line or entered by the user. */ static char identity_file[PATH_MAX]; static int have_identity = 0; /* This is set to the passphrase if given on the command line. */ static char *identity_passphrase = NULL; /* This is set to the new passphrase if given on the command line. */ static char *identity_new_passphrase = NULL; /* Key type when certifying */ static u_int cert_key_type = SSH2_CERT_TYPE_USER; /* "key ID" of signed key */ static char *cert_key_id = NULL; /* Comma-separated list of principal names for certifying keys */ static char *cert_principals = NULL; /* Validity period for certificates */ static u_int64_t cert_valid_from = 0; static u_int64_t cert_valid_to = ~0ULL; /* Certificate options */ #define CERTOPT_X_FWD (1) #define CERTOPT_AGENT_FWD (1<<1) #define CERTOPT_PORT_FWD (1<<2) #define CERTOPT_PTY (1<<3) #define CERTOPT_USER_RC (1<<4) #define CERTOPT_NO_REQUIRE_USER_PRESENCE (1<<5) #define CERTOPT_REQUIRE_VERIFY (1<<6) #define CERTOPT_DEFAULT (CERTOPT_X_FWD|CERTOPT_AGENT_FWD| \ CERTOPT_PORT_FWD|CERTOPT_PTY|CERTOPT_USER_RC) static u_int32_t certflags_flags = CERTOPT_DEFAULT; static char *certflags_command = NULL; static char *certflags_src_addr = NULL; /* Arbitrary extensions specified by user */ struct cert_ext { char *key; char *val; int crit; }; static struct cert_ext *cert_ext; static size_t ncert_ext; /* Conversion to/from various formats */ enum { FMT_RFC4716, FMT_PKCS8, FMT_PEM } convert_format = FMT_RFC4716; static char *key_type_name = NULL; /* Load key from this PKCS#11 provider */ static char *pkcs11provider = NULL; /* FIDO/U2F provider to use */ static char *sk_provider = NULL; /* Format for writing private keys */ static int private_key_format = SSHKEY_PRIVATE_OPENSSH; /* Cipher for new-format private keys */ static char *openssh_format_cipher = NULL; /* Number of KDF rounds to derive new format keys. */ static int rounds = 0; /* argv0 */ extern char *__progname; static char hostname[NI_MAXHOST]; #ifdef WITH_OPENSSL /* moduli.c */ int gen_candidates(FILE *, u_int32_t, u_int32_t, BIGNUM *); int prime_test(FILE *, FILE *, u_int32_t, u_int32_t, char *, unsigned long, unsigned long); #endif static void type_bits_valid(int type, const char *name, u_int32_t *bitsp) { if (type == KEY_UNSPEC) fatal("unknown key type %s", key_type_name); if (*bitsp == 0) { #ifdef WITH_OPENSSL int nid; switch(type) { case KEY_DSA: *bitsp = DEFAULT_BITS_DSA; break; case KEY_ECDSA: if (name != NULL && (nid = sshkey_ecdsa_nid_from_name(name)) > 0) *bitsp = sshkey_curve_nid_to_bits(nid); if (*bitsp == 0) *bitsp = DEFAULT_BITS_ECDSA; break; case KEY_RSA: *bitsp = DEFAULT_BITS; break; } #endif } #ifdef WITH_OPENSSL switch (type) { case KEY_DSA: if (*bitsp != 1024) fatal("Invalid DSA key length: must be 1024 bits"); break; case KEY_RSA: if (*bitsp < SSH_RSA_MINIMUM_MODULUS_SIZE) fatal("Invalid RSA key length: minimum is %d bits", SSH_RSA_MINIMUM_MODULUS_SIZE); else if (*bitsp > OPENSSL_RSA_MAX_MODULUS_BITS) fatal("Invalid RSA key length: maximum is %d bits", OPENSSL_RSA_MAX_MODULUS_BITS); break; case KEY_ECDSA: if (sshkey_ecdsa_bits_to_nid(*bitsp) == -1) #ifdef OPENSSL_HAS_NISTP521 fatal("Invalid ECDSA key length: valid lengths are " "256, 384 or 521 bits"); #else fatal("Invalid ECDSA key length: valid lengths are " "256 or 384 bits"); #endif } #endif } /* * Checks whether a file exists and, if so, asks the user whether they wish * to overwrite it. * Returns nonzero if the file does not already exist or if the user agrees to * overwrite, or zero otherwise. */ static int confirm_overwrite(const char *filename) { char yesno[3]; struct stat st; if (stat(filename, &st) != 0) return 1; printf("%s already exists.\n", filename); printf("Overwrite (y/n)? "); fflush(stdout); if (fgets(yesno, sizeof(yesno), stdin) == NULL) return 0; if (yesno[0] != 'y' && yesno[0] != 'Y') return 0; return 1; } static void ask_filename(struct passwd *pw, const char *prompt) { char buf[1024]; char *name = NULL; if (key_type_name == NULL) - name = _PATH_SSH_CLIENT_ID_RSA; + name = _PATH_SSH_CLIENT_ID_ED25519; else { switch (sshkey_type_from_name(key_type_name)) { case KEY_DSA_CERT: case KEY_DSA: name = _PATH_SSH_CLIENT_ID_DSA; break; #ifdef OPENSSL_HAS_ECC case KEY_ECDSA_CERT: case KEY_ECDSA: name = _PATH_SSH_CLIENT_ID_ECDSA; break; case KEY_ECDSA_SK_CERT: case KEY_ECDSA_SK: name = _PATH_SSH_CLIENT_ID_ECDSA_SK; break; #endif case KEY_RSA_CERT: case KEY_RSA: name = _PATH_SSH_CLIENT_ID_RSA; break; case KEY_ED25519: case KEY_ED25519_CERT: name = _PATH_SSH_CLIENT_ID_ED25519; break; case KEY_ED25519_SK: case KEY_ED25519_SK_CERT: name = _PATH_SSH_CLIENT_ID_ED25519_SK; break; case KEY_XMSS: case KEY_XMSS_CERT: name = _PATH_SSH_CLIENT_ID_XMSS; break; default: fatal("bad key type"); } } snprintf(identity_file, sizeof(identity_file), "%s/%s", pw->pw_dir, name); printf("%s (%s): ", prompt, identity_file); fflush(stdout); if (fgets(buf, sizeof(buf), stdin) == NULL) exit(1); buf[strcspn(buf, "\n")] = '\0'; if (strcmp(buf, "") != 0) strlcpy(identity_file, buf, sizeof(identity_file)); have_identity = 1; } static struct sshkey * load_identity(const char *filename, char **commentp) { char *pass; struct sshkey *prv; int r; if (commentp != NULL) *commentp = NULL; if ((r = sshkey_load_private(filename, "", &prv, commentp)) == 0) return prv; if (r != SSH_ERR_KEY_WRONG_PASSPHRASE) fatal_r(r, "Load key \"%s\"", filename); if (identity_passphrase) pass = xstrdup(identity_passphrase); else pass = read_passphrase("Enter passphrase: ", RP_ALLOW_STDIN); r = sshkey_load_private(filename, pass, &prv, commentp); freezero(pass, strlen(pass)); if (r != 0) fatal_r(r, "Load key \"%s\"", filename); return prv; } #define SSH_COM_PUBLIC_BEGIN "---- BEGIN SSH2 PUBLIC KEY ----" #define SSH_COM_PUBLIC_END "---- END SSH2 PUBLIC KEY ----" #define SSH_COM_PRIVATE_BEGIN "---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----" #define SSH_COM_PRIVATE_KEY_MAGIC 0x3f6ff9eb #ifdef WITH_OPENSSL static void do_convert_to_ssh2(struct passwd *pw, struct sshkey *k) { struct sshbuf *b; char comment[61], *b64; int r; if ((b = sshbuf_new()) == NULL) fatal_f("sshbuf_new failed"); if ((r = sshkey_putb(k, b)) != 0) fatal_fr(r, "put key"); if ((b64 = sshbuf_dtob64_string(b, 1)) == NULL) fatal_f("sshbuf_dtob64_string failed"); /* Comment + surrounds must fit into 72 chars (RFC 4716 sec 3.3) */ snprintf(comment, sizeof(comment), "%u-bit %s, converted by %s@%s from OpenSSH", sshkey_size(k), sshkey_type(k), pw->pw_name, hostname); sshkey_free(k); sshbuf_free(b); fprintf(stdout, "%s\n", SSH_COM_PUBLIC_BEGIN); fprintf(stdout, "Comment: \"%s\"\n%s", comment, b64); fprintf(stdout, "%s\n", SSH_COM_PUBLIC_END); free(b64); exit(0); } static void do_convert_to_pkcs8(struct sshkey *k) { switch (sshkey_type_plain(k->type)) { case KEY_RSA: if (!PEM_write_RSA_PUBKEY(stdout, k->rsa)) fatal("PEM_write_RSA_PUBKEY failed"); break; case KEY_DSA: if (!PEM_write_DSA_PUBKEY(stdout, k->dsa)) fatal("PEM_write_DSA_PUBKEY failed"); break; #ifdef OPENSSL_HAS_ECC case KEY_ECDSA: if (!PEM_write_EC_PUBKEY(stdout, k->ecdsa)) fatal("PEM_write_EC_PUBKEY failed"); break; #endif default: fatal_f("unsupported key type %s", sshkey_type(k)); } exit(0); } static void do_convert_to_pem(struct sshkey *k) { switch (sshkey_type_plain(k->type)) { case KEY_RSA: if (!PEM_write_RSAPublicKey(stdout, k->rsa)) fatal("PEM_write_RSAPublicKey failed"); break; case KEY_DSA: if (!PEM_write_DSA_PUBKEY(stdout, k->dsa)) fatal("PEM_write_DSA_PUBKEY failed"); break; #ifdef OPENSSL_HAS_ECC case KEY_ECDSA: if (!PEM_write_EC_PUBKEY(stdout, k->ecdsa)) fatal("PEM_write_EC_PUBKEY failed"); break; #endif default: fatal_f("unsupported key type %s", sshkey_type(k)); } exit(0); } static void do_convert_to(struct passwd *pw) { struct sshkey *k; struct stat st; int r; if (!have_identity) ask_filename(pw, "Enter file in which the key is"); if (stat(identity_file, &st) == -1) fatal("%s: %s: %s", __progname, identity_file, strerror(errno)); if ((r = sshkey_load_public(identity_file, &k, NULL)) != 0) k = load_identity(identity_file, NULL); switch (convert_format) { case FMT_RFC4716: do_convert_to_ssh2(pw, k); break; case FMT_PKCS8: do_convert_to_pkcs8(k); break; case FMT_PEM: do_convert_to_pem(k); break; default: fatal_f("unknown key format %d", convert_format); } exit(0); } /* * This is almost exactly the bignum1 encoding, but with 32 bit for length * instead of 16. */ static void buffer_get_bignum_bits(struct sshbuf *b, BIGNUM *value) { u_int bytes, bignum_bits; int r; if ((r = sshbuf_get_u32(b, &bignum_bits)) != 0) fatal_fr(r, "parse"); bytes = (bignum_bits + 7) / 8; if (sshbuf_len(b) < bytes) fatal_f("input buffer too small: need %d have %zu", bytes, sshbuf_len(b)); if (BN_bin2bn(sshbuf_ptr(b), bytes, value) == NULL) fatal_f("BN_bin2bn failed"); if ((r = sshbuf_consume(b, bytes)) != 0) fatal_fr(r, "consume"); } static struct sshkey * do_convert_private_ssh2(struct sshbuf *b) { struct sshkey *key = NULL; char *type, *cipher; const char *alg = NULL; u_char e1, e2, e3, *sig = NULL, data[] = "abcde12345"; int r, rlen, ktype; u_int magic, i1, i2, i3, i4; size_t slen; u_long e; BIGNUM *dsa_p = NULL, *dsa_q = NULL, *dsa_g = NULL; BIGNUM *dsa_pub_key = NULL, *dsa_priv_key = NULL; BIGNUM *rsa_n = NULL, *rsa_e = NULL, *rsa_d = NULL; BIGNUM *rsa_p = NULL, *rsa_q = NULL, *rsa_iqmp = NULL; if ((r = sshbuf_get_u32(b, &magic)) != 0) fatal_fr(r, "parse magic"); if (magic != SSH_COM_PRIVATE_KEY_MAGIC) { error("bad magic 0x%x != 0x%x", magic, SSH_COM_PRIVATE_KEY_MAGIC); return NULL; } if ((r = sshbuf_get_u32(b, &i1)) != 0 || (r = sshbuf_get_cstring(b, &type, NULL)) != 0 || (r = sshbuf_get_cstring(b, &cipher, NULL)) != 0 || (r = sshbuf_get_u32(b, &i2)) != 0 || (r = sshbuf_get_u32(b, &i3)) != 0 || (r = sshbuf_get_u32(b, &i4)) != 0) fatal_fr(r, "parse"); debug("ignore (%d %d %d %d)", i1, i2, i3, i4); if (strcmp(cipher, "none") != 0) { error("unsupported cipher %s", cipher); free(cipher); free(type); return NULL; } free(cipher); if (strstr(type, "dsa")) { ktype = KEY_DSA; } else if (strstr(type, "rsa")) { ktype = KEY_RSA; } else { free(type); return NULL; } if ((key = sshkey_new(ktype)) == NULL) fatal("sshkey_new failed"); free(type); switch (key->type) { case KEY_DSA: if ((dsa_p = BN_new()) == NULL || (dsa_q = BN_new()) == NULL || (dsa_g = BN_new()) == NULL || (dsa_pub_key = BN_new()) == NULL || (dsa_priv_key = BN_new()) == NULL) fatal_f("BN_new"); buffer_get_bignum_bits(b, dsa_p); buffer_get_bignum_bits(b, dsa_g); buffer_get_bignum_bits(b, dsa_q); buffer_get_bignum_bits(b, dsa_pub_key); buffer_get_bignum_bits(b, dsa_priv_key); if (!DSA_set0_pqg(key->dsa, dsa_p, dsa_q, dsa_g)) fatal_f("DSA_set0_pqg failed"); dsa_p = dsa_q = dsa_g = NULL; /* transferred */ if (!DSA_set0_key(key->dsa, dsa_pub_key, dsa_priv_key)) fatal_f("DSA_set0_key failed"); dsa_pub_key = dsa_priv_key = NULL; /* transferred */ break; case KEY_RSA: if ((r = sshbuf_get_u8(b, &e1)) != 0 || (e1 < 30 && (r = sshbuf_get_u8(b, &e2)) != 0) || (e1 < 30 && (r = sshbuf_get_u8(b, &e3)) != 0)) fatal_fr(r, "parse RSA"); e = e1; debug("e %lx", e); if (e < 30) { e <<= 8; e += e2; debug("e %lx", e); e <<= 8; e += e3; debug("e %lx", e); } if ((rsa_e = BN_new()) == NULL) fatal_f("BN_new"); if (!BN_set_word(rsa_e, e)) { BN_clear_free(rsa_e); sshkey_free(key); return NULL; } if ((rsa_n = BN_new()) == NULL || (rsa_d = BN_new()) == NULL || (rsa_p = BN_new()) == NULL || (rsa_q = BN_new()) == NULL || (rsa_iqmp = BN_new()) == NULL) fatal_f("BN_new"); buffer_get_bignum_bits(b, rsa_d); buffer_get_bignum_bits(b, rsa_n); buffer_get_bignum_bits(b, rsa_iqmp); buffer_get_bignum_bits(b, rsa_q); buffer_get_bignum_bits(b, rsa_p); if (!RSA_set0_key(key->rsa, rsa_n, rsa_e, rsa_d)) fatal_f("RSA_set0_key failed"); rsa_n = rsa_e = rsa_d = NULL; /* transferred */ if (!RSA_set0_factors(key->rsa, rsa_p, rsa_q)) fatal_f("RSA_set0_factors failed"); rsa_p = rsa_q = NULL; /* transferred */ if ((r = ssh_rsa_complete_crt_parameters(key, rsa_iqmp)) != 0) fatal_fr(r, "generate RSA parameters"); BN_clear_free(rsa_iqmp); alg = "rsa-sha2-256"; break; } rlen = sshbuf_len(b); if (rlen != 0) error_f("remaining bytes in key blob %d", rlen); /* try the key */ if ((r = sshkey_sign(key, &sig, &slen, data, sizeof(data), alg, NULL, NULL, 0)) != 0) error_fr(r, "signing with converted key failed"); else if ((r = sshkey_verify(key, sig, slen, data, sizeof(data), alg, 0, NULL)) != 0) error_fr(r, "verification with converted key failed"); if (r != 0) { sshkey_free(key); free(sig); return NULL; } free(sig); return key; } static int get_line(FILE *fp, char *line, size_t len) { int c; size_t pos = 0; line[0] = '\0'; while ((c = fgetc(fp)) != EOF) { if (pos >= len - 1) fatal("input line too long."); switch (c) { case '\r': c = fgetc(fp); if (c != EOF && c != '\n' && ungetc(c, fp) == EOF) fatal("unget: %s", strerror(errno)); return pos; case '\n': return pos; } line[pos++] = c; line[pos] = '\0'; } /* We reached EOF */ return -1; } static void do_convert_from_ssh2(struct passwd *pw, struct sshkey **k, int *private) { int r, blen, escaped = 0; u_int len; char line[1024]; struct sshbuf *buf; char encoded[8096]; FILE *fp; if ((buf = sshbuf_new()) == NULL) fatal("sshbuf_new failed"); if ((fp = fopen(identity_file, "r")) == NULL) fatal("%s: %s: %s", __progname, identity_file, strerror(errno)); encoded[0] = '\0'; while ((blen = get_line(fp, line, sizeof(line))) != -1) { if (blen > 0 && line[blen - 1] == '\\') escaped++; if (strncmp(line, "----", 4) == 0 || strstr(line, ": ") != NULL) { if (strstr(line, SSH_COM_PRIVATE_BEGIN) != NULL) *private = 1; if (strstr(line, " END ") != NULL) { break; } /* fprintf(stderr, "ignore: %s", line); */ continue; } if (escaped) { escaped--; /* fprintf(stderr, "escaped: %s", line); */ continue; } strlcat(encoded, line, sizeof(encoded)); } len = strlen(encoded); if (((len % 4) == 3) && (encoded[len-1] == '=') && (encoded[len-2] == '=') && (encoded[len-3] == '=')) encoded[len-3] = '\0'; if ((r = sshbuf_b64tod(buf, encoded)) != 0) fatal_fr(r, "base64 decode"); if (*private) { if ((*k = do_convert_private_ssh2(buf)) == NULL) fatal_f("private key conversion failed"); } else if ((r = sshkey_fromb(buf, k)) != 0) fatal_fr(r, "parse key"); sshbuf_free(buf); fclose(fp); } static void do_convert_from_pkcs8(struct sshkey **k, int *private) { EVP_PKEY *pubkey; FILE *fp; if ((fp = fopen(identity_file, "r")) == NULL) fatal("%s: %s: %s", __progname, identity_file, strerror(errno)); if ((pubkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL)) == NULL) { fatal_f("%s is not a recognised public key format", identity_file); } fclose(fp); switch (EVP_PKEY_base_id(pubkey)) { case EVP_PKEY_RSA: if ((*k = sshkey_new(KEY_UNSPEC)) == NULL) fatal("sshkey_new failed"); (*k)->type = KEY_RSA; (*k)->rsa = EVP_PKEY_get1_RSA(pubkey); break; case EVP_PKEY_DSA: if ((*k = sshkey_new(KEY_UNSPEC)) == NULL) fatal("sshkey_new failed"); (*k)->type = KEY_DSA; (*k)->dsa = EVP_PKEY_get1_DSA(pubkey); break; #ifdef OPENSSL_HAS_ECC case EVP_PKEY_EC: if ((*k = sshkey_new(KEY_UNSPEC)) == NULL) fatal("sshkey_new failed"); (*k)->type = KEY_ECDSA; (*k)->ecdsa = EVP_PKEY_get1_EC_KEY(pubkey); (*k)->ecdsa_nid = sshkey_ecdsa_key_to_nid((*k)->ecdsa); break; #endif default: fatal_f("unsupported pubkey type %d", EVP_PKEY_base_id(pubkey)); } EVP_PKEY_free(pubkey); return; } static void do_convert_from_pem(struct sshkey **k, int *private) { FILE *fp; RSA *rsa; if ((fp = fopen(identity_file, "r")) == NULL) fatal("%s: %s: %s", __progname, identity_file, strerror(errno)); if ((rsa = PEM_read_RSAPublicKey(fp, NULL, NULL, NULL)) != NULL) { if ((*k = sshkey_new(KEY_UNSPEC)) == NULL) fatal("sshkey_new failed"); (*k)->type = KEY_RSA; (*k)->rsa = rsa; fclose(fp); return; } fatal_f("unrecognised raw private key format"); } static void do_convert_from(struct passwd *pw) { struct sshkey *k = NULL; int r, private = 0, ok = 0; struct stat st; if (!have_identity) ask_filename(pw, "Enter file in which the key is"); if (stat(identity_file, &st) == -1) fatal("%s: %s: %s", __progname, identity_file, strerror(errno)); switch (convert_format) { case FMT_RFC4716: do_convert_from_ssh2(pw, &k, &private); break; case FMT_PKCS8: do_convert_from_pkcs8(&k, &private); break; case FMT_PEM: do_convert_from_pem(&k, &private); break; default: fatal_f("unknown key format %d", convert_format); } if (!private) { if ((r = sshkey_write(k, stdout)) == 0) ok = 1; if (ok) fprintf(stdout, "\n"); } else { switch (k->type) { case KEY_DSA: ok = PEM_write_DSAPrivateKey(stdout, k->dsa, NULL, NULL, 0, NULL, NULL); break; #ifdef OPENSSL_HAS_ECC case KEY_ECDSA: ok = PEM_write_ECPrivateKey(stdout, k->ecdsa, NULL, NULL, 0, NULL, NULL); break; #endif case KEY_RSA: ok = PEM_write_RSAPrivateKey(stdout, k->rsa, NULL, NULL, 0, NULL, NULL); break; default: fatal_f("unsupported key type %s", sshkey_type(k)); } } if (!ok) fatal("key write failed"); sshkey_free(k); exit(0); } #endif static void do_print_public(struct passwd *pw) { struct sshkey *prv; struct stat st; int r; char *comment = NULL; if (!have_identity) ask_filename(pw, "Enter file in which the key is"); if (stat(identity_file, &st) == -1) fatal("%s: %s", identity_file, strerror(errno)); prv = load_identity(identity_file, &comment); if ((r = sshkey_write(prv, stdout)) != 0) fatal_fr(r, "write key"); if (comment != NULL && *comment != '\0') fprintf(stdout, " %s", comment); fprintf(stdout, "\n"); if (sshkey_is_sk(prv)) { debug("sk_application: \"%s\", sk_flags 0x%02x", prv->sk_application, prv->sk_flags); } sshkey_free(prv); free(comment); exit(0); } static void do_download(struct passwd *pw) { #ifdef ENABLE_PKCS11 struct sshkey **keys = NULL; int i, nkeys; enum sshkey_fp_rep rep; int fptype; char *fp, *ra, **comments = NULL; fptype = print_bubblebabble ? SSH_DIGEST_SHA1 : fingerprint_hash; rep = print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_DEFAULT; pkcs11_init(1); nkeys = pkcs11_add_provider(pkcs11provider, NULL, &keys, &comments); if (nkeys <= 0) fatal("cannot read public key from pkcs11"); for (i = 0; i < nkeys; i++) { if (print_fingerprint) { fp = sshkey_fingerprint(keys[i], fptype, rep); ra = sshkey_fingerprint(keys[i], fingerprint_hash, SSH_FP_RANDOMART); if (fp == NULL || ra == NULL) fatal_f("sshkey_fingerprint fail"); printf("%u %s %s (PKCS11 key)\n", sshkey_size(keys[i]), fp, sshkey_type(keys[i])); if (log_level_get() >= SYSLOG_LEVEL_VERBOSE) printf("%s\n", ra); free(ra); free(fp); } else { (void) sshkey_write(keys[i], stdout); /* XXX check */ fprintf(stdout, "%s%s\n", *(comments[i]) == '\0' ? "" : " ", comments[i]); } free(comments[i]); sshkey_free(keys[i]); } free(comments); free(keys); pkcs11_terminate(); exit(0); #else fatal("no pkcs11 support"); #endif /* ENABLE_PKCS11 */ } static struct sshkey * try_read_key(char **cpp) { struct sshkey *ret; int r; if ((ret = sshkey_new(KEY_UNSPEC)) == NULL) fatal("sshkey_new failed"); if ((r = sshkey_read(ret, cpp)) == 0) return ret; /* Not a key */ sshkey_free(ret); return NULL; } static void fingerprint_one_key(const struct sshkey *public, const char *comment) { char *fp = NULL, *ra = NULL; enum sshkey_fp_rep rep; int fptype; fptype = print_bubblebabble ? SSH_DIGEST_SHA1 : fingerprint_hash; rep = print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_DEFAULT; fp = sshkey_fingerprint(public, fptype, rep); ra = sshkey_fingerprint(public, fingerprint_hash, SSH_FP_RANDOMART); if (fp == NULL || ra == NULL) fatal_f("sshkey_fingerprint failed"); mprintf("%u %s %s (%s)\n", sshkey_size(public), fp, comment ? comment : "no comment", sshkey_type(public)); if (log_level_get() >= SYSLOG_LEVEL_VERBOSE) printf("%s\n", ra); free(ra); free(fp); } static void fingerprint_private(const char *path) { struct stat st; char *comment = NULL; struct sshkey *privkey = NULL, *pubkey = NULL; int r; if (stat(identity_file, &st) == -1) fatal("%s: %s", path, strerror(errno)); if ((r = sshkey_load_public(path, &pubkey, &comment)) != 0) debug_r(r, "load public \"%s\"", path); if (pubkey == NULL || comment == NULL || *comment == '\0') { free(comment); if ((r = sshkey_load_private(path, NULL, &privkey, &comment)) != 0) debug_r(r, "load private \"%s\"", path); } if (pubkey == NULL && privkey == NULL) fatal("%s is not a key file.", path); fingerprint_one_key(pubkey == NULL ? privkey : pubkey, comment); sshkey_free(pubkey); sshkey_free(privkey); free(comment); } static void do_fingerprint(struct passwd *pw) { FILE *f; struct sshkey *public = NULL; char *comment = NULL, *cp, *ep, *line = NULL; size_t linesize = 0; int i, invalid = 1; const char *path; u_long lnum = 0; if (!have_identity) ask_filename(pw, "Enter file in which the key is"); path = identity_file; if (strcmp(identity_file, "-") == 0) { f = stdin; path = "(stdin)"; } else if ((f = fopen(path, "r")) == NULL) fatal("%s: %s: %s", __progname, path, strerror(errno)); while (getline(&line, &linesize, f) != -1) { lnum++; cp = line; cp[strcspn(cp, "\n")] = '\0'; /* Trim leading space and comments */ cp = line + strspn(line, " \t"); if (*cp == '#' || *cp == '\0') continue; /* * Input may be plain keys, private keys, authorized_keys * or known_hosts. */ /* * Try private keys first. Assume a key is private if * "SSH PRIVATE KEY" appears on the first line and we're * not reading from stdin (XXX support private keys on stdin). */ if (lnum == 1 && strcmp(identity_file, "-") != 0 && strstr(cp, "PRIVATE KEY") != NULL) { free(line); fclose(f); fingerprint_private(path); exit(0); } /* * If it's not a private key, then this must be prepared to * accept a public key prefixed with a hostname or options. * Try a bare key first, otherwise skip the leading stuff. */ comment = NULL; if ((public = try_read_key(&cp)) == NULL) { i = strtol(cp, &ep, 10); if (i == 0 || ep == NULL || (*ep != ' ' && *ep != '\t')) { int quoted = 0; comment = cp; for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) { if (*cp == '\\' && cp[1] == '"') cp++; /* Skip both */ else if (*cp == '"') quoted = !quoted; } if (!*cp) continue; *cp++ = '\0'; } } /* Retry after parsing leading hostname/key options */ if (public == NULL && (public = try_read_key(&cp)) == NULL) { debug("%s:%lu: not a public key", path, lnum); continue; } /* Find trailing comment, if any */ for (; *cp == ' ' || *cp == '\t'; cp++) ; if (*cp != '\0' && *cp != '#') comment = cp; fingerprint_one_key(public, comment); sshkey_free(public); invalid = 0; /* One good key in the file is sufficient */ } fclose(f); free(line); if (invalid) fatal("%s is not a public key file.", path); exit(0); } static void do_gen_all_hostkeys(struct passwd *pw) { struct { char *key_type; char *key_type_display; char *path; } key_types[] = { #ifdef WITH_OPENSSL { "rsa", "RSA" ,_PATH_HOST_RSA_KEY_FILE }, #ifdef OPENSSL_HAS_ECC { "ecdsa", "ECDSA",_PATH_HOST_ECDSA_KEY_FILE }, #endif /* OPENSSL_HAS_ECC */ #endif /* WITH_OPENSSL */ { "ed25519", "ED25519",_PATH_HOST_ED25519_KEY_FILE }, #ifdef WITH_XMSS { "xmss", "XMSS",_PATH_HOST_XMSS_KEY_FILE }, #endif /* WITH_XMSS */ { NULL, NULL, NULL } }; u_int32_t bits = 0; int first = 0; struct stat st; struct sshkey *private, *public; char comment[1024], *prv_tmp, *pub_tmp, *prv_file, *pub_file; int i, type, fd, r; for (i = 0; key_types[i].key_type; i++) { public = private = NULL; prv_tmp = pub_tmp = prv_file = pub_file = NULL; xasprintf(&prv_file, "%s%s", identity_file, key_types[i].path); /* Check whether private key exists and is not zero-length */ if (stat(prv_file, &st) == 0) { if (st.st_size != 0) goto next; } else if (errno != ENOENT) { error("Could not stat %s: %s", key_types[i].path, strerror(errno)); goto failnext; } /* * Private key doesn't exist or is invalid; proceed with * key generation. */ xasprintf(&prv_tmp, "%s%s.XXXXXXXXXX", identity_file, key_types[i].path); xasprintf(&pub_tmp, "%s%s.pub.XXXXXXXXXX", identity_file, key_types[i].path); xasprintf(&pub_file, "%s%s.pub", identity_file, key_types[i].path); if (first == 0) { first = 1; printf("%s: generating new host keys: ", __progname); } printf("%s ", key_types[i].key_type_display); fflush(stdout); type = sshkey_type_from_name(key_types[i].key_type); if ((fd = mkstemp(prv_tmp)) == -1) { error("Could not save your private key in %s: %s", prv_tmp, strerror(errno)); goto failnext; } (void)close(fd); /* just using mkstemp() to reserve a name */ bits = 0; type_bits_valid(type, NULL, &bits); if ((r = sshkey_generate(type, bits, &private)) != 0) { error_r(r, "sshkey_generate failed"); goto failnext; } if ((r = sshkey_from_private(private, &public)) != 0) fatal_fr(r, "sshkey_from_private"); snprintf(comment, sizeof comment, "%s@%s", pw->pw_name, hostname); if ((r = sshkey_save_private(private, prv_tmp, "", comment, private_key_format, openssh_format_cipher, rounds)) != 0) { error_r(r, "Saving key \"%s\" failed", prv_tmp); goto failnext; } if ((fd = mkstemp(pub_tmp)) == -1) { error("Could not save your public key in %s: %s", pub_tmp, strerror(errno)); goto failnext; } (void)fchmod(fd, 0644); (void)close(fd); if ((r = sshkey_save_public(public, pub_tmp, comment)) != 0) { error_r(r, "Unable to save public key to %s", identity_file); goto failnext; } /* Rename temporary files to their permanent locations. */ if (rename(pub_tmp, pub_file) != 0) { error("Unable to move %s into position: %s", pub_file, strerror(errno)); goto failnext; } if (rename(prv_tmp, prv_file) != 0) { error("Unable to move %s into position: %s", key_types[i].path, strerror(errno)); failnext: first = 0; goto next; } next: sshkey_free(private); sshkey_free(public); free(prv_tmp); free(pub_tmp); free(prv_file); free(pub_file); } if (first != 0) printf("\n"); } struct known_hosts_ctx { const char *host; /* Hostname searched for in find/delete case */ FILE *out; /* Output file, stdout for find_hosts case */ int has_unhashed; /* When hashing, original had unhashed hosts */ int found_key; /* For find/delete, host was found */ int invalid; /* File contained invalid items; don't delete */ int hash_hosts; /* Hash hostnames as we go */ int find_host; /* Search for specific hostname */ int delete_host; /* Delete host from known_hosts */ }; static int known_hosts_hash(struct hostkey_foreach_line *l, void *_ctx) { struct known_hosts_ctx *ctx = (struct known_hosts_ctx *)_ctx; char *hashed, *cp, *hosts, *ohosts; int has_wild = l->hosts && strcspn(l->hosts, "*?!") != strlen(l->hosts); int was_hashed = l->hosts && l->hosts[0] == HASH_DELIM; switch (l->status) { case HKF_STATUS_OK: case HKF_STATUS_MATCHED: /* * Don't hash hosts already hashed, with wildcard * characters or a CA/revocation marker. */ if (was_hashed || has_wild || l->marker != MRK_NONE) { fprintf(ctx->out, "%s\n", l->line); if (has_wild && !ctx->find_host) { logit("%s:%lu: ignoring host name " "with wildcard: %.64s", l->path, l->linenum, l->hosts); } return 0; } /* * Split any comma-separated hostnames from the host list, * hash and store separately. */ ohosts = hosts = xstrdup(l->hosts); while ((cp = strsep(&hosts, ",")) != NULL && *cp != '\0') { lowercase(cp); if ((hashed = host_hash(cp, NULL, 0)) == NULL) fatal("hash_host failed"); fprintf(ctx->out, "%s %s\n", hashed, l->rawkey); free(hashed); ctx->has_unhashed = 1; } free(ohosts); return 0; case HKF_STATUS_INVALID: /* Retain invalid lines, but mark file as invalid. */ ctx->invalid = 1; logit("%s:%lu: invalid line", l->path, l->linenum); /* FALLTHROUGH */ default: fprintf(ctx->out, "%s\n", l->line); return 0; } /* NOTREACHED */ return -1; } static int known_hosts_find_delete(struct hostkey_foreach_line *l, void *_ctx) { struct known_hosts_ctx *ctx = (struct known_hosts_ctx *)_ctx; enum sshkey_fp_rep rep; int fptype; char *fp = NULL, *ra = NULL; fptype = print_bubblebabble ? SSH_DIGEST_SHA1 : fingerprint_hash; rep = print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_DEFAULT; if (l->status == HKF_STATUS_MATCHED) { if (ctx->delete_host) { if (l->marker != MRK_NONE) { /* Don't remove CA and revocation lines */ fprintf(ctx->out, "%s\n", l->line); } else { /* * Hostname matches and has no CA/revoke * marker, delete it by *not* writing the * line to ctx->out. */ ctx->found_key = 1; if (!quiet) printf("# Host %s found: line %lu\n", ctx->host, l->linenum); } return 0; } else if (ctx->find_host) { ctx->found_key = 1; if (!quiet) { printf("# Host %s found: line %lu %s\n", ctx->host, l->linenum, l->marker == MRK_CA ? "CA" : (l->marker == MRK_REVOKE ? "REVOKED" : "")); } if (ctx->hash_hosts) known_hosts_hash(l, ctx); else if (print_fingerprint) { fp = sshkey_fingerprint(l->key, fptype, rep); ra = sshkey_fingerprint(l->key, fingerprint_hash, SSH_FP_RANDOMART); if (fp == NULL || ra == NULL) fatal_f("sshkey_fingerprint failed"); mprintf("%s %s %s%s%s\n", ctx->host, sshkey_type(l->key), fp, l->comment[0] ? " " : "", l->comment); if (log_level_get() >= SYSLOG_LEVEL_VERBOSE) printf("%s\n", ra); free(ra); free(fp); } else fprintf(ctx->out, "%s\n", l->line); return 0; } } else if (ctx->delete_host) { /* Retain non-matching hosts when deleting */ if (l->status == HKF_STATUS_INVALID) { ctx->invalid = 1; logit("%s:%lu: invalid line", l->path, l->linenum); } fprintf(ctx->out, "%s\n", l->line); } return 0; } static void do_known_hosts(struct passwd *pw, const char *name, int find_host, int delete_host, int hash_hosts) { char *cp, tmp[PATH_MAX], old[PATH_MAX]; int r, fd, oerrno, inplace = 0; struct known_hosts_ctx ctx; u_int foreach_options; struct stat sb; if (!have_identity) { cp = tilde_expand_filename(_PATH_SSH_USER_HOSTFILE, pw->pw_uid); if (strlcpy(identity_file, cp, sizeof(identity_file)) >= sizeof(identity_file)) fatal("Specified known hosts path too long"); free(cp); have_identity = 1; } if (stat(identity_file, &sb) != 0) fatal("Cannot stat %s: %s", identity_file, strerror(errno)); memset(&ctx, 0, sizeof(ctx)); ctx.out = stdout; ctx.host = name; ctx.hash_hosts = hash_hosts; ctx.find_host = find_host; ctx.delete_host = delete_host; /* * Find hosts goes to stdout, hash and deletions happen in-place * A corner case is ssh-keygen -HF foo, which should go to stdout */ if (!find_host && (hash_hosts || delete_host)) { if (strlcpy(tmp, identity_file, sizeof(tmp)) >= sizeof(tmp) || strlcat(tmp, ".XXXXXXXXXX", sizeof(tmp)) >= sizeof(tmp) || strlcpy(old, identity_file, sizeof(old)) >= sizeof(old) || strlcat(old, ".old", sizeof(old)) >= sizeof(old)) fatal("known_hosts path too long"); umask(077); if ((fd = mkstemp(tmp)) == -1) fatal("mkstemp: %s", strerror(errno)); if ((ctx.out = fdopen(fd, "w")) == NULL) { oerrno = errno; unlink(tmp); fatal("fdopen: %s", strerror(oerrno)); } (void)fchmod(fd, sb.st_mode & 0644); inplace = 1; } /* XXX support identity_file == "-" for stdin */ foreach_options = find_host ? HKF_WANT_MATCH : 0; foreach_options |= print_fingerprint ? HKF_WANT_PARSE_KEY : 0; if ((r = hostkeys_foreach(identity_file, (find_host || !hash_hosts) ? known_hosts_find_delete : known_hosts_hash, &ctx, name, NULL, foreach_options, 0)) != 0) { if (inplace) unlink(tmp); fatal_fr(r, "hostkeys_foreach"); } if (inplace) fclose(ctx.out); if (ctx.invalid) { error("%s is not a valid known_hosts file.", identity_file); if (inplace) { error("Not replacing existing known_hosts " "file because of errors"); unlink(tmp); } exit(1); } else if (delete_host && !ctx.found_key) { logit("Host %s not found in %s", name, identity_file); if (inplace) unlink(tmp); } else if (inplace) { /* Backup existing file */ if (unlink(old) == -1 && errno != ENOENT) fatal("unlink %.100s: %s", old, strerror(errno)); if (link(identity_file, old) == -1) fatal("link %.100s to %.100s: %s", identity_file, old, strerror(errno)); /* Move new one into place */ if (rename(tmp, identity_file) == -1) { error("rename\"%s\" to \"%s\": %s", tmp, identity_file, strerror(errno)); unlink(tmp); unlink(old); exit(1); } printf("%s updated.\n", identity_file); printf("Original contents retained as %s\n", old); if (ctx.has_unhashed) { logit("WARNING: %s contains unhashed entries", old); logit("Delete this file to ensure privacy " "of hostnames"); } } exit (find_host && !ctx.found_key); } /* * Perform changing a passphrase. The argument is the passwd structure * for the current user. */ static void do_change_passphrase(struct passwd *pw) { char *comment; char *old_passphrase, *passphrase1, *passphrase2; struct stat st; struct sshkey *private; int r; if (!have_identity) ask_filename(pw, "Enter file in which the key is"); if (stat(identity_file, &st) == -1) fatal("%s: %s", identity_file, strerror(errno)); /* Try to load the file with empty passphrase. */ r = sshkey_load_private(identity_file, "", &private, &comment); if (r == SSH_ERR_KEY_WRONG_PASSPHRASE) { if (identity_passphrase) old_passphrase = xstrdup(identity_passphrase); else old_passphrase = read_passphrase("Enter old passphrase: ", RP_ALLOW_STDIN); r = sshkey_load_private(identity_file, old_passphrase, &private, &comment); freezero(old_passphrase, strlen(old_passphrase)); if (r != 0) goto badkey; } else if (r != 0) { badkey: fatal_r(r, "Failed to load key %s", identity_file); } if (comment) mprintf("Key has comment '%s'\n", comment); /* Ask the new passphrase (twice). */ if (identity_new_passphrase) { passphrase1 = xstrdup(identity_new_passphrase); passphrase2 = NULL; } else { passphrase1 = read_passphrase("Enter new passphrase (empty for no " "passphrase): ", RP_ALLOW_STDIN); passphrase2 = read_passphrase("Enter same passphrase again: ", RP_ALLOW_STDIN); /* Verify that they are the same. */ if (strcmp(passphrase1, passphrase2) != 0) { explicit_bzero(passphrase1, strlen(passphrase1)); explicit_bzero(passphrase2, strlen(passphrase2)); free(passphrase1); free(passphrase2); printf("Pass phrases do not match. Try again.\n"); exit(1); } /* Destroy the other copy. */ freezero(passphrase2, strlen(passphrase2)); } /* Save the file using the new passphrase. */ if ((r = sshkey_save_private(private, identity_file, passphrase1, comment, private_key_format, openssh_format_cipher, rounds)) != 0) { error_r(r, "Saving key \"%s\" failed", identity_file); freezero(passphrase1, strlen(passphrase1)); sshkey_free(private); free(comment); exit(1); } /* Destroy the passphrase and the copy of the key in memory. */ freezero(passphrase1, strlen(passphrase1)); sshkey_free(private); /* Destroys contents */ free(comment); printf("Your identification has been saved with the new passphrase.\n"); exit(0); } /* * Print the SSHFP RR. */ static int do_print_resource_record(struct passwd *pw, char *fname, char *hname, int print_generic, char * const *opts, size_t nopts) { struct sshkey *public; char *comment = NULL; struct stat st; int r, hash = -1; size_t i; for (i = 0; i < nopts; i++) { if (strncasecmp(opts[i], "hashalg=", 8) == 0) { if ((hash = ssh_digest_alg_by_name(opts[i] + 8)) == -1) fatal("Unsupported hash algorithm"); } else { error("Invalid option \"%s\"", opts[i]); return SSH_ERR_INVALID_ARGUMENT; } } if (fname == NULL) fatal_f("no filename"); if (stat(fname, &st) == -1) { if (errno == ENOENT) return 0; fatal("%s: %s", fname, strerror(errno)); } if ((r = sshkey_load_public(fname, &public, &comment)) != 0) fatal_r(r, "Failed to read v2 public key from \"%s\"", fname); export_dns_rr(hname, public, stdout, print_generic, hash); sshkey_free(public); free(comment); return 1; } /* * Change the comment of a private key file. */ static void do_change_comment(struct passwd *pw, const char *identity_comment) { char new_comment[1024], *comment, *passphrase; struct sshkey *private; struct sshkey *public; struct stat st; int r; if (!have_identity) ask_filename(pw, "Enter file in which the key is"); if (stat(identity_file, &st) == -1) fatal("%s: %s", identity_file, strerror(errno)); if ((r = sshkey_load_private(identity_file, "", &private, &comment)) == 0) passphrase = xstrdup(""); else if (r != SSH_ERR_KEY_WRONG_PASSPHRASE) fatal_r(r, "Cannot load private key \"%s\"", identity_file); else { if (identity_passphrase) passphrase = xstrdup(identity_passphrase); else if (identity_new_passphrase) passphrase = xstrdup(identity_new_passphrase); else passphrase = read_passphrase("Enter passphrase: ", RP_ALLOW_STDIN); /* Try to load using the passphrase. */ if ((r = sshkey_load_private(identity_file, passphrase, &private, &comment)) != 0) { freezero(passphrase, strlen(passphrase)); fatal_r(r, "Cannot load private key \"%s\"", identity_file); } } if (private->type != KEY_ED25519 && private->type != KEY_XMSS && private_key_format != SSHKEY_PRIVATE_OPENSSH) { error("Comments are only supported for keys stored in " "the new format (-o)."); explicit_bzero(passphrase, strlen(passphrase)); sshkey_free(private); exit(1); } if (comment) printf("Old comment: %s\n", comment); else printf("No existing comment\n"); if (identity_comment) { strlcpy(new_comment, identity_comment, sizeof(new_comment)); } else { printf("New comment: "); fflush(stdout); if (!fgets(new_comment, sizeof(new_comment), stdin)) { explicit_bzero(passphrase, strlen(passphrase)); sshkey_free(private); exit(1); } new_comment[strcspn(new_comment, "\n")] = '\0'; } if (comment != NULL && strcmp(comment, new_comment) == 0) { printf("No change to comment\n"); free(passphrase); sshkey_free(private); free(comment); exit(0); } /* Save the file using the new passphrase. */ if ((r = sshkey_save_private(private, identity_file, passphrase, new_comment, private_key_format, openssh_format_cipher, rounds)) != 0) { error_r(r, "Saving key \"%s\" failed", identity_file); freezero(passphrase, strlen(passphrase)); sshkey_free(private); free(comment); exit(1); } freezero(passphrase, strlen(passphrase)); if ((r = sshkey_from_private(private, &public)) != 0) fatal_fr(r, "sshkey_from_private"); sshkey_free(private); strlcat(identity_file, ".pub", sizeof(identity_file)); if ((r = sshkey_save_public(public, identity_file, new_comment)) != 0) fatal_r(r, "Unable to save public key to %s", identity_file); sshkey_free(public); free(comment); if (strlen(new_comment) > 0) printf("Comment '%s' applied\n", new_comment); else printf("Comment removed\n"); exit(0); } static void cert_ext_add(const char *key, const char *value, int iscrit) { cert_ext = xreallocarray(cert_ext, ncert_ext + 1, sizeof(*cert_ext)); cert_ext[ncert_ext].key = xstrdup(key); cert_ext[ncert_ext].val = value == NULL ? NULL : xstrdup(value); cert_ext[ncert_ext].crit = iscrit; ncert_ext++; } /* qsort(3) comparison function for certificate extensions */ static int cert_ext_cmp(const void *_a, const void *_b) { const struct cert_ext *a = (const struct cert_ext *)_a; const struct cert_ext *b = (const struct cert_ext *)_b; int r; if (a->crit != b->crit) return (a->crit < b->crit) ? -1 : 1; if ((r = strcmp(a->key, b->key)) != 0) return r; if ((a->val == NULL) != (b->val == NULL)) return (a->val == NULL) ? -1 : 1; if (a->val != NULL && (r = strcmp(a->val, b->val)) != 0) return r; return 0; } #define OPTIONS_CRITICAL 1 #define OPTIONS_EXTENSIONS 2 static void prepare_options_buf(struct sshbuf *c, int which) { struct sshbuf *b; size_t i; int r; const struct cert_ext *ext; if ((b = sshbuf_new()) == NULL) fatal_f("sshbuf_new failed"); sshbuf_reset(c); for (i = 0; i < ncert_ext; i++) { ext = &cert_ext[i]; if ((ext->crit && (which & OPTIONS_EXTENSIONS)) || (!ext->crit && (which & OPTIONS_CRITICAL))) continue; if (ext->val == NULL) { /* flag option */ debug3_f("%s", ext->key); if ((r = sshbuf_put_cstring(c, ext->key)) != 0 || (r = sshbuf_put_string(c, NULL, 0)) != 0) fatal_fr(r, "prepare flag"); } else { /* key/value option */ debug3_f("%s=%s", ext->key, ext->val); sshbuf_reset(b); if ((r = sshbuf_put_cstring(c, ext->key)) != 0 || (r = sshbuf_put_cstring(b, ext->val)) != 0 || (r = sshbuf_put_stringb(c, b)) != 0) fatal_fr(r, "prepare k/v"); } } sshbuf_free(b); } static void finalise_cert_exts(void) { /* critical options */ if (certflags_command != NULL) cert_ext_add("force-command", certflags_command, 1); if (certflags_src_addr != NULL) cert_ext_add("source-address", certflags_src_addr, 1); if ((certflags_flags & CERTOPT_REQUIRE_VERIFY) != 0) cert_ext_add("verify-required", NULL, 1); /* extensions */ if ((certflags_flags & CERTOPT_X_FWD) != 0) cert_ext_add("permit-X11-forwarding", NULL, 0); if ((certflags_flags & CERTOPT_AGENT_FWD) != 0) cert_ext_add("permit-agent-forwarding", NULL, 0); if ((certflags_flags & CERTOPT_PORT_FWD) != 0) cert_ext_add("permit-port-forwarding", NULL, 0); if ((certflags_flags & CERTOPT_PTY) != 0) cert_ext_add("permit-pty", NULL, 0); if ((certflags_flags & CERTOPT_USER_RC) != 0) cert_ext_add("permit-user-rc", NULL, 0); if ((certflags_flags & CERTOPT_NO_REQUIRE_USER_PRESENCE) != 0) cert_ext_add("no-touch-required", NULL, 0); /* order lexically by key */ if (ncert_ext > 0) qsort(cert_ext, ncert_ext, sizeof(*cert_ext), cert_ext_cmp); } static struct sshkey * load_pkcs11_key(char *path) { #ifdef ENABLE_PKCS11 struct sshkey **keys = NULL, *public, *private = NULL; int r, i, nkeys; if ((r = sshkey_load_public(path, &public, NULL)) != 0) fatal_r(r, "Couldn't load CA public key \"%s\"", path); nkeys = pkcs11_add_provider(pkcs11provider, identity_passphrase, &keys, NULL); debug3_f("%d keys", nkeys); if (nkeys <= 0) fatal("cannot read public key from pkcs11"); for (i = 0; i < nkeys; i++) { if (sshkey_equal_public(public, keys[i])) { private = keys[i]; continue; } sshkey_free(keys[i]); } free(keys); sshkey_free(public); return private; #else fatal("no pkcs11 support"); #endif /* ENABLE_PKCS11 */ } /* Signer for sshkey_certify_custom that uses the agent */ static int agent_signer(struct sshkey *key, u_char **sigp, size_t *lenp, const u_char *data, size_t datalen, const char *alg, const char *provider, const char *pin, u_int compat, void *ctx) { int *agent_fdp = (int *)ctx; return ssh_agent_sign(*agent_fdp, key, sigp, lenp, data, datalen, alg, compat); } static void do_ca_sign(struct passwd *pw, const char *ca_key_path, int prefer_agent, unsigned long long cert_serial, int cert_serial_autoinc, int argc, char **argv) { int r, i, found, agent_fd = -1; u_int n; struct sshkey *ca, *public; char valid[64], *otmp, *tmp, *cp, *out, *comment; char *ca_fp = NULL, **plist = NULL, *pin = NULL; struct ssh_identitylist *agent_ids; size_t j; struct notifier_ctx *notifier = NULL; #ifdef ENABLE_PKCS11 pkcs11_init(1); #endif tmp = tilde_expand_filename(ca_key_path, pw->pw_uid); if (pkcs11provider != NULL) { /* If a PKCS#11 token was specified then try to use it */ if ((ca = load_pkcs11_key(tmp)) == NULL) fatal("No PKCS#11 key matching %s found", ca_key_path); } else if (prefer_agent) { /* * Agent signature requested. Try to use agent after making * sure the public key specified is actually present in the * agent. */ if ((r = sshkey_load_public(tmp, &ca, NULL)) != 0) fatal_r(r, "Cannot load CA public key %s", tmp); if ((r = ssh_get_authentication_socket(&agent_fd)) != 0) fatal_r(r, "Cannot use public key for CA signature"); if ((r = ssh_fetch_identitylist(agent_fd, &agent_ids)) != 0) fatal_r(r, "Retrieve agent key list"); found = 0; for (j = 0; j < agent_ids->nkeys; j++) { if (sshkey_equal(ca, agent_ids->keys[j])) { found = 1; break; } } if (!found) fatal("CA key %s not found in agent", tmp); ssh_free_identitylist(agent_ids); ca->flags |= SSHKEY_FLAG_EXT; } else { /* CA key is assumed to be a private key on the filesystem */ ca = load_identity(tmp, NULL); if (sshkey_is_sk(ca) && (ca->sk_flags & SSH_SK_USER_VERIFICATION_REQD)) { if ((pin = read_passphrase("Enter PIN for CA key: ", RP_ALLOW_STDIN)) == NULL) fatal_f("couldn't read PIN"); } } free(tmp); if (key_type_name != NULL) { if (sshkey_type_from_name(key_type_name) != ca->type) { fatal("CA key type %s doesn't match specified %s", sshkey_ssh_name(ca), key_type_name); } } else if (ca->type == KEY_RSA) { /* Default to a good signature algorithm */ key_type_name = "rsa-sha2-512"; } ca_fp = sshkey_fingerprint(ca, fingerprint_hash, SSH_FP_DEFAULT); finalise_cert_exts(); for (i = 0; i < argc; i++) { /* Split list of principals */ n = 0; if (cert_principals != NULL) { otmp = tmp = xstrdup(cert_principals); plist = NULL; for (; (cp = strsep(&tmp, ",")) != NULL; n++) { plist = xreallocarray(plist, n + 1, sizeof(*plist)); if (*(plist[n] = xstrdup(cp)) == '\0') fatal("Empty principal name"); } free(otmp); } if (n > SSHKEY_CERT_MAX_PRINCIPALS) fatal("Too many certificate principals specified"); tmp = tilde_expand_filename(argv[i], pw->pw_uid); if ((r = sshkey_load_public(tmp, &public, &comment)) != 0) fatal_r(r, "load pubkey \"%s\"", tmp); if (sshkey_is_cert(public)) fatal_f("key \"%s\" type %s cannot be certified", tmp, sshkey_type(public)); /* Prepare certificate to sign */ if ((r = sshkey_to_certified(public)) != 0) fatal_r(r, "Could not upgrade key %s to certificate", tmp); public->cert->type = cert_key_type; public->cert->serial = (u_int64_t)cert_serial; public->cert->key_id = xstrdup(cert_key_id); public->cert->nprincipals = n; public->cert->principals = plist; public->cert->valid_after = cert_valid_from; public->cert->valid_before = cert_valid_to; prepare_options_buf(public->cert->critical, OPTIONS_CRITICAL); prepare_options_buf(public->cert->extensions, OPTIONS_EXTENSIONS); if ((r = sshkey_from_private(ca, &public->cert->signature_key)) != 0) fatal_r(r, "sshkey_from_private (ca key)"); if (agent_fd != -1 && (ca->flags & SSHKEY_FLAG_EXT) != 0) { if ((r = sshkey_certify_custom(public, ca, key_type_name, sk_provider, NULL, agent_signer, &agent_fd)) != 0) fatal_r(r, "Couldn't certify %s via agent", tmp); } else { if (sshkey_is_sk(ca) && (ca->sk_flags & SSH_SK_USER_PRESENCE_REQD)) { notifier = notify_start(0, "Confirm user presence for key %s %s", sshkey_type(ca), ca_fp); } r = sshkey_certify(public, ca, key_type_name, sk_provider, pin); notify_complete(notifier, "User presence confirmed"); if (r != 0) fatal_r(r, "Couldn't certify key %s", tmp); } if ((cp = strrchr(tmp, '.')) != NULL && strcmp(cp, ".pub") == 0) *cp = '\0'; xasprintf(&out, "%s-cert.pub", tmp); free(tmp); if ((r = sshkey_save_public(public, out, comment)) != 0) { fatal_r(r, "Unable to save public key to %s", identity_file); } if (!quiet) { sshkey_format_cert_validity(public->cert, valid, sizeof(valid)); logit("Signed %s key %s: id \"%s\" serial %llu%s%s " "valid %s", sshkey_cert_type(public), out, public->cert->key_id, (unsigned long long)public->cert->serial, cert_principals != NULL ? " for " : "", cert_principals != NULL ? cert_principals : "", valid); } sshkey_free(public); free(out); if (cert_serial_autoinc) cert_serial++; } if (pin != NULL) freezero(pin, strlen(pin)); free(ca_fp); #ifdef ENABLE_PKCS11 pkcs11_terminate(); #endif exit(0); } static u_int64_t parse_relative_time(const char *s, time_t now) { int64_t mul, secs; mul = *s == '-' ? -1 : 1; if ((secs = convtime(s + 1)) == -1) fatal("Invalid relative certificate time %s", s); if (mul == -1 && secs > now) fatal("Certificate time %s cannot be represented", s); return now + (u_int64_t)(secs * mul); } static void parse_hex_u64(const char *s, uint64_t *up) { char *ep; unsigned long long ull; errno = 0; ull = strtoull(s, &ep, 16); if (*s == '\0' || *ep != '\0') fatal("Invalid certificate time: not a number"); if (errno == ERANGE && ull == ULONG_MAX) fatal_fr(SSH_ERR_SYSTEM_ERROR, "Invalid certificate time"); *up = (uint64_t)ull; } static void parse_cert_times(char *timespec) { char *from, *to; time_t now = time(NULL); int64_t secs; /* +timespec relative to now */ if (*timespec == '+' && strchr(timespec, ':') == NULL) { if ((secs = convtime(timespec + 1)) == -1) fatal("Invalid relative certificate life %s", timespec); cert_valid_to = now + secs; /* * Backdate certificate one minute to avoid problems on hosts * with poorly-synchronised clocks. */ cert_valid_from = ((now - 59)/ 60) * 60; return; } /* * from:to, where * from := [+-]timespec | YYYYMMDD | YYYYMMDDHHMMSS | 0x... | "always" * to := [+-]timespec | YYYYMMDD | YYYYMMDDHHMMSS | 0x... | "forever" */ from = xstrdup(timespec); to = strchr(from, ':'); if (to == NULL || from == to || *(to + 1) == '\0') fatal("Invalid certificate life specification %s", timespec); *to++ = '\0'; if (*from == '-' || *from == '+') cert_valid_from = parse_relative_time(from, now); else if (strcmp(from, "always") == 0) cert_valid_from = 0; else if (strncmp(from, "0x", 2) == 0) parse_hex_u64(from, &cert_valid_from); else if (parse_absolute_time(from, &cert_valid_from) != 0) fatal("Invalid from time \"%s\"", from); if (*to == '-' || *to == '+') cert_valid_to = parse_relative_time(to, now); else if (strcmp(to, "forever") == 0) cert_valid_to = ~(u_int64_t)0; else if (strncmp(to, "0x", 2) == 0) parse_hex_u64(to, &cert_valid_to); else if (parse_absolute_time(to, &cert_valid_to) != 0) fatal("Invalid to time \"%s\"", to); if (cert_valid_to <= cert_valid_from) fatal("Empty certificate validity interval"); free(from); } static void add_cert_option(char *opt) { char *val, *cp; int iscrit = 0; if (strcasecmp(opt, "clear") == 0) certflags_flags = 0; else if (strcasecmp(opt, "no-x11-forwarding") == 0) certflags_flags &= ~CERTOPT_X_FWD; else if (strcasecmp(opt, "permit-x11-forwarding") == 0) certflags_flags |= CERTOPT_X_FWD; else if (strcasecmp(opt, "no-agent-forwarding") == 0) certflags_flags &= ~CERTOPT_AGENT_FWD; else if (strcasecmp(opt, "permit-agent-forwarding") == 0) certflags_flags |= CERTOPT_AGENT_FWD; else if (strcasecmp(opt, "no-port-forwarding") == 0) certflags_flags &= ~CERTOPT_PORT_FWD; else if (strcasecmp(opt, "permit-port-forwarding") == 0) certflags_flags |= CERTOPT_PORT_FWD; else if (strcasecmp(opt, "no-pty") == 0) certflags_flags &= ~CERTOPT_PTY; else if (strcasecmp(opt, "permit-pty") == 0) certflags_flags |= CERTOPT_PTY; else if (strcasecmp(opt, "no-user-rc") == 0) certflags_flags &= ~CERTOPT_USER_RC; else if (strcasecmp(opt, "permit-user-rc") == 0) certflags_flags |= CERTOPT_USER_RC; else if (strcasecmp(opt, "touch-required") == 0) certflags_flags &= ~CERTOPT_NO_REQUIRE_USER_PRESENCE; else if (strcasecmp(opt, "no-touch-required") == 0) certflags_flags |= CERTOPT_NO_REQUIRE_USER_PRESENCE; else if (strcasecmp(opt, "no-verify-required") == 0) certflags_flags &= ~CERTOPT_REQUIRE_VERIFY; else if (strcasecmp(opt, "verify-required") == 0) certflags_flags |= CERTOPT_REQUIRE_VERIFY; else if (strncasecmp(opt, "force-command=", 14) == 0) { val = opt + 14; if (*val == '\0') fatal("Empty force-command option"); if (certflags_command != NULL) fatal("force-command already specified"); certflags_command = xstrdup(val); } else if (strncasecmp(opt, "source-address=", 15) == 0) { val = opt + 15; if (*val == '\0') fatal("Empty source-address option"); if (certflags_src_addr != NULL) fatal("source-address already specified"); if (addr_match_cidr_list(NULL, val) != 0) fatal("Invalid source-address list"); certflags_src_addr = xstrdup(val); } else if (strncasecmp(opt, "extension:", 10) == 0 || (iscrit = (strncasecmp(opt, "critical:", 9) == 0))) { val = xstrdup(strchr(opt, ':') + 1); if ((cp = strchr(val, '=')) != NULL) *cp++ = '\0'; cert_ext_add(val, cp, iscrit); free(val); } else fatal("Unsupported certificate option \"%s\"", opt); } static void show_options(struct sshbuf *optbuf, int in_critical) { char *name, *arg, *hex; struct sshbuf *options, *option = NULL; int r; if ((options = sshbuf_fromb(optbuf)) == NULL) fatal_f("sshbuf_fromb failed"); while (sshbuf_len(options) != 0) { sshbuf_free(option); option = NULL; if ((r = sshbuf_get_cstring(options, &name, NULL)) != 0 || (r = sshbuf_froms(options, &option)) != 0) fatal_fr(r, "parse option"); printf(" %s", name); if (!in_critical && (strcmp(name, "permit-X11-forwarding") == 0 || strcmp(name, "permit-agent-forwarding") == 0 || strcmp(name, "permit-port-forwarding") == 0 || strcmp(name, "permit-pty") == 0 || strcmp(name, "permit-user-rc") == 0 || strcmp(name, "no-touch-required") == 0)) { printf("\n"); } else if (in_critical && (strcmp(name, "force-command") == 0 || strcmp(name, "source-address") == 0)) { if ((r = sshbuf_get_cstring(option, &arg, NULL)) != 0) fatal_fr(r, "parse critical"); printf(" %s\n", arg); free(arg); } else if (in_critical && strcmp(name, "verify-required") == 0) { printf("\n"); } else if (sshbuf_len(option) > 0) { hex = sshbuf_dtob16(option); printf(" UNKNOWN OPTION: %s (len %zu)\n", hex, sshbuf_len(option)); sshbuf_reset(option); free(hex); } else printf(" UNKNOWN FLAG OPTION\n"); free(name); if (sshbuf_len(option) != 0) fatal("Option corrupt: extra data at end"); } sshbuf_free(option); sshbuf_free(options); } static void print_cert(struct sshkey *key) { char valid[64], *key_fp, *ca_fp; u_int i; key_fp = sshkey_fingerprint(key, fingerprint_hash, SSH_FP_DEFAULT); ca_fp = sshkey_fingerprint(key->cert->signature_key, fingerprint_hash, SSH_FP_DEFAULT); if (key_fp == NULL || ca_fp == NULL) fatal_f("sshkey_fingerprint fail"); sshkey_format_cert_validity(key->cert, valid, sizeof(valid)); printf(" Type: %s %s certificate\n", sshkey_ssh_name(key), sshkey_cert_type(key)); printf(" Public key: %s %s\n", sshkey_type(key), key_fp); printf(" Signing CA: %s %s (using %s)\n", sshkey_type(key->cert->signature_key), ca_fp, key->cert->signature_type); printf(" Key ID: \"%s\"\n", key->cert->key_id); printf(" Serial: %llu\n", (unsigned long long)key->cert->serial); printf(" Valid: %s\n", valid); printf(" Principals: "); if (key->cert->nprincipals == 0) printf("(none)\n"); else { for (i = 0; i < key->cert->nprincipals; i++) printf("\n %s", key->cert->principals[i]); printf("\n"); } printf(" Critical Options: "); if (sshbuf_len(key->cert->critical) == 0) printf("(none)\n"); else { printf("\n"); show_options(key->cert->critical, 1); } printf(" Extensions: "); if (sshbuf_len(key->cert->extensions) == 0) printf("(none)\n"); else { printf("\n"); show_options(key->cert->extensions, 0); } } static void do_show_cert(struct passwd *pw) { struct sshkey *key = NULL; struct stat st; int r, is_stdin = 0, ok = 0; FILE *f; char *cp, *line = NULL; const char *path; size_t linesize = 0; u_long lnum = 0; if (!have_identity) ask_filename(pw, "Enter file in which the key is"); if (strcmp(identity_file, "-") != 0 && stat(identity_file, &st) == -1) fatal("%s: %s: %s", __progname, identity_file, strerror(errno)); path = identity_file; if (strcmp(path, "-") == 0) { f = stdin; path = "(stdin)"; is_stdin = 1; } else if ((f = fopen(identity_file, "r")) == NULL) fatal("fopen %s: %s", identity_file, strerror(errno)); while (getline(&line, &linesize, f) != -1) { lnum++; sshkey_free(key); key = NULL; /* Trim leading space and comments */ cp = line + strspn(line, " \t"); if (*cp == '#' || *cp == '\0') continue; if ((key = sshkey_new(KEY_UNSPEC)) == NULL) fatal("sshkey_new"); if ((r = sshkey_read(key, &cp)) != 0) { error_r(r, "%s:%lu: invalid key", path, lnum); continue; } if (!sshkey_is_cert(key)) { error("%s:%lu is not a certificate", path, lnum); continue; } ok = 1; if (!is_stdin && lnum == 1) printf("%s:\n", path); else printf("%s:%lu:\n", path, lnum); print_cert(key); } free(line); sshkey_free(key); fclose(f); exit(ok ? 0 : 1); } static void load_krl(const char *path, struct ssh_krl **krlp) { struct sshbuf *krlbuf; int r; if ((r = sshbuf_load_file(path, &krlbuf)) != 0) fatal_r(r, "Unable to load KRL %s", path); /* XXX check sigs */ if ((r = ssh_krl_from_blob(krlbuf, krlp)) != 0 || *krlp == NULL) fatal_r(r, "Invalid KRL file %s", path); sshbuf_free(krlbuf); } static void hash_to_blob(const char *cp, u_char **blobp, size_t *lenp, const char *file, u_long lnum) { char *tmp; size_t tlen; struct sshbuf *b; int r; if (strncmp(cp, "SHA256:", 7) != 0) fatal("%s:%lu: unsupported hash algorithm", file, lnum); cp += 7; /* * OpenSSH base64 hashes omit trailing '=' * characters; put them back for decode. */ if ((tlen = strlen(cp)) >= SIZE_MAX - 5) fatal_f("hash too long: %zu bytes", tlen); tmp = xmalloc(tlen + 4 + 1); strlcpy(tmp, cp, tlen + 1); while ((tlen % 4) != 0) { tmp[tlen++] = '='; tmp[tlen] = '\0'; } if ((b = sshbuf_new()) == NULL) fatal_f("sshbuf_new failed"); if ((r = sshbuf_b64tod(b, tmp)) != 0) fatal_r(r, "%s:%lu: decode hash failed", file, lnum); free(tmp); *lenp = sshbuf_len(b); *blobp = xmalloc(*lenp); memcpy(*blobp, sshbuf_ptr(b), *lenp); sshbuf_free(b); } static void update_krl_from_file(struct passwd *pw, const char *file, int wild_ca, const struct sshkey *ca, struct ssh_krl *krl) { struct sshkey *key = NULL; u_long lnum = 0; char *path, *cp, *ep, *line = NULL; u_char *blob = NULL; size_t blen = 0, linesize = 0; unsigned long long serial, serial2; int i, was_explicit_key, was_sha1, was_sha256, was_hash, r; FILE *krl_spec; path = tilde_expand_filename(file, pw->pw_uid); if (strcmp(path, "-") == 0) { krl_spec = stdin; free(path); path = xstrdup("(standard input)"); } else if ((krl_spec = fopen(path, "r")) == NULL) fatal("fopen %s: %s", path, strerror(errno)); if (!quiet) printf("Revoking from %s\n", path); while (getline(&line, &linesize, krl_spec) != -1) { if (linesize >= INT_MAX) { fatal_f("%s contains unparsable line, len=%zu", path, linesize); } lnum++; was_explicit_key = was_sha1 = was_sha256 = was_hash = 0; cp = line + strspn(line, " \t"); /* Trim trailing space, comments and strip \n */ for (i = 0, r = -1; cp[i] != '\0'; i++) { if (cp[i] == '#' || cp[i] == '\n') { cp[i] = '\0'; break; } if (cp[i] == ' ' || cp[i] == '\t') { /* Remember the start of a span of whitespace */ if (r == -1) r = i; } else r = -1; } if (r != -1) cp[r] = '\0'; if (*cp == '\0') continue; if (strncasecmp(cp, "serial:", 7) == 0) { if (ca == NULL && !wild_ca) { fatal("revoking certificates by serial number " "requires specification of a CA key"); } cp += 7; cp = cp + strspn(cp, " \t"); errno = 0; serial = strtoull(cp, &ep, 0); if (*cp == '\0' || (*ep != '\0' && *ep != '-')) fatal("%s:%lu: invalid serial \"%s\"", path, lnum, cp); if (errno == ERANGE && serial == ULLONG_MAX) fatal("%s:%lu: serial out of range", path, lnum); serial2 = serial; if (*ep == '-') { cp = ep + 1; errno = 0; serial2 = strtoull(cp, &ep, 0); if (*cp == '\0' || *ep != '\0') fatal("%s:%lu: invalid serial \"%s\"", path, lnum, cp); if (errno == ERANGE && serial2 == ULLONG_MAX) fatal("%s:%lu: serial out of range", path, lnum); if (serial2 <= serial) fatal("%s:%lu: invalid serial range " "%llu:%llu", path, lnum, (unsigned long long)serial, (unsigned long long)serial2); } if (ssh_krl_revoke_cert_by_serial_range(krl, ca, serial, serial2) != 0) { fatal_f("revoke serial failed"); } } else if (strncasecmp(cp, "id:", 3) == 0) { if (ca == NULL && !wild_ca) { fatal("revoking certificates by key ID " "requires specification of a CA key"); } cp += 3; cp = cp + strspn(cp, " \t"); if (ssh_krl_revoke_cert_by_key_id(krl, ca, cp) != 0) fatal_f("revoke key ID failed"); } else if (strncasecmp(cp, "hash:", 5) == 0) { cp += 5; cp = cp + strspn(cp, " \t"); hash_to_blob(cp, &blob, &blen, file, lnum); r = ssh_krl_revoke_key_sha256(krl, blob, blen); if (r != 0) fatal_fr(r, "revoke key failed"); } else { if (strncasecmp(cp, "key:", 4) == 0) { cp += 4; cp = cp + strspn(cp, " \t"); was_explicit_key = 1; } else if (strncasecmp(cp, "sha1:", 5) == 0) { cp += 5; cp = cp + strspn(cp, " \t"); was_sha1 = 1; } else if (strncasecmp(cp, "sha256:", 7) == 0) { cp += 7; cp = cp + strspn(cp, " \t"); was_sha256 = 1; /* * Just try to process the line as a key. * Parsing will fail if it isn't. */ } if ((key = sshkey_new(KEY_UNSPEC)) == NULL) fatal("sshkey_new"); if ((r = sshkey_read(key, &cp)) != 0) fatal_r(r, "%s:%lu: invalid key", path, lnum); if (was_explicit_key) r = ssh_krl_revoke_key_explicit(krl, key); else if (was_sha1) { if (sshkey_fingerprint_raw(key, SSH_DIGEST_SHA1, &blob, &blen) != 0) { fatal("%s:%lu: fingerprint failed", file, lnum); } r = ssh_krl_revoke_key_sha1(krl, blob, blen); } else if (was_sha256) { if (sshkey_fingerprint_raw(key, SSH_DIGEST_SHA256, &blob, &blen) != 0) { fatal("%s:%lu: fingerprint failed", file, lnum); } r = ssh_krl_revoke_key_sha256(krl, blob, blen); } else r = ssh_krl_revoke_key(krl, key); if (r != 0) fatal_fr(r, "revoke key failed"); freezero(blob, blen); blob = NULL; blen = 0; sshkey_free(key); } } if (strcmp(path, "-") != 0) fclose(krl_spec); free(line); free(path); } static void do_gen_krl(struct passwd *pw, int updating, const char *ca_key_path, unsigned long long krl_version, const char *krl_comment, int argc, char **argv) { struct ssh_krl *krl; struct stat sb; struct sshkey *ca = NULL; int i, r, wild_ca = 0; char *tmp; struct sshbuf *kbuf; if (*identity_file == '\0') fatal("KRL generation requires an output file"); if (stat(identity_file, &sb) == -1) { if (errno != ENOENT) fatal("Cannot access KRL \"%s\": %s", identity_file, strerror(errno)); if (updating) fatal("KRL \"%s\" does not exist", identity_file); } if (ca_key_path != NULL) { if (strcasecmp(ca_key_path, "none") == 0) wild_ca = 1; else { tmp = tilde_expand_filename(ca_key_path, pw->pw_uid); if ((r = sshkey_load_public(tmp, &ca, NULL)) != 0) fatal_r(r, "Cannot load CA public key %s", tmp); free(tmp); } } if (updating) load_krl(identity_file, &krl); else if ((krl = ssh_krl_init()) == NULL) fatal("couldn't create KRL"); if (krl_version != 0) ssh_krl_set_version(krl, krl_version); if (krl_comment != NULL) ssh_krl_set_comment(krl, krl_comment); for (i = 0; i < argc; i++) update_krl_from_file(pw, argv[i], wild_ca, ca, krl); if ((kbuf = sshbuf_new()) == NULL) fatal("sshbuf_new failed"); if (ssh_krl_to_blob(krl, kbuf) != 0) fatal("Couldn't generate KRL"); if ((r = sshbuf_write_file(identity_file, kbuf)) != 0) fatal("write %s: %s", identity_file, strerror(errno)); sshbuf_free(kbuf); ssh_krl_free(krl); sshkey_free(ca); } static void do_check_krl(struct passwd *pw, int print_krl, int argc, char **argv) { int i, r, ret = 0; char *comment; struct ssh_krl *krl; struct sshkey *k; if (*identity_file == '\0') fatal("KRL checking requires an input file"); load_krl(identity_file, &krl); if (print_krl) krl_dump(krl, stdout); for (i = 0; i < argc; i++) { if ((r = sshkey_load_public(argv[i], &k, &comment)) != 0) fatal_r(r, "Cannot load public key %s", argv[i]); r = ssh_krl_check_key(krl, k); printf("%s%s%s%s: %s\n", argv[i], *comment ? " (" : "", comment, *comment ? ")" : "", r == 0 ? "ok" : "REVOKED"); if (r != 0) ret = 1; sshkey_free(k); free(comment); } ssh_krl_free(krl); exit(ret); } static struct sshkey * load_sign_key(const char *keypath, const struct sshkey *pubkey) { size_t i, slen, plen = strlen(keypath); char *privpath = xstrdup(keypath); static const char * const suffixes[] = { "-cert.pub", ".pub", NULL }; struct sshkey *ret = NULL, *privkey = NULL; int r, waspub = 0; struct stat st; /* * If passed a public key filename, then try to locate the corresponding * private key. This lets us specify certificates on the command-line * and have ssh-keygen find the appropriate private key. */ for (i = 0; suffixes[i]; i++) { slen = strlen(suffixes[i]); if (plen <= slen || strcmp(privpath + plen - slen, suffixes[i]) != 0) continue; privpath[plen - slen] = '\0'; debug_f("%s looks like a public key, using private key " "path %s instead", keypath, privpath); waspub = 1; } if (waspub && stat(privpath, &st) != 0 && errno == ENOENT) fatal("No private key found for public key \"%s\"", keypath); if ((r = sshkey_load_private(privpath, "", &privkey, NULL)) != 0 && (r != SSH_ERR_KEY_WRONG_PASSPHRASE)) { debug_fr(r, "load private key \"%s\"", privpath); fatal("No private key found for \"%s\"", privpath); } else if (privkey == NULL) privkey = load_identity(privpath, NULL); if (!sshkey_equal_public(pubkey, privkey)) { error("Public key %s doesn't match private %s", keypath, privpath); goto done; } if (sshkey_is_cert(pubkey) && !sshkey_is_cert(privkey)) { /* * Graft the certificate onto the private key to make * it capable of signing. */ if ((r = sshkey_to_certified(privkey)) != 0) { error_fr(r, "sshkey_to_certified"); goto done; } if ((r = sshkey_cert_copy(pubkey, privkey)) != 0) { error_fr(r, "sshkey_cert_copy"); goto done; } } /* success */ ret = privkey; privkey = NULL; done: sshkey_free(privkey); free(privpath); return ret; } static int sign_one(struct sshkey *signkey, const char *filename, int fd, const char *sig_namespace, const char *hashalg, sshsig_signer *signer, void *signer_ctx) { struct sshbuf *sigbuf = NULL, *abuf = NULL; int r = SSH_ERR_INTERNAL_ERROR, wfd = -1, oerrno; char *wfile = NULL, *asig = NULL, *fp = NULL; char *pin = NULL, *prompt = NULL; if (!quiet) { if (fd == STDIN_FILENO) fprintf(stderr, "Signing data on standard input\n"); else fprintf(stderr, "Signing file %s\n", filename); } if (signer == NULL && sshkey_is_sk(signkey)) { if ((signkey->sk_flags & SSH_SK_USER_VERIFICATION_REQD)) { xasprintf(&prompt, "Enter PIN for %s key: ", sshkey_type(signkey)); if ((pin = read_passphrase(prompt, RP_ALLOW_STDIN)) == NULL) fatal_f("couldn't read PIN"); } if ((signkey->sk_flags & SSH_SK_USER_PRESENCE_REQD)) { if ((fp = sshkey_fingerprint(signkey, fingerprint_hash, SSH_FP_DEFAULT)) == NULL) fatal_f("fingerprint failed"); fprintf(stderr, "Confirm user presence for key %s %s\n", sshkey_type(signkey), fp); free(fp); } } if ((r = sshsig_sign_fd(signkey, hashalg, sk_provider, pin, fd, sig_namespace, &sigbuf, signer, signer_ctx)) != 0) { error_r(r, "Signing %s failed", filename); goto out; } if ((r = sshsig_armor(sigbuf, &abuf)) != 0) { error_fr(r, "sshsig_armor"); goto out; } if ((asig = sshbuf_dup_string(abuf)) == NULL) { error_f("buffer error"); r = SSH_ERR_ALLOC_FAIL; goto out; } if (fd == STDIN_FILENO) { fputs(asig, stdout); fflush(stdout); } else { xasprintf(&wfile, "%s.sig", filename); if (confirm_overwrite(wfile)) { if ((wfd = open(wfile, O_WRONLY|O_CREAT|O_TRUNC, 0666)) == -1) { oerrno = errno; error("Cannot open %s: %s", wfile, strerror(errno)); errno = oerrno; r = SSH_ERR_SYSTEM_ERROR; goto out; } if (atomicio(vwrite, wfd, asig, strlen(asig)) != strlen(asig)) { oerrno = errno; error("Cannot write to %s: %s", wfile, strerror(errno)); errno = oerrno; r = SSH_ERR_SYSTEM_ERROR; goto out; } if (!quiet) { fprintf(stderr, "Write signature to %s\n", wfile); } } } /* success */ r = 0; out: free(wfile); free(prompt); free(asig); if (pin != NULL) freezero(pin, strlen(pin)); sshbuf_free(abuf); sshbuf_free(sigbuf); if (wfd != -1) close(wfd); return r; } static int sig_process_opts(char * const *opts, size_t nopts, char **hashalgp, uint64_t *verify_timep, int *print_pubkey) { size_t i; time_t now; if (verify_timep != NULL) *verify_timep = 0; if (print_pubkey != NULL) *print_pubkey = 0; if (hashalgp != NULL) *hashalgp = NULL; for (i = 0; i < nopts; i++) { if (hashalgp != NULL && strncasecmp(opts[i], "hashalg=", 8) == 0) { *hashalgp = xstrdup(opts[i] + 8); } else if (verify_timep && strncasecmp(opts[i], "verify-time=", 12) == 0) { if (parse_absolute_time(opts[i] + 12, verify_timep) != 0 || *verify_timep == 0) { error("Invalid \"verify-time\" option"); return SSH_ERR_INVALID_ARGUMENT; } } else if (print_pubkey && strcasecmp(opts[i], "print-pubkey") == 0) { *print_pubkey = 1; } else { error("Invalid option \"%s\"", opts[i]); return SSH_ERR_INVALID_ARGUMENT; } } if (verify_timep && *verify_timep == 0) { if ((now = time(NULL)) < 0) { error("Time is before epoch"); return SSH_ERR_INVALID_ARGUMENT; } *verify_timep = (uint64_t)now; } return 0; } static int sig_sign(const char *keypath, const char *sig_namespace, int require_agent, int argc, char **argv, char * const *opts, size_t nopts) { int i, fd = -1, r, ret = -1; int agent_fd = -1; struct sshkey *pubkey = NULL, *privkey = NULL, *signkey = NULL; sshsig_signer *signer = NULL; char *hashalg = NULL; /* Check file arguments. */ for (i = 0; i < argc; i++) { if (strcmp(argv[i], "-") != 0) continue; if (i > 0 || argc > 1) fatal("Cannot sign mix of paths and standard input"); } if (sig_process_opts(opts, nopts, &hashalg, NULL, NULL) != 0) goto done; /* error already logged */ if ((r = sshkey_load_public(keypath, &pubkey, NULL)) != 0) { error_r(r, "Couldn't load public key %s", keypath); goto done; } if ((r = ssh_get_authentication_socket(&agent_fd)) != 0) { if (require_agent) fatal("Couldn't get agent socket"); debug_r(r, "Couldn't get agent socket"); } else { if ((r = ssh_agent_has_key(agent_fd, pubkey)) == 0) signer = agent_signer; else { if (require_agent) fatal("Couldn't find key in agent"); debug_r(r, "Couldn't find key in agent"); } } if (signer == NULL) { /* Not using agent - try to load private key */ if ((privkey = load_sign_key(keypath, pubkey)) == NULL) goto done; signkey = privkey; } else { /* Will use key in agent */ signkey = pubkey; } if (argc == 0) { if ((r = sign_one(signkey, "(stdin)", STDIN_FILENO, sig_namespace, hashalg, signer, &agent_fd)) != 0) goto done; } else { for (i = 0; i < argc; i++) { if (strcmp(argv[i], "-") == 0) fd = STDIN_FILENO; else if ((fd = open(argv[i], O_RDONLY)) == -1) { error("Cannot open %s for signing: %s", argv[i], strerror(errno)); goto done; } if ((r = sign_one(signkey, argv[i], fd, sig_namespace, hashalg, signer, &agent_fd)) != 0) goto done; if (fd != STDIN_FILENO) close(fd); fd = -1; } } ret = 0; done: if (fd != -1 && fd != STDIN_FILENO) close(fd); sshkey_free(pubkey); sshkey_free(privkey); free(hashalg); return ret; } static int sig_verify(const char *signature, const char *sig_namespace, const char *principal, const char *allowed_keys, const char *revoked_keys, char * const *opts, size_t nopts) { int r, ret = -1; int print_pubkey = 0; struct sshbuf *sigbuf = NULL, *abuf = NULL; struct sshkey *sign_key = NULL; char *fp = NULL; struct sshkey_sig_details *sig_details = NULL; uint64_t verify_time = 0; if (sig_process_opts(opts, nopts, NULL, &verify_time, &print_pubkey) != 0) goto done; /* error already logged */ memset(&sig_details, 0, sizeof(sig_details)); if ((r = sshbuf_load_file(signature, &abuf)) != 0) { error_r(r, "Couldn't read signature file"); goto done; } if ((r = sshsig_dearmor(abuf, &sigbuf)) != 0) { error_fr(r, "sshsig_armor"); goto done; } if ((r = sshsig_verify_fd(sigbuf, STDIN_FILENO, sig_namespace, &sign_key, &sig_details)) != 0) goto done; /* sshsig_verify() prints error */ if ((fp = sshkey_fingerprint(sign_key, fingerprint_hash, SSH_FP_DEFAULT)) == NULL) fatal_f("sshkey_fingerprint failed"); debug("Valid (unverified) signature from key %s", fp); if (sig_details != NULL) { debug2_f("signature details: counter = %u, flags = 0x%02x", sig_details->sk_counter, sig_details->sk_flags); } free(fp); fp = NULL; if (revoked_keys != NULL) { if ((r = sshkey_check_revoked(sign_key, revoked_keys)) != 0) { debug3_fr(r, "sshkey_check_revoked"); goto done; } } if (allowed_keys != NULL && (r = sshsig_check_allowed_keys(allowed_keys, sign_key, principal, sig_namespace, verify_time)) != 0) { debug3_fr(r, "sshsig_check_allowed_keys"); goto done; } /* success */ ret = 0; done: if (!quiet) { if (ret == 0) { if ((fp = sshkey_fingerprint(sign_key, fingerprint_hash, SSH_FP_DEFAULT)) == NULL) fatal_f("sshkey_fingerprint failed"); if (principal == NULL) { printf("Good \"%s\" signature with %s key %s\n", sig_namespace, sshkey_type(sign_key), fp); } else { printf("Good \"%s\" signature for %s with %s key %s\n", sig_namespace, principal, sshkey_type(sign_key), fp); } } else { printf("Could not verify signature.\n"); } } /* Print the signature key if requested */ if (ret == 0 && print_pubkey && sign_key != NULL) { if ((r = sshkey_write(sign_key, stdout)) == 0) fputc('\n', stdout); else { error_r(r, "Could not print public key.\n"); ret = -1; } } sshbuf_free(sigbuf); sshbuf_free(abuf); sshkey_free(sign_key); sshkey_sig_details_free(sig_details); free(fp); return ret; } static int sig_find_principals(const char *signature, const char *allowed_keys, char * const *opts, size_t nopts) { int r, ret = -1; struct sshbuf *sigbuf = NULL, *abuf = NULL; struct sshkey *sign_key = NULL; char *principals = NULL, *cp, *tmp; uint64_t verify_time = 0; if (sig_process_opts(opts, nopts, NULL, &verify_time, NULL) != 0) goto done; /* error already logged */ if ((r = sshbuf_load_file(signature, &abuf)) != 0) { error_r(r, "Couldn't read signature file"); goto done; } if ((r = sshsig_dearmor(abuf, &sigbuf)) != 0) { error_fr(r, "sshsig_armor"); goto done; } if ((r = sshsig_get_pubkey(sigbuf, &sign_key)) != 0) { error_fr(r, "sshsig_get_pubkey"); goto done; } if ((r = sshsig_find_principals(allowed_keys, sign_key, verify_time, &principals)) != 0) { if (r != SSH_ERR_KEY_NOT_FOUND) error_fr(r, "sshsig_find_principal"); goto done; } ret = 0; done: if (ret == 0 ) { /* Emit matching principals one per line */ tmp = principals; while ((cp = strsep(&tmp, ",")) != NULL && *cp != '\0') puts(cp); } else { fprintf(stderr, "No principal matched.\n"); } sshbuf_free(sigbuf); sshbuf_free(abuf); sshkey_free(sign_key); free(principals); return ret; } static int sig_match_principals(const char *allowed_keys, char *principal, char * const *opts, size_t nopts) { int r; char **principals = NULL; size_t i, nprincipals = 0; if ((r = sig_process_opts(opts, nopts, NULL, NULL, NULL)) != 0) return r; /* error already logged */ if ((r = sshsig_match_principals(allowed_keys, principal, &principals, &nprincipals)) != 0) { debug_f("match: %s", ssh_err(r)); fprintf(stderr, "No principal matched.\n"); return r; } for (i = 0; i < nprincipals; i++) { printf("%s\n", principals[i]); free(principals[i]); } free(principals); return 0; } static void do_moduli_gen(const char *out_file, char **opts, size_t nopts) { #ifdef WITH_OPENSSL /* Moduli generation/screening */ u_int32_t memory = 0; BIGNUM *start = NULL; int moduli_bits = 0; FILE *out; size_t i; const char *errstr; /* Parse options */ for (i = 0; i < nopts; i++) { if (strncmp(opts[i], "memory=", 7) == 0) { memory = (u_int32_t)strtonum(opts[i]+7, 1, UINT_MAX, &errstr); if (errstr) { fatal("Memory limit is %s: %s", errstr, opts[i]+7); } } else if (strncmp(opts[i], "start=", 6) == 0) { /* XXX - also compare length against bits */ if (BN_hex2bn(&start, opts[i]+6) == 0) fatal("Invalid start point."); } else if (strncmp(opts[i], "bits=", 5) == 0) { moduli_bits = (int)strtonum(opts[i]+5, 1, INT_MAX, &errstr); if (errstr) { fatal("Invalid number: %s (%s)", opts[i]+12, errstr); } } else { fatal("Option \"%s\" is unsupported for moduli " "generation", opts[i]); } } if ((out = fopen(out_file, "w")) == NULL) { fatal("Couldn't open modulus candidate file \"%s\": %s", out_file, strerror(errno)); } setvbuf(out, NULL, _IOLBF, 0); if (moduli_bits == 0) moduli_bits = DEFAULT_BITS; if (gen_candidates(out, memory, moduli_bits, start) != 0) fatal("modulus candidate generation failed"); #else /* WITH_OPENSSL */ fatal("Moduli generation is not supported"); #endif /* WITH_OPENSSL */ } static void do_moduli_screen(const char *out_file, char **opts, size_t nopts) { #ifdef WITH_OPENSSL /* Moduli generation/screening */ char *checkpoint = NULL; u_int32_t generator_wanted = 0; unsigned long start_lineno = 0, lines_to_process = 0; int prime_tests = 0; FILE *out, *in = stdin; size_t i; const char *errstr; /* Parse options */ for (i = 0; i < nopts; i++) { if (strncmp(opts[i], "lines=", 6) == 0) { lines_to_process = strtoul(opts[i]+6, NULL, 10); } else if (strncmp(opts[i], "start-line=", 11) == 0) { start_lineno = strtoul(opts[i]+11, NULL, 10); } else if (strncmp(opts[i], "checkpoint=", 11) == 0) { free(checkpoint); checkpoint = xstrdup(opts[i]+11); } else if (strncmp(opts[i], "generator=", 10) == 0) { generator_wanted = (u_int32_t)strtonum( opts[i]+10, 1, UINT_MAX, &errstr); if (errstr != NULL) { fatal("Generator invalid: %s (%s)", opts[i]+10, errstr); } } else if (strncmp(opts[i], "prime-tests=", 12) == 0) { prime_tests = (int)strtonum(opts[i]+12, 1, INT_MAX, &errstr); if (errstr) { fatal("Invalid number: %s (%s)", opts[i]+12, errstr); } } else { fatal("Option \"%s\" is unsupported for moduli " "screening", opts[i]); } } if (have_identity && strcmp(identity_file, "-") != 0) { if ((in = fopen(identity_file, "r")) == NULL) { fatal("Couldn't open modulus candidate " "file \"%s\": %s", identity_file, strerror(errno)); } } if ((out = fopen(out_file, "a")) == NULL) { fatal("Couldn't open moduli file \"%s\": %s", out_file, strerror(errno)); } setvbuf(out, NULL, _IOLBF, 0); if (prime_test(in, out, prime_tests == 0 ? 100 : prime_tests, generator_wanted, checkpoint, start_lineno, lines_to_process) != 0) fatal("modulus screening failed"); if (in != stdin) (void)fclose(in); free(checkpoint); #else /* WITH_OPENSSL */ fatal("Moduli screening is not supported"); #endif /* WITH_OPENSSL */ } /* Read and confirm a passphrase */ static char * read_check_passphrase(const char *prompt1, const char *prompt2, const char *retry_prompt) { char *passphrase1, *passphrase2; for (;;) { passphrase1 = read_passphrase(prompt1, RP_ALLOW_STDIN); passphrase2 = read_passphrase(prompt2, RP_ALLOW_STDIN); if (strcmp(passphrase1, passphrase2) == 0) { freezero(passphrase2, strlen(passphrase2)); return passphrase1; } /* The passphrases do not match. Clear them and retry. */ freezero(passphrase1, strlen(passphrase1)); freezero(passphrase2, strlen(passphrase2)); fputs(retry_prompt, stdout); fputc('\n', stdout); fflush(stdout); } /* NOTREACHED */ return NULL; } static char * private_key_passphrase(void) { if (identity_passphrase) return xstrdup(identity_passphrase); if (identity_new_passphrase) return xstrdup(identity_new_passphrase); return read_check_passphrase( "Enter passphrase (empty for no passphrase): ", "Enter same passphrase again: ", "Passphrases do not match. Try again."); } static char * sk_suffix(const char *application, const uint8_t *user, size_t userlen) { char *ret, *cp; size_t slen, i; /* Trim off URL-like preamble */ if (strncmp(application, "ssh://", 6) == 0) ret = xstrdup(application + 6); else if (strncmp(application, "ssh:", 4) == 0) ret = xstrdup(application + 4); else ret = xstrdup(application); /* Count trailing zeros in user */ for (i = 0; i < userlen; i++) { if (user[userlen - i - 1] != 0) break; } if (i >= userlen) return ret; /* user-id was default all-zeros */ /* Append user-id, escaping non-UTF-8 characters */ slen = userlen - i; if (asmprintf(&cp, INT_MAX, NULL, "%.*s", (int)slen, user) == -1) fatal_f("asmprintf failed"); /* Don't emit a user-id that contains path or control characters */ if (strchr(cp, '/') != NULL || strstr(cp, "..") != NULL || strchr(cp, '\\') != NULL) { free(cp); cp = tohex(user, slen); } xextendf(&ret, "_", "%s", cp); free(cp); return ret; } static int do_download_sk(const char *skprovider, const char *device) { struct sshsk_resident_key **srks; size_t nsrks, i; int r, ret = -1; char *fp, *pin = NULL, *pass = NULL, *path, *pubpath; const char *ext; struct sshkey *key; if (skprovider == NULL) fatal("Cannot download keys without provider"); pin = read_passphrase("Enter PIN for authenticator: ", RP_ALLOW_STDIN); if (!quiet) { printf("You may need to touch your authenticator " "to authorize key download.\n"); } if ((r = sshsk_load_resident(skprovider, device, pin, 0, &srks, &nsrks)) != 0) { if (pin != NULL) freezero(pin, strlen(pin)); error_r(r, "Unable to load resident keys"); return -1; } if (nsrks == 0) logit("No keys to download"); if (pin != NULL) freezero(pin, strlen(pin)); for (i = 0; i < nsrks; i++) { key = srks[i]->key; if (key->type != KEY_ECDSA_SK && key->type != KEY_ED25519_SK) { error("Unsupported key type %s (%d)", sshkey_type(key), key->type); continue; } if ((fp = sshkey_fingerprint(key, fingerprint_hash, SSH_FP_DEFAULT)) == NULL) fatal_f("sshkey_fingerprint failed"); debug_f("key %zu: %s %s %s (flags 0x%02x)", i, sshkey_type(key), fp, key->sk_application, key->sk_flags); ext = sk_suffix(key->sk_application, srks[i]->user_id, srks[i]->user_id_len); xasprintf(&path, "id_%s_rk%s%s", key->type == KEY_ECDSA_SK ? "ecdsa_sk" : "ed25519_sk", *ext == '\0' ? "" : "_", ext); /* If the file already exists, ask the user to confirm. */ if (!confirm_overwrite(path)) { free(path); break; } /* Save the key with the application string as the comment */ if (pass == NULL) pass = private_key_passphrase(); if ((r = sshkey_save_private(key, path, pass, key->sk_application, private_key_format, openssh_format_cipher, rounds)) != 0) { error_r(r, "Saving key \"%s\" failed", path); free(path); break; } if (!quiet) { printf("Saved %s key%s%s to %s\n", sshkey_type(key), *ext != '\0' ? " " : "", *ext != '\0' ? key->sk_application : "", path); } /* Save public key too */ xasprintf(&pubpath, "%s.pub", path); free(path); if ((r = sshkey_save_public(key, pubpath, key->sk_application)) != 0) { error_r(r, "Saving public key \"%s\" failed", pubpath); free(pubpath); break; } free(pubpath); } if (i >= nsrks) ret = 0; /* success */ if (pass != NULL) freezero(pass, strlen(pass)); sshsk_free_resident_keys(srks, nsrks); return ret; } static void save_attestation(struct sshbuf *attest, const char *path) { mode_t omask; int r; if (path == NULL) return; /* nothing to do */ if (attest == NULL || sshbuf_len(attest) == 0) fatal("Enrollment did not return attestation data"); omask = umask(077); r = sshbuf_write_file(path, attest); umask(omask); if (r != 0) fatal_r(r, "Unable to write attestation data \"%s\"", path); if (!quiet) printf("Your FIDO attestation certificate has been saved in " "%s\n", path); } static int confirm_sk_overwrite(const char *application, const char *user) { char yesno[3]; printf("A resident key scoped to '%s' with user id '%s' already " "exists.\n", application == NULL ? "ssh:" : application, user == NULL ? "null" : user); printf("Overwrite key in token (y/n)? "); fflush(stdout); if (fgets(yesno, sizeof(yesno), stdin) == NULL) return 0; if (yesno[0] != 'y' && yesno[0] != 'Y') return 0; return 1; } static void usage(void) { fprintf(stderr, "usage: ssh-keygen [-q] [-a rounds] [-b bits] [-C comment] [-f output_keyfile]\n" " [-m format] [-N new_passphrase] [-O option]\n" " [-t dsa | ecdsa | ecdsa-sk | ed25519 | ed25519-sk | rsa]\n" " [-w provider] [-Z cipher]\n" " ssh-keygen -p [-a rounds] [-f keyfile] [-m format] [-N new_passphrase]\n" " [-P old_passphrase] [-Z cipher]\n" #ifdef WITH_OPENSSL " ssh-keygen -i [-f input_keyfile] [-m key_format]\n" " ssh-keygen -e [-f input_keyfile] [-m key_format]\n" #endif " ssh-keygen -y [-f input_keyfile]\n" " ssh-keygen -c [-a rounds] [-C comment] [-f keyfile] [-P passphrase]\n" " ssh-keygen -l [-v] [-E fingerprint_hash] [-f input_keyfile]\n" " ssh-keygen -B [-f input_keyfile]\n"); #ifdef ENABLE_PKCS11 fprintf(stderr, " ssh-keygen -D pkcs11\n"); #endif fprintf(stderr, " ssh-keygen -F hostname [-lv] [-f known_hosts_file]\n" " ssh-keygen -H [-f known_hosts_file]\n" " ssh-keygen -K [-a rounds] [-w provider]\n" " ssh-keygen -R hostname [-f known_hosts_file]\n" " ssh-keygen -r hostname [-g] [-f input_keyfile]\n" #ifdef WITH_OPENSSL " ssh-keygen -M generate [-O option] output_file\n" " ssh-keygen -M screen [-f input_file] [-O option] output_file\n" #endif " ssh-keygen -I certificate_identity -s ca_key [-hU] [-D pkcs11_provider]\n" " [-n principals] [-O option] [-V validity_interval]\n" " [-z serial_number] file ...\n" " ssh-keygen -L [-f input_keyfile]\n" " ssh-keygen -A [-a rounds] [-f prefix_path]\n" " ssh-keygen -k -f krl_file [-u] [-s ca_public] [-z version_number]\n" " file ...\n" " ssh-keygen -Q [-l] -f krl_file [file ...]\n" " ssh-keygen -Y find-principals -s signature_file -f allowed_signers_file\n" " ssh-keygen -Y match-principals -I signer_identity -f allowed_signers_file\n" " ssh-keygen -Y check-novalidate -n namespace -s signature_file\n" " ssh-keygen -Y sign -f key_file -n namespace file [-O option] ...\n" " ssh-keygen -Y verify -f allowed_signers_file -I signer_identity\n" " -n namespace -s signature_file [-r krl_file] [-O option]\n"); exit(1); } /* * Main program for key management. */ int main(int argc, char **argv) { char comment[1024], *passphrase = NULL; char *rr_hostname = NULL, *ep, *fp, *ra; struct sshkey *private, *public; struct passwd *pw; int r, opt, type; int change_passphrase = 0, change_comment = 0, show_cert = 0; int find_host = 0, delete_host = 0, hash_hosts = 0; int gen_all_hostkeys = 0, gen_krl = 0, update_krl = 0, check_krl = 0; int prefer_agent = 0, convert_to = 0, convert_from = 0; int print_public = 0, print_generic = 0, cert_serial_autoinc = 0; int do_gen_candidates = 0, do_screen_candidates = 0, download_sk = 0; unsigned long long cert_serial = 0; char *identity_comment = NULL, *ca_key_path = NULL, **opts = NULL; char *sk_application = NULL, *sk_device = NULL, *sk_user = NULL; char *sk_attestation_path = NULL; struct sshbuf *challenge = NULL, *attest = NULL; size_t i, nopts = 0; u_int32_t bits = 0; uint8_t sk_flags = SSH_SK_USER_PRESENCE_REQD; const char *errstr; int log_level = SYSLOG_LEVEL_INFO; char *sign_op = NULL; extern int optind; extern char *optarg; /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ sanitise_stdfd(); __progname = ssh_get_progname(argv[0]); seed_rng(); log_init(argv[0], SYSLOG_LEVEL_INFO, SYSLOG_FACILITY_USER, 1); msetlocale(); /* we need this for the home * directory. */ pw = getpwuid(getuid()); if (!pw) fatal("No user exists for uid %lu", (u_long)getuid()); pw = pwcopy(pw); if (gethostname(hostname, sizeof(hostname)) == -1) fatal("gethostname: %s", strerror(errno)); sk_provider = getenv("SSH_SK_PROVIDER"); /* Remaining characters: dGjJSTWx */ while ((opt = getopt(argc, argv, "ABHKLQUXceghiklopquvy" "C:D:E:F:I:M:N:O:P:R:V:Y:Z:" "a:b:f:g:m:n:r:s:t:w:z:")) != -1) { switch (opt) { case 'A': gen_all_hostkeys = 1; break; case 'b': bits = (u_int32_t)strtonum(optarg, 1, UINT32_MAX, &errstr); if (errstr) fatal("Bits has bad value %s (%s)", optarg, errstr); break; case 'E': fingerprint_hash = ssh_digest_alg_by_name(optarg); if (fingerprint_hash == -1) fatal("Invalid hash algorithm \"%s\"", optarg); break; case 'F': find_host = 1; rr_hostname = optarg; break; case 'H': hash_hosts = 1; break; case 'I': cert_key_id = optarg; break; case 'R': delete_host = 1; rr_hostname = optarg; break; case 'L': show_cert = 1; break; case 'l': print_fingerprint = 1; break; case 'B': print_bubblebabble = 1; break; case 'm': if (strcasecmp(optarg, "RFC4716") == 0 || strcasecmp(optarg, "ssh2") == 0) { convert_format = FMT_RFC4716; break; } if (strcasecmp(optarg, "PKCS8") == 0) { convert_format = FMT_PKCS8; private_key_format = SSHKEY_PRIVATE_PKCS8; break; } if (strcasecmp(optarg, "PEM") == 0) { convert_format = FMT_PEM; private_key_format = SSHKEY_PRIVATE_PEM; break; } fatal("Unsupported conversion format \"%s\"", optarg); case 'n': cert_principals = optarg; break; case 'o': /* no-op; new format is already the default */ break; case 'p': change_passphrase = 1; break; case 'c': change_comment = 1; break; case 'f': if (strlcpy(identity_file, optarg, sizeof(identity_file)) >= sizeof(identity_file)) fatal("Identity filename too long"); have_identity = 1; break; case 'g': print_generic = 1; break; case 'K': download_sk = 1; break; case 'P': identity_passphrase = optarg; break; case 'N': identity_new_passphrase = optarg; break; case 'Q': check_krl = 1; break; case 'O': opts = xrecallocarray(opts, nopts, nopts + 1, sizeof(*opts)); opts[nopts++] = xstrdup(optarg); break; case 'Z': openssh_format_cipher = optarg; if (cipher_by_name(openssh_format_cipher) == NULL) fatal("Invalid OpenSSH-format cipher '%s'", openssh_format_cipher); break; case 'C': identity_comment = optarg; break; case 'q': quiet = 1; break; case 'e': /* export key */ convert_to = 1; break; case 'h': cert_key_type = SSH2_CERT_TYPE_HOST; certflags_flags = 0; break; case 'k': gen_krl = 1; break; case 'i': case 'X': /* import key */ convert_from = 1; break; case 'y': print_public = 1; break; case 's': ca_key_path = optarg; break; case 't': key_type_name = optarg; break; case 'D': pkcs11provider = optarg; break; case 'U': prefer_agent = 1; break; case 'u': update_krl = 1; break; case 'v': if (log_level == SYSLOG_LEVEL_INFO) log_level = SYSLOG_LEVEL_DEBUG1; else { if (log_level >= SYSLOG_LEVEL_DEBUG1 && log_level < SYSLOG_LEVEL_DEBUG3) log_level++; } break; case 'r': rr_hostname = optarg; break; case 'a': rounds = (int)strtonum(optarg, 1, INT_MAX, &errstr); if (errstr) fatal("Invalid number: %s (%s)", optarg, errstr); break; case 'V': parse_cert_times(optarg); break; case 'Y': sign_op = optarg; break; case 'w': sk_provider = optarg; break; case 'z': errno = 0; if (*optarg == '+') { cert_serial_autoinc = 1; optarg++; } cert_serial = strtoull(optarg, &ep, 10); if (*optarg < '0' || *optarg > '9' || *ep != '\0' || (errno == ERANGE && cert_serial == ULLONG_MAX)) fatal("Invalid serial number \"%s\"", optarg); break; case 'M': if (strcmp(optarg, "generate") == 0) do_gen_candidates = 1; else if (strcmp(optarg, "screen") == 0) do_screen_candidates = 1; else fatal("Unsupported moduli option %s", optarg); break; default: usage(); } } #ifdef ENABLE_SK_INTERNAL if (sk_provider == NULL) sk_provider = "internal"; #endif /* reinit */ log_init(argv[0], log_level, SYSLOG_FACILITY_USER, 1); argv += optind; argc -= optind; if (sign_op != NULL) { if (strncmp(sign_op, "find-principals", 15) == 0) { if (ca_key_path == NULL) { error("Too few arguments for find-principals:" "missing signature file"); exit(1); } if (!have_identity) { error("Too few arguments for find-principals:" "missing allowed keys file"); exit(1); } return sig_find_principals(ca_key_path, identity_file, opts, nopts); } else if (strncmp(sign_op, "match-principals", 16) == 0) { if (!have_identity) { error("Too few arguments for match-principals:" "missing allowed keys file"); exit(1); } if (cert_key_id == NULL) { error("Too few arguments for match-principals: " "missing principal ID"); exit(1); } return sig_match_principals(identity_file, cert_key_id, opts, nopts); } else if (strncmp(sign_op, "sign", 4) == 0) { /* NB. cert_principals is actually namespace, via -n */ if (cert_principals == NULL || *cert_principals == '\0') { error("Too few arguments for sign: " "missing namespace"); exit(1); } if (!have_identity) { error("Too few arguments for sign: " "missing key"); exit(1); } return sig_sign(identity_file, cert_principals, prefer_agent, argc, argv, opts, nopts); } else if (strncmp(sign_op, "check-novalidate", 16) == 0) { /* NB. cert_principals is actually namespace, via -n */ if (cert_principals == NULL || *cert_principals == '\0') { error("Too few arguments for check-novalidate: " "missing namespace"); exit(1); } if (ca_key_path == NULL) { error("Too few arguments for check-novalidate: " "missing signature file"); exit(1); } return sig_verify(ca_key_path, cert_principals, NULL, NULL, NULL, opts, nopts); } else if (strncmp(sign_op, "verify", 6) == 0) { /* NB. cert_principals is actually namespace, via -n */ if (cert_principals == NULL || *cert_principals == '\0') { error("Too few arguments for verify: " "missing namespace"); exit(1); } if (ca_key_path == NULL) { error("Too few arguments for verify: " "missing signature file"); exit(1); } if (!have_identity) { error("Too few arguments for sign: " "missing allowed keys file"); exit(1); } if (cert_key_id == NULL) { error("Too few arguments for verify: " "missing principal identity"); exit(1); } return sig_verify(ca_key_path, cert_principals, cert_key_id, identity_file, rr_hostname, opts, nopts); } error("Unsupported operation for -Y: \"%s\"", sign_op); usage(); /* NOTREACHED */ } if (ca_key_path != NULL) { if (argc < 1 && !gen_krl) { error("Too few arguments."); usage(); } } else if (argc > 0 && !gen_krl && !check_krl && !do_gen_candidates && !do_screen_candidates) { error("Too many arguments."); usage(); } if (change_passphrase && change_comment) { error("Can only have one of -p and -c."); usage(); } if (print_fingerprint && (delete_host || hash_hosts)) { error("Cannot use -l with -H or -R."); usage(); } if (gen_krl) { do_gen_krl(pw, update_krl, ca_key_path, cert_serial, identity_comment, argc, argv); return (0); } if (check_krl) { do_check_krl(pw, print_fingerprint, argc, argv); return (0); } if (ca_key_path != NULL) { if (cert_key_id == NULL) fatal("Must specify key id (-I) when certifying"); for (i = 0; i < nopts; i++) add_cert_option(opts[i]); do_ca_sign(pw, ca_key_path, prefer_agent, cert_serial, cert_serial_autoinc, argc, argv); } if (show_cert) do_show_cert(pw); if (delete_host || hash_hosts || find_host) { do_known_hosts(pw, rr_hostname, find_host, delete_host, hash_hosts); } if (pkcs11provider != NULL) do_download(pw); if (download_sk) { for (i = 0; i < nopts; i++) { if (strncasecmp(opts[i], "device=", 7) == 0) { sk_device = xstrdup(opts[i] + 7); } else { fatal("Option \"%s\" is unsupported for " "FIDO authenticator download", opts[i]); } } return do_download_sk(sk_provider, sk_device); } if (print_fingerprint || print_bubblebabble) do_fingerprint(pw); if (change_passphrase) do_change_passphrase(pw); if (change_comment) do_change_comment(pw, identity_comment); #ifdef WITH_OPENSSL if (convert_to) do_convert_to(pw); if (convert_from) do_convert_from(pw); #else /* WITH_OPENSSL */ if (convert_to || convert_from) fatal("key conversion disabled at compile time"); #endif /* WITH_OPENSSL */ if (print_public) do_print_public(pw); if (rr_hostname != NULL) { unsigned int n = 0; if (have_identity) { n = do_print_resource_record(pw, identity_file, rr_hostname, print_generic, opts, nopts); if (n == 0) fatal("%s: %s", identity_file, strerror(errno)); exit(0); } else { n += do_print_resource_record(pw, _PATH_HOST_RSA_KEY_FILE, rr_hostname, print_generic, opts, nopts); n += do_print_resource_record(pw, _PATH_HOST_DSA_KEY_FILE, rr_hostname, print_generic, opts, nopts); n += do_print_resource_record(pw, _PATH_HOST_ECDSA_KEY_FILE, rr_hostname, print_generic, opts, nopts); n += do_print_resource_record(pw, _PATH_HOST_ED25519_KEY_FILE, rr_hostname, print_generic, opts, nopts); n += do_print_resource_record(pw, _PATH_HOST_XMSS_KEY_FILE, rr_hostname, print_generic, opts, nopts); if (n == 0) fatal("no keys found."); exit(0); } } if (do_gen_candidates || do_screen_candidates) { if (argc <= 0) fatal("No output file specified"); else if (argc > 1) fatal("Too many output files specified"); } if (do_gen_candidates) { do_moduli_gen(argv[0], opts, nopts); return 0; } if (do_screen_candidates) { do_moduli_screen(argv[0], opts, nopts); return 0; } if (gen_all_hostkeys) { do_gen_all_hostkeys(pw); return (0); } if (key_type_name == NULL) key_type_name = DEFAULT_KEY_TYPE_NAME; type = sshkey_type_from_name(key_type_name); type_bits_valid(type, key_type_name, &bits); if (!quiet) printf("Generating public/private %s key pair.\n", key_type_name); switch (type) { case KEY_ECDSA_SK: case KEY_ED25519_SK: for (i = 0; i < nopts; i++) { if (strcasecmp(opts[i], "no-touch-required") == 0) { sk_flags &= ~SSH_SK_USER_PRESENCE_REQD; } else if (strcasecmp(opts[i], "verify-required") == 0) { sk_flags |= SSH_SK_USER_VERIFICATION_REQD; } else if (strcasecmp(opts[i], "resident") == 0) { sk_flags |= SSH_SK_RESIDENT_KEY; } else if (strncasecmp(opts[i], "device=", 7) == 0) { sk_device = xstrdup(opts[i] + 7); } else if (strncasecmp(opts[i], "user=", 5) == 0) { sk_user = xstrdup(opts[i] + 5); } else if (strncasecmp(opts[i], "challenge=", 10) == 0) { if ((r = sshbuf_load_file(opts[i] + 10, &challenge)) != 0) { fatal_r(r, "Unable to load FIDO " "enrollment challenge \"%s\"", opts[i] + 10); } } else if (strncasecmp(opts[i], "write-attestation=", 18) == 0) { sk_attestation_path = opts[i] + 18; } else if (strncasecmp(opts[i], "application=", 12) == 0) { sk_application = xstrdup(opts[i] + 12); if (strncmp(sk_application, "ssh:", 4) != 0) { fatal("FIDO application string must " "begin with \"ssh:\""); } } else { fatal("Option \"%s\" is unsupported for " "FIDO authenticator enrollment", opts[i]); } } if ((attest = sshbuf_new()) == NULL) fatal("sshbuf_new failed"); r = 0; for (i = 0 ;;) { if (!quiet) { printf("You may need to touch your " "authenticator%s to authorize key " "generation.\n", r == 0 ? "" : " again"); } fflush(stdout); r = sshsk_enroll(type, sk_provider, sk_device, sk_application == NULL ? "ssh:" : sk_application, sk_user, sk_flags, passphrase, challenge, &private, attest); if (r == 0) break; if (r == SSH_ERR_KEY_BAD_PERMISSIONS && (sk_flags & SSH_SK_RESIDENT_KEY) != 0 && (sk_flags & SSH_SK_FORCE_OPERATION) == 0 && confirm_sk_overwrite(sk_application, sk_user)) { sk_flags |= SSH_SK_FORCE_OPERATION; continue; } if (r != SSH_ERR_KEY_WRONG_PASSPHRASE) fatal_r(r, "Key enrollment failed"); else if (passphrase != NULL) { error("PIN incorrect"); freezero(passphrase, strlen(passphrase)); passphrase = NULL; } if (++i >= 3) fatal("Too many incorrect PINs"); passphrase = read_passphrase("Enter PIN for " "authenticator: ", RP_ALLOW_STDIN); } if (passphrase != NULL) { freezero(passphrase, strlen(passphrase)); passphrase = NULL; } break; default: if ((r = sshkey_generate(type, bits, &private)) != 0) fatal("sshkey_generate failed"); break; } if ((r = sshkey_from_private(private, &public)) != 0) fatal_r(r, "sshkey_from_private"); if (!have_identity) ask_filename(pw, "Enter file in which to save the key"); /* Create ~/.ssh directory if it doesn't already exist. */ hostfile_create_user_ssh_dir(identity_file, !quiet); /* If the file already exists, ask the user to confirm. */ if (!confirm_overwrite(identity_file)) exit(1); /* Determine the passphrase for the private key */ passphrase = private_key_passphrase(); if (identity_comment) { strlcpy(comment, identity_comment, sizeof(comment)); } else { /* Create default comment field for the passphrase. */ snprintf(comment, sizeof comment, "%s@%s", pw->pw_name, hostname); } /* Save the key with the given passphrase and comment. */ if ((r = sshkey_save_private(private, identity_file, passphrase, comment, private_key_format, openssh_format_cipher, rounds)) != 0) { error_r(r, "Saving key \"%s\" failed", identity_file); freezero(passphrase, strlen(passphrase)); exit(1); } freezero(passphrase, strlen(passphrase)); sshkey_free(private); if (!quiet) { printf("Your identification has been saved in %s\n", identity_file); } strlcat(identity_file, ".pub", sizeof(identity_file)); if ((r = sshkey_save_public(public, identity_file, comment)) != 0) fatal_r(r, "Unable to save public key to %s", identity_file); if (!quiet) { fp = sshkey_fingerprint(public, fingerprint_hash, SSH_FP_DEFAULT); ra = sshkey_fingerprint(public, fingerprint_hash, SSH_FP_RANDOMART); if (fp == NULL || ra == NULL) fatal("sshkey_fingerprint failed"); printf("Your public key has been saved in %s\n", identity_file); printf("The key fingerprint is:\n"); printf("%s %s\n", fp, comment); printf("The key's randomart image is:\n"); printf("%s\n", ra); free(ra); free(fp); } if (sk_attestation_path != NULL) save_attestation(attest, sk_attestation_path); sshbuf_free(attest); sshkey_free(public); exit(0); } diff --git a/ssh.c b/ssh.c index caf3c692c7dd..1dbbda7d6e36 100644 --- a/ssh.c +++ b/ssh.c @@ -1,2408 +1,2411 @@ -/* $OpenBSD: ssh.c,v 1.593 2023/07/26 23:06:00 djm Exp $ */ +/* $OpenBSD: ssh.c,v 1.594 2023/09/03 23:59:32 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * Ssh client program. This program can be used to log into a remote machine. * The software supports strong authentication, encryption, and forwarding * of X11, TCP/IP, and authentication connections. * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". * * Copyright (c) 1999 Niels Provos. All rights reserved. * Copyright (c) 2000, 2001, 2002, 2003 Markus Friedl. All rights reserved. * * Modified to work with SSLeay by Niels Provos * in Canada (German citizen). * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" #include #ifdef HAVE_SYS_STAT_H # include #endif #include #include #include #include #include #include #include #include #ifdef HAVE_PATHS_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef WITH_OPENSSL #include #include #endif #include "openbsd-compat/openssl-compat.h" #include "openbsd-compat/sys-queue.h" #include "xmalloc.h" #include "ssh.h" #include "ssh2.h" #include "canohost.h" #include "compat.h" #include "cipher.h" #include "packet.h" #include "sshbuf.h" #include "channels.h" #include "sshkey.h" #include "authfd.h" #include "authfile.h" #include "pathnames.h" #include "dispatch.h" #include "clientloop.h" #include "log.h" #include "misc.h" #include "readconf.h" #include "sshconnect.h" #include "kex.h" #include "mac.h" #include "sshpty.h" #include "match.h" #include "msg.h" #include "version.h" #include "ssherr.h" #include "myproposal.h" #include "utf8.h" #ifdef ENABLE_PKCS11 #include "ssh-pkcs11.h" #endif extern char *__progname; /* Saves a copy of argv for setproctitle emulation */ #ifndef HAVE_SETPROCTITLE static char **saved_av; #endif /* Flag indicating whether debug mode is on. May be set on the command line. */ int debug_flag = 0; /* Flag indicating whether a tty should be requested */ int tty_flag = 0; /* * Flag indicating that the current process should be backgrounded and * a new mux-client launched in the foreground for ControlPersist. */ static int need_controlpersist_detach = 0; /* Copies of flags for ControlPersist foreground mux-client */ static int ostdin_null_flag, osession_type, otty_flag, orequest_tty; static int ofork_after_authentication; /* * General data structure for command line options and options configurable * in configuration files. See readconf.h. */ Options options; /* optional user configfile */ char *config = NULL; /* * Name of the host we are connecting to. This is the name given on the * command line, or the Hostname specified for the user-supplied name in a * configuration file. */ char *host; /* * A config can specify a path to forward, overriding SSH_AUTH_SOCK. If this is * not NULL, forward the socket at this path instead. */ char *forward_agent_sock_path = NULL; /* socket address the host resolves to */ struct sockaddr_storage hostaddr; /* Private host keys. */ Sensitive sensitive_data; /* command to be executed */ struct sshbuf *command; /* # of replies received for global requests */ static int forward_confirms_pending = -1; /* mux.c */ extern int muxserver_sock; extern u_int muxclient_command; /* Prints a help message to the user. This function never returns. */ static void usage(void) { fprintf(stderr, "usage: ssh [-46AaCfGgKkMNnqsTtVvXxYy] [-B bind_interface] [-b bind_address]\n" " [-c cipher_spec] [-D [bind_address:]port] [-E log_file]\n" " [-e escape_char] [-F configfile] [-I pkcs11] [-i identity_file]\n" " [-J destination] [-L address] [-l login_name] [-m mac_spec]\n" " [-O ctl_cmd] [-o option] [-P tag] [-p port] [-Q query_option]\n" " [-R address] [-S ctl_path] [-W host:port] [-w local_tun[:remote_tun]]\n" " destination [command [argument ...]]\n" ); exit(255); } static int ssh_session2(struct ssh *, const struct ssh_conn_info *); static void load_public_identity_files(const struct ssh_conn_info *); static void main_sigchld_handler(int); /* ~/ expand a list of paths. NB. assumes path[n] is heap-allocated. */ static void tilde_expand_paths(char **paths, u_int num_paths) { u_int i; char *cp; for (i = 0; i < num_paths; i++) { cp = tilde_expand_filename(paths[i], getuid()); free(paths[i]); paths[i] = cp; } } /* * Expands the set of percent_expand options used by the majority of keywords * in the client that support percent expansion. * Caller must free returned string. */ static char * default_client_percent_expand(const char *str, const struct ssh_conn_info *cinfo) { return percent_expand(str, DEFAULT_CLIENT_PERCENT_EXPAND_ARGS(cinfo), (char *)NULL); } /* * Expands the set of percent_expand options used by the majority of keywords * AND perform environment variable substitution. * Caller must free returned string. */ static char * default_client_percent_dollar_expand(const char *str, const struct ssh_conn_info *cinfo) { char *ret; ret = percent_dollar_expand(str, DEFAULT_CLIENT_PERCENT_EXPAND_ARGS(cinfo), (char *)NULL); if (ret == NULL) fatal("invalid environment variable expansion"); return ret; } /* * Attempt to resolve a host name / port to a set of addresses and * optionally return any CNAMEs encountered along the way. * Returns NULL on failure. * NB. this function must operate with a options having undefined members. */ static struct addrinfo * resolve_host(const char *name, int port, int logerr, char *cname, size_t clen) { char strport[NI_MAXSERV]; const char *errstr = NULL; struct addrinfo hints, *res; int gaierr; LogLevel loglevel = SYSLOG_LEVEL_DEBUG1; if (port <= 0) port = default_ssh_port(); if (cname != NULL) *cname = '\0'; debug3_f("lookup %s:%d", name, port); snprintf(strport, sizeof strport, "%d", port); memset(&hints, 0, sizeof(hints)); hints.ai_family = options.address_family == -1 ? AF_UNSPEC : options.address_family; hints.ai_socktype = SOCK_STREAM; if (cname != NULL) hints.ai_flags = AI_CANONNAME; if ((gaierr = getaddrinfo(name, strport, &hints, &res)) != 0) { if (logerr || (gaierr != EAI_NONAME && gaierr != EAI_NODATA)) loglevel = SYSLOG_LEVEL_ERROR; do_log2(loglevel, "%s: Could not resolve hostname %.100s: %s", __progname, name, ssh_gai_strerror(gaierr)); return NULL; } if (cname != NULL && res->ai_canonname != NULL) { if (!valid_domain(res->ai_canonname, 0, &errstr)) { error("ignoring bad CNAME \"%s\" for host \"%s\": %s", res->ai_canonname, name, errstr); } else if (strlcpy(cname, res->ai_canonname, clen) >= clen) { error_f("host \"%s\" cname \"%s\" too long (max %lu)", name, res->ai_canonname, (u_long)clen); if (clen > 0) *cname = '\0'; } } return res; } /* Returns non-zero if name can only be an address and not a hostname */ static int is_addr_fast(const char *name) { return (strchr(name, '%') != NULL || strchr(name, ':') != NULL || strspn(name, "0123456789.") == strlen(name)); } /* Returns non-zero if name represents a valid, single address */ static int is_addr(const char *name) { char strport[NI_MAXSERV]; struct addrinfo hints, *res; if (is_addr_fast(name)) return 1; snprintf(strport, sizeof strport, "%u", default_ssh_port()); memset(&hints, 0, sizeof(hints)); hints.ai_family = options.address_family == -1 ? AF_UNSPEC : options.address_family; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_NUMERICHOST|AI_NUMERICSERV; if (getaddrinfo(name, strport, &hints, &res) != 0) return 0; if (res == NULL || res->ai_next != NULL) { freeaddrinfo(res); return 0; } freeaddrinfo(res); return 1; } /* * Attempt to resolve a numeric host address / port to a single address. * Returns a canonical address string. * Returns NULL on failure. * NB. this function must operate with a options having undefined members. */ static struct addrinfo * resolve_addr(const char *name, int port, char *caddr, size_t clen) { char addr[NI_MAXHOST], strport[NI_MAXSERV]; struct addrinfo hints, *res; int gaierr; if (port <= 0) port = default_ssh_port(); snprintf(strport, sizeof strport, "%u", port); memset(&hints, 0, sizeof(hints)); hints.ai_family = options.address_family == -1 ? AF_UNSPEC : options.address_family; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_NUMERICHOST|AI_NUMERICSERV; if ((gaierr = getaddrinfo(name, strport, &hints, &res)) != 0) { debug2_f("could not resolve name %.100s as address: %s", name, ssh_gai_strerror(gaierr)); return NULL; } if (res == NULL) { debug_f("getaddrinfo %.100s returned no addresses", name); return NULL; } if (res->ai_next != NULL) { debug_f("getaddrinfo %.100s returned multiple addresses", name); goto fail; } if ((gaierr = getnameinfo(res->ai_addr, res->ai_addrlen, addr, sizeof(addr), NULL, 0, NI_NUMERICHOST)) != 0) { debug_f("Could not format address for name %.100s: %s", name, ssh_gai_strerror(gaierr)); goto fail; } if (strlcpy(caddr, addr, clen) >= clen) { error_f("host \"%s\" addr \"%s\" too long (max %lu)", name, addr, (u_long)clen); if (clen > 0) *caddr = '\0'; fail: freeaddrinfo(res); return NULL; } return res; } /* * Check whether the cname is a permitted replacement for the hostname * and perform the replacement if it is. * NB. this function must operate with a options having undefined members. */ static int check_follow_cname(int direct, char **namep, const char *cname) { int i; struct allowed_cname *rule; if (*cname == '\0' || !config_has_permitted_cnames(&options) || strcmp(*namep, cname) == 0) return 0; if (options.canonicalize_hostname == SSH_CANONICALISE_NO) return 0; /* * Don't attempt to canonicalize names that will be interpreted by * a proxy or jump host unless the user specifically requests so. */ if (!direct && options.canonicalize_hostname != SSH_CANONICALISE_ALWAYS) return 0; debug3_f("check \"%s\" CNAME \"%s\"", *namep, cname); for (i = 0; i < options.num_permitted_cnames; i++) { rule = options.permitted_cnames + i; if (match_pattern_list(*namep, rule->source_list, 1) != 1 || match_pattern_list(cname, rule->target_list, 1) != 1) continue; verbose("Canonicalized DNS aliased hostname " "\"%s\" => \"%s\"", *namep, cname); free(*namep); *namep = xstrdup(cname); return 1; } return 0; } /* * Attempt to resolve the supplied hostname after applying the user's * canonicalization rules. Returns the address list for the host or NULL * if no name was found after canonicalization. * NB. this function must operate with a options having undefined members. */ static struct addrinfo * resolve_canonicalize(char **hostp, int port) { int i, direct, ndots; char *cp, *fullhost, newname[NI_MAXHOST]; struct addrinfo *addrs; /* * Attempt to canonicalise addresses, regardless of * whether hostname canonicalisation was requested */ if ((addrs = resolve_addr(*hostp, port, newname, sizeof(newname))) != NULL) { debug2_f("hostname %.100s is address", *hostp); if (strcasecmp(*hostp, newname) != 0) { debug2_f("canonicalised address \"%s\" => \"%s\"", *hostp, newname); free(*hostp); *hostp = xstrdup(newname); } return addrs; } /* * If this looks like an address but didn't parse as one, it might * be an address with an invalid interface scope. Skip further * attempts at canonicalisation. */ if (is_addr_fast(*hostp)) { debug_f("hostname %.100s is an unrecognised address", *hostp); return NULL; } if (options.canonicalize_hostname == SSH_CANONICALISE_NO) return NULL; /* * Don't attempt to canonicalize names that will be interpreted by * a proxy unless the user specifically requests so. */ direct = option_clear_or_none(options.proxy_command) && option_clear_or_none(options.jump_host); if (!direct && options.canonicalize_hostname != SSH_CANONICALISE_ALWAYS) return NULL; /* If domain name is anchored, then resolve it now */ if ((*hostp)[strlen(*hostp) - 1] == '.') { debug3_f("name is fully qualified"); fullhost = xstrdup(*hostp); if ((addrs = resolve_host(fullhost, port, 0, newname, sizeof(newname))) != NULL) goto found; free(fullhost); goto notfound; } /* Don't apply canonicalization to sufficiently-qualified hostnames */ ndots = 0; for (cp = *hostp; *cp != '\0'; cp++) { if (*cp == '.') ndots++; } if (ndots > options.canonicalize_max_dots) { debug3_f("not canonicalizing hostname \"%s\" (max dots %d)", *hostp, options.canonicalize_max_dots); return NULL; } /* Attempt each supplied suffix */ for (i = 0; i < options.num_canonical_domains; i++) { if (strcasecmp(options.canonical_domains[i], "none") == 0) break; xasprintf(&fullhost, "%s.%s.", *hostp, options.canonical_domains[i]); debug3_f("attempting \"%s\" => \"%s\"", *hostp, fullhost); if ((addrs = resolve_host(fullhost, port, 0, newname, sizeof(newname))) == NULL) { free(fullhost); continue; } found: /* Remove trailing '.' */ fullhost[strlen(fullhost) - 1] = '\0'; /* Follow CNAME if requested */ if (!check_follow_cname(direct, &fullhost, newname)) { debug("Canonicalized hostname \"%s\" => \"%s\"", *hostp, fullhost); } free(*hostp); *hostp = fullhost; return addrs; } notfound: if (!options.canonicalize_fallback_local) fatal("%s: Could not resolve host \"%s\"", __progname, *hostp); debug2_f("host %s not found in any suffix", *hostp); return NULL; } /* * Check the result of hostkey loading, ignoring some errors and either * discarding the key or fatal()ing for others. */ static void check_load(int r, struct sshkey **k, const char *path, const char *message) { switch (r) { case 0: /* Check RSA keys size and discard if undersized */ if (k != NULL && *k != NULL && (r = sshkey_check_rsa_length(*k, options.required_rsa_size)) != 0) { error_r(r, "load %s \"%s\"", message, path); free(*k); *k = NULL; } break; case SSH_ERR_INTERNAL_ERROR: case SSH_ERR_ALLOC_FAIL: fatal_r(r, "load %s \"%s\"", message, path); case SSH_ERR_SYSTEM_ERROR: /* Ignore missing files */ if (errno == ENOENT) break; /* FALLTHROUGH */ default: error_r(r, "load %s \"%s\"", message, path); break; } } /* * Read per-user configuration file. Ignore the system wide config * file if the user specifies a config file on the command line. */ static void process_config_files(const char *host_name, struct passwd *pw, int final_pass, int *want_final_pass) { char buf[PATH_MAX]; int r; if (config != NULL) { if (strcasecmp(config, "none") != 0 && !read_config_file(config, pw, host, host_name, &options, SSHCONF_USERCONF | (final_pass ? SSHCONF_FINAL : 0), want_final_pass)) fatal("Can't open user config file %.100s: " "%.100s", config, strerror(errno)); } else { r = snprintf(buf, sizeof buf, "%s/%s", pw->pw_dir, _PATH_SSH_USER_CONFFILE); if (r > 0 && (size_t)r < sizeof(buf)) (void)read_config_file(buf, pw, host, host_name, &options, SSHCONF_CHECKPERM | SSHCONF_USERCONF | (final_pass ? SSHCONF_FINAL : 0), want_final_pass); /* Read systemwide configuration file after user config. */ (void)read_config_file(_PATH_HOST_CONFIG_FILE, pw, host, host_name, &options, final_pass ? SSHCONF_FINAL : 0, want_final_pass); } } /* Rewrite the port number in an addrinfo list of addresses */ static void set_addrinfo_port(struct addrinfo *addrs, int port) { struct addrinfo *addr; for (addr = addrs; addr != NULL; addr = addr->ai_next) { switch (addr->ai_family) { case AF_INET: ((struct sockaddr_in *)addr->ai_addr)-> sin_port = htons(port); break; case AF_INET6: ((struct sockaddr_in6 *)addr->ai_addr)-> sin6_port = htons(port); break; } } } static void ssh_conn_info_free(struct ssh_conn_info *cinfo) { if (cinfo == NULL) return; free(cinfo->conn_hash_hex); free(cinfo->shorthost); free(cinfo->uidstr); free(cinfo->keyalias); free(cinfo->thishost); free(cinfo->host_arg); free(cinfo->portstr); free(cinfo->remhost); free(cinfo->remuser); free(cinfo->homedir); free(cinfo->locuser); free(cinfo); } /* * Main program for the ssh client. */ int main(int ac, char **av) { struct ssh *ssh = NULL; int i, r, opt, exit_status, use_syslog, direct, timeout_ms; int was_addr, config_test = 0, opt_terminated = 0, want_final_pass = 0; char *p, *cp, *line, *argv0, *logfile; char cname[NI_MAXHOST], thishost[NI_MAXHOST]; struct stat st; struct passwd *pw; extern int optind, optreset; extern char *optarg; struct Forward fwd; struct addrinfo *addrs = NULL; size_t n, len; u_int j; struct ssh_conn_info *cinfo = NULL; /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ sanitise_stdfd(); /* * Discard other fds that are hanging around. These can cause problem * with backgrounded ssh processes started by ControlPersist. */ closefrom(STDERR_FILENO + 1); __progname = ssh_get_progname(av[0]); #ifndef HAVE_SETPROCTITLE /* Prepare for later setproctitle emulation */ /* Save argv so it isn't clobbered by setproctitle() emulation */ saved_av = xcalloc(ac + 1, sizeof(*saved_av)); for (i = 0; i < ac; i++) saved_av[i] = xstrdup(av[i]); saved_av[i] = NULL; compat_init_setproctitle(ac, av); av = saved_av; #endif seed_rng(); /* Get user data. */ pw = getpwuid(getuid()); if (!pw) { logit("No user exists for uid %lu", (u_long)getuid()); exit(255); } /* Take a copy of the returned structure. */ pw = pwcopy(pw); /* * Set our umask to something reasonable, as some files are created * with the default umask. This will make them world-readable but * writable only by the owner, which is ok for all files for which we * don't set the modes explicitly. */ umask(022 | umask(077)); msetlocale(); /* * Initialize option structure to indicate that no values have been * set. */ initialize_options(&options); /* * Prepare main ssh transport/connection structures */ if ((ssh = ssh_alloc_session_state()) == NULL) fatal("Couldn't allocate session state"); channel_init_channels(ssh); /* Parse command-line arguments. */ host = NULL; use_syslog = 0; logfile = NULL; argv0 = av[0]; again: while ((opt = getopt(ac, av, "1246ab:c:e:fgi:kl:m:no:p:qstvx" "AB:CD:E:F:GI:J:KL:MNO:P:Q:R:S:TVw:W:XYy")) != -1) { /* HUZdhjruz */ switch (opt) { case '1': fatal("SSH protocol v.1 is no longer supported"); break; case '2': /* Ignored */ break; case '4': options.address_family = AF_INET; break; case '6': options.address_family = AF_INET6; break; case 'n': options.stdin_null = 1; break; case 'f': options.fork_after_authentication = 1; options.stdin_null = 1; break; case 'x': options.forward_x11 = 0; break; case 'X': options.forward_x11 = 1; break; case 'y': use_syslog = 1; break; case 'E': logfile = optarg; break; case 'G': config_test = 1; break; case 'Y': options.forward_x11 = 1; options.forward_x11_trusted = 1; break; case 'g': options.fwd_opts.gateway_ports = 1; break; case 'O': if (options.stdio_forward_host != NULL) fatal("Cannot specify multiplexing " "command with -W"); else if (muxclient_command != 0) fatal("Multiplexing command already specified"); if (strcmp(optarg, "check") == 0) muxclient_command = SSHMUX_COMMAND_ALIVE_CHECK; else if (strcmp(optarg, "forward") == 0) muxclient_command = SSHMUX_COMMAND_FORWARD; else if (strcmp(optarg, "exit") == 0) muxclient_command = SSHMUX_COMMAND_TERMINATE; else if (strcmp(optarg, "stop") == 0) muxclient_command = SSHMUX_COMMAND_STOP; else if (strcmp(optarg, "cancel") == 0) muxclient_command = SSHMUX_COMMAND_CANCEL_FWD; else if (strcmp(optarg, "proxy") == 0) muxclient_command = SSHMUX_COMMAND_PROXY; else fatal("Invalid multiplex command."); break; case 'P': if (options.tag == NULL) options.tag = xstrdup(optarg); break; case 'Q': cp = NULL; if (strcmp(optarg, "cipher") == 0 || strcasecmp(optarg, "Ciphers") == 0) cp = cipher_alg_list('\n', 0); else if (strcmp(optarg, "cipher-auth") == 0) cp = cipher_alg_list('\n', 1); else if (strcmp(optarg, "mac") == 0 || strcasecmp(optarg, "MACs") == 0) cp = mac_alg_list('\n'); else if (strcmp(optarg, "kex") == 0 || strcasecmp(optarg, "KexAlgorithms") == 0) cp = kex_alg_list('\n'); else if (strcmp(optarg, "key") == 0) cp = sshkey_alg_list(0, 0, 0, '\n'); else if (strcmp(optarg, "key-cert") == 0) cp = sshkey_alg_list(1, 0, 0, '\n'); else if (strcmp(optarg, "key-plain") == 0) cp = sshkey_alg_list(0, 1, 0, '\n'); else if (strcmp(optarg, "key-ca-sign") == 0 || strcasecmp(optarg, "CASignatureAlgorithms") == 0) cp = sshkey_alg_list(0, 1, 1, '\n'); else if (strcmp(optarg, "key-sig") == 0 || strcasecmp(optarg, "PubkeyAcceptedKeyTypes") == 0 || /* deprecated name */ strcasecmp(optarg, "PubkeyAcceptedAlgorithms") == 0 || strcasecmp(optarg, "HostKeyAlgorithms") == 0 || strcasecmp(optarg, "HostbasedKeyTypes") == 0 || /* deprecated name */ strcasecmp(optarg, "HostbasedAcceptedKeyTypes") == 0 || /* deprecated name */ strcasecmp(optarg, "HostbasedAcceptedAlgorithms") == 0) cp = sshkey_alg_list(0, 0, 1, '\n'); else if (strcmp(optarg, "sig") == 0) cp = sshkey_alg_list(0, 1, 1, '\n'); else if (strcmp(optarg, "protocol-version") == 0) cp = xstrdup("2"); else if (strcmp(optarg, "compression") == 0) { cp = xstrdup(compression_alg_list(0)); len = strlen(cp); for (n = 0; n < len; n++) if (cp[n] == ',') cp[n] = '\n'; } else if (strcmp(optarg, "help") == 0) { cp = xstrdup( "cipher\ncipher-auth\ncompression\nkex\n" "key\nkey-cert\nkey-plain\nkey-sig\nmac\n" "protocol-version\nsig"); } if (cp == NULL) fatal("Unsupported query \"%s\"", optarg); printf("%s\n", cp); free(cp); exit(0); break; case 'a': options.forward_agent = 0; break; case 'A': options.forward_agent = 1; break; case 'k': options.gss_deleg_creds = 0; break; case 'K': options.gss_authentication = 1; options.gss_deleg_creds = 1; break; case 'i': p = tilde_expand_filename(optarg, getuid()); if (stat(p, &st) == -1) fprintf(stderr, "Warning: Identity file %s " "not accessible: %s.\n", p, strerror(errno)); else add_identity_file(&options, NULL, p, 1); free(p); break; case 'I': #ifdef ENABLE_PKCS11 free(options.pkcs11_provider); options.pkcs11_provider = xstrdup(optarg); #else fprintf(stderr, "no support for PKCS#11.\n"); #endif break; case 'J': if (options.jump_host != NULL) { fatal("Only a single -J option is permitted " "(use commas to separate multiple " "jump hops)"); } if (options.proxy_command != NULL) fatal("Cannot specify -J with ProxyCommand"); if (parse_jump(optarg, &options, 1) == -1) fatal("Invalid -J argument"); options.proxy_command = xstrdup("none"); break; case 't': if (options.request_tty == REQUEST_TTY_YES) options.request_tty = REQUEST_TTY_FORCE; else options.request_tty = REQUEST_TTY_YES; break; case 'v': if (debug_flag == 0) { debug_flag = 1; options.log_level = SYSLOG_LEVEL_DEBUG1; } else { if (options.log_level < SYSLOG_LEVEL_DEBUG3) { debug_flag++; options.log_level++; } } break; case 'V': fprintf(stderr, "%s, %s\n", SSH_RELEASE, SSH_OPENSSL_VERSION); exit(0); break; case 'w': if (options.tun_open == -1) options.tun_open = SSH_TUNMODE_DEFAULT; options.tun_local = a2tun(optarg, &options.tun_remote); if (options.tun_local == SSH_TUNID_ERR) { fprintf(stderr, "Bad tun device '%s'\n", optarg); exit(255); } break; case 'W': if (options.stdio_forward_host != NULL) fatal("stdio forward already specified"); if (muxclient_command != 0) fatal("Cannot specify stdio forward with -O"); if (parse_forward(&fwd, optarg, 1, 0)) { options.stdio_forward_host = fwd.listen_port == PORT_STREAMLOCAL ? fwd.listen_path : fwd.listen_host; options.stdio_forward_port = fwd.listen_port; free(fwd.connect_host); } else { fprintf(stderr, "Bad stdio forwarding specification '%s'\n", optarg); exit(255); } options.request_tty = REQUEST_TTY_NO; options.session_type = SESSION_TYPE_NONE; break; case 'q': options.log_level = SYSLOG_LEVEL_QUIET; break; case 'e': if (optarg[0] == '^' && optarg[2] == 0 && (u_char) optarg[1] >= 64 && (u_char) optarg[1] < 128) options.escape_char = (u_char) optarg[1] & 31; else if (strlen(optarg) == 1) options.escape_char = (u_char) optarg[0]; else if (strcmp(optarg, "none") == 0) options.escape_char = SSH_ESCAPECHAR_NONE; else { fprintf(stderr, "Bad escape character '%s'.\n", optarg); exit(255); } break; case 'c': if (!ciphers_valid(*optarg == '+' || *optarg == '^' ? optarg + 1 : optarg)) { fprintf(stderr, "Unknown cipher type '%s'\n", optarg); exit(255); } free(options.ciphers); options.ciphers = xstrdup(optarg); break; case 'm': if (mac_valid(optarg)) { free(options.macs); options.macs = xstrdup(optarg); } else { fprintf(stderr, "Unknown mac type '%s'\n", optarg); exit(255); } break; case 'M': if (options.control_master == SSHCTL_MASTER_YES) options.control_master = SSHCTL_MASTER_ASK; else options.control_master = SSHCTL_MASTER_YES; break; case 'p': if (options.port == -1) { options.port = a2port(optarg); if (options.port <= 0) { fprintf(stderr, "Bad port '%s'\n", optarg); exit(255); } } break; case 'l': if (options.user == NULL) options.user = optarg; break; case 'L': if (parse_forward(&fwd, optarg, 0, 0)) add_local_forward(&options, &fwd); else { fprintf(stderr, "Bad local forwarding specification '%s'\n", optarg); exit(255); } break; case 'R': if (parse_forward(&fwd, optarg, 0, 1) || parse_forward(&fwd, optarg, 1, 1)) { add_remote_forward(&options, &fwd); } else { fprintf(stderr, "Bad remote forwarding specification " "'%s'\n", optarg); exit(255); } break; case 'D': if (parse_forward(&fwd, optarg, 1, 0)) { add_local_forward(&options, &fwd); } else { fprintf(stderr, "Bad dynamic forwarding specification " "'%s'\n", optarg); exit(255); } break; case 'C': #ifdef WITH_ZLIB options.compression = 1; #else error("Compression not supported, disabling."); #endif break; case 'N': if (options.session_type != -1 && options.session_type != SESSION_TYPE_NONE) fatal("Cannot specify -N with -s/SessionType"); options.session_type = SESSION_TYPE_NONE; options.request_tty = REQUEST_TTY_NO; break; case 'T': options.request_tty = REQUEST_TTY_NO; break; case 'o': line = xstrdup(optarg); if (process_config_line(&options, pw, host ? host : "", host ? host : "", line, "command-line", 0, NULL, SSHCONF_USERCONF) != 0) exit(255); free(line); break; case 's': if (options.session_type != -1 && options.session_type != SESSION_TYPE_SUBSYSTEM) fatal("Cannot specify -s with -N/SessionType"); options.session_type = SESSION_TYPE_SUBSYSTEM; break; case 'S': free(options.control_path); options.control_path = xstrdup(optarg); break; case 'b': options.bind_address = optarg; break; case 'B': options.bind_interface = optarg; break; case 'F': config = optarg; break; default: usage(); } } if (optind > 1 && strcmp(av[optind - 1], "--") == 0) opt_terminated = 1; ac -= optind; av += optind; if (ac > 0 && !host) { int tport; char *tuser; switch (parse_ssh_uri(*av, &tuser, &host, &tport)) { case -1: usage(); break; case 0: if (options.user == NULL) { options.user = tuser; tuser = NULL; } free(tuser); if (options.port == -1 && tport != -1) options.port = tport; break; default: p = xstrdup(*av); cp = strrchr(p, '@'); if (cp != NULL) { if (cp == p) usage(); if (options.user == NULL) { options.user = p; p = NULL; } *cp++ = '\0'; host = xstrdup(cp); free(p); } else host = p; break; } if (ac > 1 && !opt_terminated) { optind = optreset = 1; goto again; } ac--, av++; } /* Check that we got a host name. */ if (!host) usage(); options.host_arg = xstrdup(host); /* Initialize the command to execute on remote host. */ if ((command = sshbuf_new()) == NULL) fatal("sshbuf_new failed"); /* * Save the command to execute on the remote host in a buffer. There * is no limit on the length of the command, except by the maximum * packet size. Also sets the tty flag if there is no command. */ if (!ac) { /* No command specified - execute shell on a tty. */ if (options.session_type == SESSION_TYPE_SUBSYSTEM) { fprintf(stderr, "You must specify a subsystem to invoke.\n"); usage(); } } else { /* A command has been specified. Store it into the buffer. */ for (i = 0; i < ac; i++) { if ((r = sshbuf_putf(command, "%s%s", i ? " " : "", av[i])) != 0) fatal_fr(r, "buffer error"); } } ssh_signal(SIGPIPE, SIG_IGN); /* ignore SIGPIPE early */ /* * Initialize "log" output. Since we are the client all output * goes to stderr unless otherwise specified by -y or -E. */ if (use_syslog && logfile != NULL) fatal("Can't specify both -y and -E"); if (logfile != NULL) log_redirect_stderr_to(logfile); log_init(argv0, options.log_level == SYSLOG_LEVEL_NOT_SET ? SYSLOG_LEVEL_INFO : options.log_level, options.log_facility == SYSLOG_FACILITY_NOT_SET ? SYSLOG_FACILITY_USER : options.log_facility, !use_syslog); if (debug_flag) logit("%s, %s", SSH_RELEASE, SSH_OPENSSL_VERSION); /* Parse the configuration files */ process_config_files(options.host_arg, pw, 0, &want_final_pass); if (want_final_pass) debug("configuration requests final Match pass"); /* Hostname canonicalisation needs a few options filled. */ fill_default_options_for_canonicalization(&options); /* If the user has replaced the hostname then take it into use now */ if (options.hostname != NULL) { /* NB. Please keep in sync with readconf.c:match_cfg_line() */ cp = percent_expand(options.hostname, "h", host, (char *)NULL); free(host); host = cp; free(options.hostname); options.hostname = xstrdup(host); } /* Don't lowercase addresses, they will be explicitly canonicalised */ if ((was_addr = is_addr(host)) == 0) lowercase(host); /* * Try to canonicalize if requested by configuration or the * hostname is an address. */ if (options.canonicalize_hostname != SSH_CANONICALISE_NO || was_addr) addrs = resolve_canonicalize(&host, options.port); /* * If CanonicalizePermittedCNAMEs have been specified but * other canonicalization did not happen (by not being requested * or by failing with fallback) then the hostname may still be changed * as a result of CNAME following. * * Try to resolve the bare hostname name using the system resolver's * usual search rules and then apply the CNAME follow rules. * * Skip the lookup if a ProxyCommand is being used unless the user * has specifically requested canonicalisation for this case via * CanonicalizeHostname=always */ direct = option_clear_or_none(options.proxy_command) && option_clear_or_none(options.jump_host); if (addrs == NULL && config_has_permitted_cnames(&options) && (direct || options.canonicalize_hostname == SSH_CANONICALISE_ALWAYS)) { if ((addrs = resolve_host(host, options.port, direct, cname, sizeof(cname))) == NULL) { /* Don't fatal proxied host names not in the DNS */ if (direct) cleanup_exit(255); /* logged in resolve_host */ } else check_follow_cname(direct, &host, cname); } /* * If canonicalisation is enabled then re-parse the configuration * files as new stanzas may match. */ if (options.canonicalize_hostname != 0 && !want_final_pass) { debug("hostname canonicalisation enabled, " "will re-parse configuration"); want_final_pass = 1; } if (want_final_pass) { debug("re-parsing configuration"); free(options.hostname); options.hostname = xstrdup(host); process_config_files(options.host_arg, pw, 1, NULL); /* * Address resolution happens early with canonicalisation * enabled and the port number may have changed since, so * reset it in address list */ if (addrs != NULL && options.port > 0) set_addrinfo_port(addrs, options.port); } /* Fill configuration defaults. */ if (fill_default_options(&options) != 0) cleanup_exit(255); if (options.user == NULL) options.user = xstrdup(pw->pw_name); /* * If ProxyJump option specified, then construct a ProxyCommand now. */ if (options.jump_host != NULL) { char port_s[8]; const char *jumpuser = options.jump_user, *sshbin = argv0; int port = options.port, jumpport = options.jump_port; if (port <= 0) port = default_ssh_port(); if (jumpport <= 0) jumpport = default_ssh_port(); if (jumpuser == NULL) jumpuser = options.user; if (strcmp(options.jump_host, host) == 0 && port == jumpport && strcmp(options.user, jumpuser) == 0) fatal("jumphost loop via %s", options.jump_host); /* * Try to use SSH indicated by argv[0], but fall back to * "ssh" if it appears unavailable. */ if (strchr(argv0, '/') != NULL && access(argv0, X_OK) != 0) sshbin = "ssh"; /* Consistency check */ if (options.proxy_command != NULL) fatal("inconsistent options: ProxyCommand+ProxyJump"); /* Never use FD passing for ProxyJump */ options.proxy_use_fdpass = 0; snprintf(port_s, sizeof(port_s), "%d", options.jump_port); xasprintf(&options.proxy_command, "%s%s%s%s%s%s%s%s%s%s%.*s -W '[%%h]:%%p' %s", sshbin, /* Optional "-l user" argument if jump_user set */ options.jump_user == NULL ? "" : " -l ", options.jump_user == NULL ? "" : options.jump_user, /* Optional "-p port" argument if jump_port set */ options.jump_port <= 0 ? "" : " -p ", options.jump_port <= 0 ? "" : port_s, /* Optional additional jump hosts ",..." */ options.jump_extra == NULL ? "" : " -J ", options.jump_extra == NULL ? "" : options.jump_extra, /* Optional "-F" argument if -F specified */ config == NULL ? "" : " -F ", config == NULL ? "" : config, /* Optional "-v" arguments if -v set */ debug_flag ? " -" : "", debug_flag, "vvv", /* Mandatory hostname */ options.jump_host); debug("Setting implicit ProxyCommand from ProxyJump: %s", options.proxy_command); } if (options.port == 0) options.port = default_ssh_port(); channel_set_af(ssh, options.address_family); /* Tidy and check options */ if (options.host_key_alias != NULL) lowercase(options.host_key_alias); if (options.proxy_command != NULL && strcmp(options.proxy_command, "-") == 0 && options.proxy_use_fdpass) fatal("ProxyCommand=- and ProxyUseFDPass are incompatible"); if (options.update_hostkeys == SSH_UPDATE_HOSTKEYS_ASK) { if (options.control_persist && options.control_path != NULL) { debug("UpdateHostKeys=ask is incompatible with " "ControlPersist; disabling"); options.update_hostkeys = 0; } else if (sshbuf_len(command) != 0 || options.remote_command != NULL || options.request_tty == REQUEST_TTY_NO) { debug("UpdateHostKeys=ask is incompatible with " "remote command execution; disabling"); options.update_hostkeys = 0; } else if (options.log_level < SYSLOG_LEVEL_INFO) { /* no point logging anything; user won't see it */ options.update_hostkeys = 0; } } if (options.connection_attempts <= 0) fatal("Invalid number of ConnectionAttempts"); if (sshbuf_len(command) != 0 && options.remote_command != NULL) fatal("Cannot execute command-line and remote command."); /* Cannot fork to background if no command. */ if (options.fork_after_authentication && sshbuf_len(command) == 0 && options.remote_command == NULL && options.session_type != SESSION_TYPE_NONE) fatal("Cannot fork into background without a command " "to execute."); /* reinit */ log_init(argv0, options.log_level, options.log_facility, !use_syslog); for (j = 0; j < options.num_log_verbose; j++) { if (strcasecmp(options.log_verbose[j], "none") == 0) break; log_verbose_add(options.log_verbose[j]); } if (options.request_tty == REQUEST_TTY_YES || options.request_tty == REQUEST_TTY_FORCE) tty_flag = 1; /* Allocate a tty by default if no command specified. */ if (sshbuf_len(command) == 0 && options.remote_command == NULL) tty_flag = options.request_tty != REQUEST_TTY_NO; /* Force no tty */ if (options.request_tty == REQUEST_TTY_NO || (muxclient_command && muxclient_command != SSHMUX_COMMAND_PROXY) || options.session_type == SESSION_TYPE_NONE) tty_flag = 0; /* Do not allocate a tty if stdin is not a tty. */ if ((!isatty(fileno(stdin)) || options.stdin_null) && options.request_tty != REQUEST_TTY_FORCE) { if (tty_flag) logit("Pseudo-terminal will not be allocated because " "stdin is not a terminal."); tty_flag = 0; } /* Set up strings used to percent_expand() arguments */ cinfo = xcalloc(1, sizeof(*cinfo)); if (gethostname(thishost, sizeof(thishost)) == -1) fatal("gethostname: %s", strerror(errno)); cinfo->thishost = xstrdup(thishost); thishost[strcspn(thishost, ".")] = '\0'; cinfo->shorthost = xstrdup(thishost); xasprintf(&cinfo->portstr, "%d", options.port); xasprintf(&cinfo->uidstr, "%llu", (unsigned long long)pw->pw_uid); cinfo->keyalias = xstrdup(options.host_key_alias ? options.host_key_alias : options.host_arg); cinfo->conn_hash_hex = ssh_connection_hash(cinfo->thishost, host, cinfo->portstr, options.user); cinfo->host_arg = xstrdup(options.host_arg); cinfo->remhost = xstrdup(host); cinfo->remuser = xstrdup(options.user); cinfo->homedir = xstrdup(pw->pw_dir); cinfo->locuser = xstrdup(pw->pw_name); /* * Expand tokens in arguments. NB. LocalCommand is expanded later, * after port-forwarding is set up, so it may pick up any local * tunnel interface name allocated. */ if (options.remote_command != NULL) { debug3("expanding RemoteCommand: %s", options.remote_command); cp = options.remote_command; options.remote_command = default_client_percent_expand(cp, cinfo); debug3("expanded RemoteCommand: %s", options.remote_command); free(cp); if ((r = sshbuf_put(command, options.remote_command, strlen(options.remote_command))) != 0) fatal_fr(r, "buffer error"); } if (options.control_path != NULL) { cp = tilde_expand_filename(options.control_path, getuid()); free(options.control_path); options.control_path = default_client_percent_dollar_expand(cp, cinfo); free(cp); } if (options.identity_agent != NULL) { p = tilde_expand_filename(options.identity_agent, getuid()); cp = default_client_percent_dollar_expand(p, cinfo); free(p); free(options.identity_agent); options.identity_agent = cp; } if (options.revoked_host_keys != NULL) { p = tilde_expand_filename(options.revoked_host_keys, getuid()); cp = default_client_percent_dollar_expand(p, cinfo); free(p); free(options.revoked_host_keys); options.revoked_host_keys = cp; } if (options.forward_agent_sock_path != NULL) { p = tilde_expand_filename(options.forward_agent_sock_path, getuid()); cp = default_client_percent_dollar_expand(p, cinfo); free(p); free(options.forward_agent_sock_path); options.forward_agent_sock_path = cp; if (stat(options.forward_agent_sock_path, &st) != 0) { error("Cannot forward agent socket path \"%s\": %s", options.forward_agent_sock_path, strerror(errno)); if (options.exit_on_forward_failure) cleanup_exit(255); } } if (options.num_system_hostfiles > 0 && strcasecmp(options.system_hostfiles[0], "none") == 0) { if (options.num_system_hostfiles > 1) fatal("Invalid GlobalKnownHostsFiles: \"none\" " "appears with other entries"); free(options.system_hostfiles[0]); options.system_hostfiles[0] = NULL; options.num_system_hostfiles = 0; } if (options.num_user_hostfiles > 0 && strcasecmp(options.user_hostfiles[0], "none") == 0) { if (options.num_user_hostfiles > 1) fatal("Invalid UserKnownHostsFiles: \"none\" " "appears with other entries"); free(options.user_hostfiles[0]); options.user_hostfiles[0] = NULL; options.num_user_hostfiles = 0; } for (j = 0; j < options.num_user_hostfiles; j++) { if (options.user_hostfiles[j] == NULL) continue; cp = tilde_expand_filename(options.user_hostfiles[j], getuid()); p = default_client_percent_dollar_expand(cp, cinfo); if (strcmp(options.user_hostfiles[j], p) != 0) debug3("expanded UserKnownHostsFile '%s' -> " "'%s'", options.user_hostfiles[j], p); free(options.user_hostfiles[j]); free(cp); options.user_hostfiles[j] = p; } for (i = 0; i < options.num_local_forwards; i++) { if (options.local_forwards[i].listen_path != NULL) { cp = options.local_forwards[i].listen_path; p = options.local_forwards[i].listen_path = default_client_percent_expand(cp, cinfo); if (strcmp(cp, p) != 0) debug3("expanded LocalForward listen path " "'%s' -> '%s'", cp, p); free(cp); } if (options.local_forwards[i].connect_path != NULL) { cp = options.local_forwards[i].connect_path; p = options.local_forwards[i].connect_path = default_client_percent_expand(cp, cinfo); if (strcmp(cp, p) != 0) debug3("expanded LocalForward connect path " "'%s' -> '%s'", cp, p); free(cp); } } for (i = 0; i < options.num_remote_forwards; i++) { if (options.remote_forwards[i].listen_path != NULL) { cp = options.remote_forwards[i].listen_path; p = options.remote_forwards[i].listen_path = default_client_percent_expand(cp, cinfo); if (strcmp(cp, p) != 0) debug3("expanded RemoteForward listen path " "'%s' -> '%s'", cp, p); free(cp); } if (options.remote_forwards[i].connect_path != NULL) { cp = options.remote_forwards[i].connect_path; p = options.remote_forwards[i].connect_path = default_client_percent_expand(cp, cinfo); if (strcmp(cp, p) != 0) debug3("expanded RemoteForward connect path " "'%s' -> '%s'", cp, p); free(cp); } } if (config_test) { dump_client_config(&options, host); exit(0); } /* Expand SecurityKeyProvider if it refers to an environment variable */ if (options.sk_provider != NULL && *options.sk_provider == '$' && strlen(options.sk_provider) > 1) { if ((cp = getenv(options.sk_provider + 1)) == NULL) { debug("Authenticator provider %s did not resolve; " "disabling", options.sk_provider); free(options.sk_provider); options.sk_provider = NULL; } else { debug2("resolved SecurityKeyProvider %s => %s", options.sk_provider, cp); free(options.sk_provider); options.sk_provider = xstrdup(cp); } } if (muxclient_command != 0 && options.control_path == NULL) fatal("No ControlPath specified for \"-O\" command"); if (options.control_path != NULL) { int sock; if ((sock = muxclient(options.control_path)) >= 0) { ssh_packet_set_connection(ssh, sock, sock); ssh_packet_set_mux(ssh); goto skip_connect; } } /* * If hostname canonicalisation was not enabled, then we may not * have yet resolved the hostname. Do so now. */ if (addrs == NULL && options.proxy_command == NULL) { debug2("resolving \"%s\" port %d", host, options.port); if ((addrs = resolve_host(host, options.port, 1, cname, sizeof(cname))) == NULL) cleanup_exit(255); /* resolve_host logs the error */ } if (options.connection_timeout >= INT_MAX/1000) timeout_ms = INT_MAX; else timeout_ms = options.connection_timeout * 1000; /* Open a connection to the remote host. */ if (ssh_connect(ssh, host, options.host_arg, addrs, &hostaddr, options.port, options.connection_attempts, &timeout_ms, options.tcp_keep_alive) != 0) exit(255); if (addrs != NULL) freeaddrinfo(addrs); ssh_packet_set_timeout(ssh, options.server_alive_interval, options.server_alive_count_max); if (timeout_ms > 0) debug3("timeout: %d ms remain after connect", timeout_ms); /* * If we successfully made the connection and we have hostbased auth * enabled, load the public keys so we can later use the ssh-keysign * helper to sign challenges. */ sensitive_data.nkeys = 0; sensitive_data.keys = NULL; if (options.hostbased_authentication) { int loaded = 0; sensitive_data.nkeys = 10; sensitive_data.keys = xcalloc(sensitive_data.nkeys, sizeof(*sensitive_data.keys)); /* XXX check errors? */ #define L_PUBKEY(p,o) do { \ if ((o) >= sensitive_data.nkeys) \ fatal_f("pubkey out of array bounds"); \ check_load(sshkey_load_public(p, &(sensitive_data.keys[o]), NULL), \ &(sensitive_data.keys[o]), p, "pubkey"); \ if (sensitive_data.keys[o] != NULL) { \ debug2("hostbased key %d: %s key from \"%s\"", o, \ sshkey_ssh_name(sensitive_data.keys[o]), p); \ loaded++; \ } \ } while (0) #define L_CERT(p,o) do { \ if ((o) >= sensitive_data.nkeys) \ fatal_f("cert out of array bounds"); \ check_load(sshkey_load_cert(p, &(sensitive_data.keys[o])), \ &(sensitive_data.keys[o]), p, "cert"); \ if (sensitive_data.keys[o] != NULL) { \ debug2("hostbased key %d: %s cert from \"%s\"", o, \ sshkey_ssh_name(sensitive_data.keys[o]), p); \ loaded++; \ } \ } while (0) if (options.hostbased_authentication == 1) { L_CERT(_PATH_HOST_ECDSA_KEY_FILE, 0); L_CERT(_PATH_HOST_ED25519_KEY_FILE, 1); L_CERT(_PATH_HOST_RSA_KEY_FILE, 2); L_CERT(_PATH_HOST_DSA_KEY_FILE, 3); L_PUBKEY(_PATH_HOST_ECDSA_KEY_FILE, 4); L_PUBKEY(_PATH_HOST_ED25519_KEY_FILE, 5); L_PUBKEY(_PATH_HOST_RSA_KEY_FILE, 6); L_PUBKEY(_PATH_HOST_DSA_KEY_FILE, 7); L_CERT(_PATH_HOST_XMSS_KEY_FILE, 8); L_PUBKEY(_PATH_HOST_XMSS_KEY_FILE, 9); if (loaded == 0) debug("HostbasedAuthentication enabled but no " "local public host keys could be loaded."); } } /* load options.identity_files */ load_public_identity_files(cinfo); /* optionally set the SSH_AUTHSOCKET_ENV_NAME variable */ if (options.identity_agent && strcmp(options.identity_agent, SSH_AUTHSOCKET_ENV_NAME) != 0) { if (strcmp(options.identity_agent, "none") == 0) { unsetenv(SSH_AUTHSOCKET_ENV_NAME); } else { cp = options.identity_agent; /* legacy (limited) format */ if (cp[0] == '$' && cp[1] != '{') { if (!valid_env_name(cp + 1)) { fatal("Invalid IdentityAgent " "environment variable name %s", cp); } if ((p = getenv(cp + 1)) == NULL) unsetenv(SSH_AUTHSOCKET_ENV_NAME); else setenv(SSH_AUTHSOCKET_ENV_NAME, p, 1); } else { /* identity_agent specifies a path directly */ setenv(SSH_AUTHSOCKET_ENV_NAME, cp, 1); } } } if (options.forward_agent && options.forward_agent_sock_path != NULL) { cp = options.forward_agent_sock_path; if (cp[0] == '$') { if (!valid_env_name(cp + 1)) { fatal("Invalid ForwardAgent environment variable name %s", cp); } if ((p = getenv(cp + 1)) != NULL) forward_agent_sock_path = xstrdup(p); else options.forward_agent = 0; free(cp); } else { forward_agent_sock_path = cp; } } /* Expand ~ in known host file names. */ tilde_expand_paths(options.system_hostfiles, options.num_system_hostfiles); tilde_expand_paths(options.user_hostfiles, options.num_user_hostfiles); ssh_signal(SIGCHLD, main_sigchld_handler); /* Log into the remote system. Never returns if the login fails. */ ssh_login(ssh, &sensitive_data, host, (struct sockaddr *)&hostaddr, options.port, pw, timeout_ms, cinfo); /* We no longer need the private host keys. Clear them now. */ if (sensitive_data.nkeys != 0) { for (i = 0; i < sensitive_data.nkeys; i++) { if (sensitive_data.keys[i] != NULL) { /* Destroys contents safely */ debug3("clear hostkey %d", i); sshkey_free(sensitive_data.keys[i]); sensitive_data.keys[i] = NULL; } } free(sensitive_data.keys); } for (i = 0; i < options.num_identity_files; i++) { free(options.identity_files[i]); options.identity_files[i] = NULL; if (options.identity_keys[i]) { sshkey_free(options.identity_keys[i]); options.identity_keys[i] = NULL; } } for (i = 0; i < options.num_certificate_files; i++) { free(options.certificate_files[i]); options.certificate_files[i] = NULL; } #ifdef ENABLE_PKCS11 (void)pkcs11_del_provider(options.pkcs11_provider); #endif skip_connect: exit_status = ssh_session2(ssh, cinfo); ssh_conn_info_free(cinfo); ssh_packet_close(ssh); if (options.control_path != NULL && muxserver_sock != -1) unlink(options.control_path); /* Kill ProxyCommand if it is running. */ ssh_kill_proxy_command(); return exit_status; } static void control_persist_detach(void) { pid_t pid; debug_f("backgrounding master process"); /* * master (current process) into the background, and make the * foreground process a client of the backgrounded master. */ switch ((pid = fork())) { case -1: fatal_f("fork: %s", strerror(errno)); case 0: /* Child: master process continues mainloop */ break; default: /* * Parent: set up mux client to connect to backgrounded * master. */ debug2_f("background process is %ld", (long)pid); options.stdin_null = ostdin_null_flag; options.request_tty = orequest_tty; tty_flag = otty_flag; options.fork_after_authentication = ofork_after_authentication; options.session_type = osession_type; close(muxserver_sock); muxserver_sock = -1; options.control_master = SSHCTL_MASTER_NO; (void)muxclient(options.control_path); /* muxclient() doesn't return on success. */ fatal("Failed to connect to new control master"); } if (stdfd_devnull(1, 1, !(log_is_on_stderr() && debug_flag)) == -1) error_f("stdfd_devnull failed"); daemon(1, 1); setproctitle("%s [mux]", options.control_path); } /* Do fork() after authentication. Used by "ssh -f" */ static void fork_postauth(void) { if (need_controlpersist_detach) control_persist_detach(); debug("forking to background"); options.fork_after_authentication = 0; if (daemon(1, 1) == -1) fatal("daemon() failed: %.200s", strerror(errno)); if (stdfd_devnull(1, 1, !(log_is_on_stderr() && debug_flag)) == -1) error_f("stdfd_devnull failed"); } static void forwarding_success(void) { if (forward_confirms_pending == -1) return; if (--forward_confirms_pending == 0) { debug_f("all expected forwarding replies received"); if (options.fork_after_authentication) fork_postauth(); } else { debug2_f("%d expected forwarding replies remaining", forward_confirms_pending); } } /* Callback for remote forward global requests */ static void ssh_confirm_remote_forward(struct ssh *ssh, int type, u_int32_t seq, void *ctxt) { struct Forward *rfwd = (struct Forward *)ctxt; u_int port; int r; /* XXX verbose() on failure? */ debug("remote forward %s for: listen %s%s%d, connect %s:%d", type == SSH2_MSG_REQUEST_SUCCESS ? "success" : "failure", rfwd->listen_path ? rfwd->listen_path : rfwd->listen_host ? rfwd->listen_host : "", (rfwd->listen_path || rfwd->listen_host) ? ":" : "", rfwd->listen_port, rfwd->connect_path ? rfwd->connect_path : rfwd->connect_host, rfwd->connect_port); if (rfwd->listen_path == NULL && rfwd->listen_port == 0) { if (type == SSH2_MSG_REQUEST_SUCCESS) { if ((r = sshpkt_get_u32(ssh, &port)) != 0) fatal_fr(r, "parse packet"); if (port > 65535) { error("Invalid allocated port %u for remote " "forward to %s:%d", port, rfwd->connect_host, rfwd->connect_port); /* Ensure failure processing runs below */ type = SSH2_MSG_REQUEST_FAILURE; channel_update_permission(ssh, rfwd->handle, -1); } else { rfwd->allocated_port = (int)port; logit("Allocated port %u for remote " "forward to %s:%d", rfwd->allocated_port, rfwd->connect_path ? rfwd->connect_path : rfwd->connect_host, rfwd->connect_port); channel_update_permission(ssh, rfwd->handle, rfwd->allocated_port); } } else { channel_update_permission(ssh, rfwd->handle, -1); } } if (type == SSH2_MSG_REQUEST_FAILURE) { if (options.exit_on_forward_failure) { if (rfwd->listen_path != NULL) fatal("Error: remote port forwarding failed " "for listen path %s", rfwd->listen_path); else fatal("Error: remote port forwarding failed " "for listen port %d", rfwd->listen_port); } else { if (rfwd->listen_path != NULL) logit("Warning: remote port forwarding failed " "for listen path %s", rfwd->listen_path); else logit("Warning: remote port forwarding failed " "for listen port %d", rfwd->listen_port); } } forwarding_success(); } static void client_cleanup_stdio_fwd(struct ssh *ssh, int id, int force, void *arg) { debug("stdio forwarding: done"); cleanup_exit(0); } static void ssh_stdio_confirm(struct ssh *ssh, int id, int success, void *arg) { if (!success) fatal("stdio forwarding failed"); } static void ssh_tun_confirm(struct ssh *ssh, int id, int success, void *arg) { if (!success) { error("Tunnel forwarding failed"); if (options.exit_on_forward_failure) cleanup_exit(255); } debug_f("tunnel forward established, id=%d", id); forwarding_success(); } static void ssh_init_stdio_forwarding(struct ssh *ssh) { Channel *c; int in, out; if (options.stdio_forward_host == NULL) return; debug3_f("%s:%d", options.stdio_forward_host, options.stdio_forward_port); if ((in = dup(STDIN_FILENO)) == -1 || (out = dup(STDOUT_FILENO)) == -1) fatal_f("dup() in/out failed"); if ((c = channel_connect_stdio_fwd(ssh, options.stdio_forward_host, options.stdio_forward_port, in, out, CHANNEL_NONBLOCK_STDIO)) == NULL) fatal_f("channel_connect_stdio_fwd failed"); channel_register_cleanup(ssh, c->self, client_cleanup_stdio_fwd, 0); channel_register_open_confirm(ssh, c->self, ssh_stdio_confirm, NULL); } static void ssh_init_forward_permissions(struct ssh *ssh, const char *what, char **opens, u_int num_opens) { u_int i; int port; char *addr, *arg, *oarg; int where = FORWARD_LOCAL; channel_clear_permission(ssh, FORWARD_ADM, where); if (num_opens == 0) return; /* permit any */ /* handle keywords: "any" / "none" */ if (num_opens == 1 && strcmp(opens[0], "any") == 0) return; if (num_opens == 1 && strcmp(opens[0], "none") == 0) { channel_disable_admin(ssh, where); return; } /* Otherwise treat it as a list of permitted host:port */ for (i = 0; i < num_opens; i++) { oarg = arg = xstrdup(opens[i]); addr = hpdelim(&arg); if (addr == NULL) fatal_f("missing host in %s", what); addr = cleanhostname(addr); if (arg == NULL || ((port = permitopen_port(arg)) < 0)) fatal_f("bad port number in %s", what); /* Send it to channels layer */ channel_add_permission(ssh, FORWARD_ADM, where, addr, port); free(oarg); } } static void ssh_init_forwarding(struct ssh *ssh, char **ifname) { int success = 0; int i; ssh_init_forward_permissions(ssh, "permitremoteopen", options.permitted_remote_opens, options.num_permitted_remote_opens); if (options.exit_on_forward_failure) forward_confirms_pending = 0; /* track pending requests */ /* Initiate local TCP/IP port forwardings. */ for (i = 0; i < options.num_local_forwards; i++) { debug("Local connections to %.200s:%d forwarded to remote " "address %.200s:%d", (options.local_forwards[i].listen_path != NULL) ? options.local_forwards[i].listen_path : (options.local_forwards[i].listen_host == NULL) ? (options.fwd_opts.gateway_ports ? "*" : "LOCALHOST") : options.local_forwards[i].listen_host, options.local_forwards[i].listen_port, (options.local_forwards[i].connect_path != NULL) ? options.local_forwards[i].connect_path : options.local_forwards[i].connect_host, options.local_forwards[i].connect_port); success += channel_setup_local_fwd_listener(ssh, &options.local_forwards[i], &options.fwd_opts); } if (i > 0 && success != i && options.exit_on_forward_failure) fatal("Could not request local forwarding."); if (i > 0 && success == 0) error("Could not request local forwarding."); /* Initiate remote TCP/IP port forwardings. */ for (i = 0; i < options.num_remote_forwards; i++) { debug("Remote connections from %.200s:%d forwarded to " "local address %.200s:%d", (options.remote_forwards[i].listen_path != NULL) ? options.remote_forwards[i].listen_path : (options.remote_forwards[i].listen_host == NULL) ? "LOCALHOST" : options.remote_forwards[i].listen_host, options.remote_forwards[i].listen_port, (options.remote_forwards[i].connect_path != NULL) ? options.remote_forwards[i].connect_path : options.remote_forwards[i].connect_host, options.remote_forwards[i].connect_port); if ((options.remote_forwards[i].handle = channel_request_remote_forwarding(ssh, &options.remote_forwards[i])) >= 0) { client_register_global_confirm( ssh_confirm_remote_forward, &options.remote_forwards[i]); forward_confirms_pending++; } else if (options.exit_on_forward_failure) fatal("Could not request remote forwarding."); else logit("Warning: Could not request remote forwarding."); } /* Initiate tunnel forwarding. */ if (options.tun_open != SSH_TUNMODE_NO) { if ((*ifname = client_request_tun_fwd(ssh, options.tun_open, options.tun_local, options.tun_remote, ssh_tun_confirm, NULL)) != NULL) forward_confirms_pending++; else if (options.exit_on_forward_failure) fatal("Could not request tunnel forwarding."); else error("Could not request tunnel forwarding."); } if (forward_confirms_pending > 0) { debug_f("expecting replies for %d forwards", forward_confirms_pending); } } static void check_agent_present(void) { int r; if (options.forward_agent) { /* Clear agent forwarding if we don't have an agent. */ if ((r = ssh_get_authentication_socket(NULL)) != 0) { options.forward_agent = 0; if (r != SSH_ERR_AGENT_NOT_PRESENT) debug_r(r, "ssh_get_authentication_socket"); } } } static void ssh_session2_setup(struct ssh *ssh, int id, int success, void *arg) { extern char **environ; const char *display, *term; int r, interactive = tty_flag; char *proto = NULL, *data = NULL; if (!success) return; /* No need for error message, channels code sends one */ display = getenv("DISPLAY"); if (display == NULL && options.forward_x11) debug("X11 forwarding requested but DISPLAY not set"); if (options.forward_x11 && client_x11_get_proto(ssh, display, options.xauth_location, options.forward_x11_trusted, options.forward_x11_timeout, &proto, &data) == 0) { /* Request forwarding with authentication spoofing. */ debug("Requesting X11 forwarding with authentication " "spoofing."); x11_request_forwarding_with_spoofing(ssh, id, display, proto, data, 1); client_expect_confirm(ssh, id, "X11 forwarding", CONFIRM_WARN); /* XXX exit_on_forward_failure */ interactive = 1; } check_agent_present(); if (options.forward_agent) { debug("Requesting authentication agent forwarding."); channel_request_start(ssh, id, "auth-agent-req@openssh.com", 0); if ((r = sshpkt_send(ssh)) != 0) fatal_fr(r, "send packet"); } /* Tell the packet module whether this is an interactive session. */ ssh_packet_set_interactive(ssh, interactive, options.ip_qos_interactive, options.ip_qos_bulk); if ((term = lookup_env_in_list("TERM", options.setenv, options.num_setenv)) == NULL || *term == '\0') term = getenv("TERM"); client_session2_setup(ssh, id, tty_flag, options.session_type == SESSION_TYPE_SUBSYSTEM, term, NULL, fileno(stdin), command, environ); } /* open new channel for a session */ static int ssh_session2_open(struct ssh *ssh) { Channel *c; int window, packetmax, in, out, err; if (options.stdin_null) { in = open(_PATH_DEVNULL, O_RDONLY); } else { in = dup(STDIN_FILENO); } out = dup(STDOUT_FILENO); err = dup(STDERR_FILENO); if (in == -1 || out == -1 || err == -1) fatal("dup() in/out/err failed"); window = CHAN_SES_WINDOW_DEFAULT; packetmax = CHAN_SES_PACKET_DEFAULT; if (tty_flag) { window >>= 1; packetmax >>= 1; } c = channel_new(ssh, "session", SSH_CHANNEL_OPENING, in, out, err, window, packetmax, CHAN_EXTENDED_WRITE, "client-session", CHANNEL_NONBLOCK_STDIO); debug3_f("channel_new: %d", c->self); channel_send_open(ssh, c->self); if (options.session_type != SESSION_TYPE_NONE) channel_register_open_confirm(ssh, c->self, ssh_session2_setup, NULL); return c->self; } static int ssh_session2(struct ssh *ssh, const struct ssh_conn_info *cinfo) { - int r, id = -1; + int r, interactive, id = -1; char *cp, *tun_fwd_ifname = NULL; /* XXX should be pre-session */ if (!options.control_persist) ssh_init_stdio_forwarding(ssh); ssh_init_forwarding(ssh, &tun_fwd_ifname); if (options.local_command != NULL) { debug3("expanding LocalCommand: %s", options.local_command); cp = options.local_command; options.local_command = percent_expand(cp, DEFAULT_CLIENT_PERCENT_EXPAND_ARGS(cinfo), "T", tun_fwd_ifname == NULL ? "NONE" : tun_fwd_ifname, (char *)NULL); debug3("expanded LocalCommand: %s", options.local_command); free(cp); } /* Start listening for multiplex clients */ if (!ssh_packet_get_mux(ssh)) muxserver_listen(ssh); /* * If we are in control persist mode and have a working mux listen * socket, then prepare to background ourselves and have a foreground * client attach as a control client. * NB. we must save copies of the flags that we override for * the backgrounding, since we defer attachment of the client until * after the connection is fully established (in particular, * async rfwd replies have been received for ExitOnForwardFailure). */ if (options.control_persist && muxserver_sock != -1) { ostdin_null_flag = options.stdin_null; osession_type = options.session_type; orequest_tty = options.request_tty; otty_flag = tty_flag; ofork_after_authentication = options.fork_after_authentication; options.stdin_null = 1; options.session_type = SESSION_TYPE_NONE; tty_flag = 0; if ((osession_type != SESSION_TYPE_NONE || options.stdio_forward_host != NULL)) need_controlpersist_detach = 1; options.fork_after_authentication = 1; } /* * ControlPersist mux listen socket setup failed, attempt the * stdio forward setup that we skipped earlier. */ if (options.control_persist && muxserver_sock == -1) ssh_init_stdio_forwarding(ssh); if (options.session_type != SESSION_TYPE_NONE) id = ssh_session2_open(ssh); else { - ssh_packet_set_interactive(ssh, - options.control_master == SSHCTL_MASTER_NO, + interactive = options.control_master == SSHCTL_MASTER_NO; + /* ControlPersist may have clobbered ControlMaster, so check */ + if (need_controlpersist_detach) + interactive = otty_flag != 0; + ssh_packet_set_interactive(ssh, interactive, options.ip_qos_interactive, options.ip_qos_bulk); } /* If we don't expect to open a new session, then disallow it */ if (options.control_master == SSHCTL_MASTER_NO && (ssh->compat & SSH_NEW_OPENSSH)) { debug("Requesting no-more-sessions@openssh.com"); if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || (r = sshpkt_put_cstring(ssh, "no-more-sessions@openssh.com")) != 0 || (r = sshpkt_put_u8(ssh, 0)) != 0 || (r = sshpkt_send(ssh)) != 0) fatal_fr(r, "send packet"); } /* Execute a local command */ if (options.local_command != NULL && options.permit_local_command) ssh_local_cmd(options.local_command); /* * stdout is now owned by the session channel; clobber it here * so future channel closes are propagated to the local fd. * NB. this can only happen after LocalCommand has completed, * as it may want to write to stdout. */ if (!need_controlpersist_detach && stdfd_devnull(0, 1, 0) == -1) error_f("stdfd_devnull failed"); /* * If requested and we are not interested in replies to remote * forwarding requests, then let ssh continue in the background. */ if (options.fork_after_authentication) { if (options.exit_on_forward_failure && options.num_remote_forwards > 0) { debug("deferring postauth fork until remote forward " "confirmation received"); } else fork_postauth(); } return client_loop(ssh, tty_flag, tty_flag ? options.escape_char : SSH_ESCAPECHAR_NONE, id); } /* Loads all IdentityFile and CertificateFile keys */ static void load_public_identity_files(const struct ssh_conn_info *cinfo) { char *filename, *cp; struct sshkey *public; int i; u_int n_ids, n_certs; char *identity_files[SSH_MAX_IDENTITY_FILES]; struct sshkey *identity_keys[SSH_MAX_IDENTITY_FILES]; int identity_file_userprovided[SSH_MAX_IDENTITY_FILES]; char *certificate_files[SSH_MAX_CERTIFICATE_FILES]; struct sshkey *certificates[SSH_MAX_CERTIFICATE_FILES]; int certificate_file_userprovided[SSH_MAX_CERTIFICATE_FILES]; #ifdef ENABLE_PKCS11 struct sshkey **keys = NULL; char **comments = NULL; int nkeys; #endif /* PKCS11 */ n_ids = n_certs = 0; memset(identity_files, 0, sizeof(identity_files)); memset(identity_keys, 0, sizeof(identity_keys)); memset(identity_file_userprovided, 0, sizeof(identity_file_userprovided)); memset(certificate_files, 0, sizeof(certificate_files)); memset(certificates, 0, sizeof(certificates)); memset(certificate_file_userprovided, 0, sizeof(certificate_file_userprovided)); #ifdef ENABLE_PKCS11 if (options.pkcs11_provider != NULL && options.num_identity_files < SSH_MAX_IDENTITY_FILES && (pkcs11_init(!options.batch_mode) == 0) && (nkeys = pkcs11_add_provider(options.pkcs11_provider, NULL, &keys, &comments)) > 0) { for (i = 0; i < nkeys; i++) { if (n_ids >= SSH_MAX_IDENTITY_FILES) { sshkey_free(keys[i]); free(comments[i]); continue; } identity_keys[n_ids] = keys[i]; identity_files[n_ids] = comments[i]; /* transferred */ n_ids++; } free(keys); free(comments); } #endif /* ENABLE_PKCS11 */ for (i = 0; i < options.num_identity_files; i++) { if (n_ids >= SSH_MAX_IDENTITY_FILES || strcasecmp(options.identity_files[i], "none") == 0) { free(options.identity_files[i]); options.identity_files[i] = NULL; continue; } cp = tilde_expand_filename(options.identity_files[i], getuid()); filename = default_client_percent_dollar_expand(cp, cinfo); free(cp); check_load(sshkey_load_public(filename, &public, NULL), &public, filename, "pubkey"); debug("identity file %s type %d", filename, public ? public->type : -1); free(options.identity_files[i]); identity_files[n_ids] = filename; identity_keys[n_ids] = public; identity_file_userprovided[n_ids] = options.identity_file_userprovided[i]; if (++n_ids >= SSH_MAX_IDENTITY_FILES) continue; /* * If no certificates have been explicitly listed then try * to add the default certificate variant too. */ if (options.num_certificate_files != 0) continue; xasprintf(&cp, "%s-cert", filename); check_load(sshkey_load_public(cp, &public, NULL), &public, filename, "pubkey"); debug("identity file %s type %d", cp, public ? public->type : -1); if (public == NULL) { free(cp); continue; } if (!sshkey_is_cert(public)) { debug_f("key %s type %s is not a certificate", cp, sshkey_type(public)); sshkey_free(public); free(cp); continue; } /* NB. leave filename pointing to private key */ identity_files[n_ids] = xstrdup(filename); identity_keys[n_ids] = public; identity_file_userprovided[n_ids] = options.identity_file_userprovided[i]; n_ids++; } if (options.num_certificate_files > SSH_MAX_CERTIFICATE_FILES) fatal_f("too many certificates"); for (i = 0; i < options.num_certificate_files; i++) { cp = tilde_expand_filename(options.certificate_files[i], getuid()); filename = default_client_percent_dollar_expand(cp, cinfo); free(cp); check_load(sshkey_load_public(filename, &public, NULL), &public, filename, "certificate"); debug("certificate file %s type %d", filename, public ? public->type : -1); free(options.certificate_files[i]); options.certificate_files[i] = NULL; if (public == NULL) { free(filename); continue; } if (!sshkey_is_cert(public)) { debug_f("key %s type %s is not a certificate", filename, sshkey_type(public)); sshkey_free(public); free(filename); continue; } certificate_files[n_certs] = filename; certificates[n_certs] = public; certificate_file_userprovided[n_certs] = options.certificate_file_userprovided[i]; ++n_certs; } options.num_identity_files = n_ids; memcpy(options.identity_files, identity_files, sizeof(identity_files)); memcpy(options.identity_keys, identity_keys, sizeof(identity_keys)); memcpy(options.identity_file_userprovided, identity_file_userprovided, sizeof(identity_file_userprovided)); options.num_certificate_files = n_certs; memcpy(options.certificate_files, certificate_files, sizeof(certificate_files)); memcpy(options.certificates, certificates, sizeof(certificates)); memcpy(options.certificate_file_userprovided, certificate_file_userprovided, sizeof(certificate_file_userprovided)); } static void main_sigchld_handler(int sig) { int save_errno = errno; pid_t pid; int status; while ((pid = waitpid(-1, &status, WNOHANG)) > 0 || (pid == -1 && errno == EINTR)) ; errno = save_errno; } diff --git a/ssh2.h b/ssh2.h index 32ddae89b33b..0d48d052782f 100644 --- a/ssh2.h +++ b/ssh2.h @@ -1,174 +1,179 @@ -/* $OpenBSD: ssh2.h,v 1.19 2020/11/19 23:05:05 dtucker Exp $ */ +/* $OpenBSD: ssh2.h,v 1.21 2023/08/28 03:28:43 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * RFC4251: * * Transport layer protocol: * * 1-19 Transport layer generic (e.g. disconnect, ignore, debug, * etc) * 20-29 Algorithm negotiation * 30-49 Key exchange method specific (numbers can be reused for * different authentication methods) * * User authentication protocol: * * 50-59 User authentication generic * 60-79 User authentication method specific (numbers can be reused * for different authentication methods) * * Connection protocol: * * 80-89 Connection protocol generic * 90-127 Channel related messages * * Reserved for client protocols: * * 128-191 Reserved * * Local extensions: * * 192-255 Local extensions */ /* special marker for no message */ #define SSH_MSG_NONE 0 /* ranges */ #define SSH2_MSG_TRANSPORT_MIN 1 #define SSH2_MSG_TRANSPORT_MAX 49 #define SSH2_MSG_USERAUTH_MIN 50 #define SSH2_MSG_USERAUTH_MAX 79 #define SSH2_MSG_USERAUTH_PER_METHOD_MIN 60 #define SSH2_MSG_USERAUTH_PER_METHOD_MAX SSH2_MSG_USERAUTH_MAX #define SSH2_MSG_CONNECTION_MIN 80 #define SSH2_MSG_CONNECTION_MAX 127 #define SSH2_MSG_RESERVED_MIN 128 #define SSH2_MSG_RESERVED_MAX 191 #define SSH2_MSG_LOCAL_MIN 192 #define SSH2_MSG_LOCAL_MAX 255 #define SSH2_MSG_MIN 1 #define SSH2_MSG_MAX 255 /* transport layer: generic */ #define SSH2_MSG_DISCONNECT 1 #define SSH2_MSG_IGNORE 2 #define SSH2_MSG_UNIMPLEMENTED 3 #define SSH2_MSG_DEBUG 4 #define SSH2_MSG_SERVICE_REQUEST 5 #define SSH2_MSG_SERVICE_ACCEPT 6 #define SSH2_MSG_EXT_INFO 7 +#define SSH2_MSG_NEWCOMPRESS 8 /* transport layer: alg negotiation */ #define SSH2_MSG_KEXINIT 20 #define SSH2_MSG_NEWKEYS 21 /* transport layer: kex specific messages, can be reused */ #define SSH2_MSG_KEXDH_INIT 30 #define SSH2_MSG_KEXDH_REPLY 31 /* dh-group-exchange */ #define SSH2_MSG_KEX_DH_GEX_REQUEST_OLD 30 #define SSH2_MSG_KEX_DH_GEX_GROUP 31 #define SSH2_MSG_KEX_DH_GEX_INIT 32 #define SSH2_MSG_KEX_DH_GEX_REPLY 33 #define SSH2_MSG_KEX_DH_GEX_REQUEST 34 /* ecdh */ #define SSH2_MSG_KEX_ECDH_INIT 30 #define SSH2_MSG_KEX_ECDH_REPLY 31 +/* transport layer: OpenSSH extensions */ +#define SSH2_MSG_PING 192 +#define SSH2_MSG_PONG 193 + /* user authentication: generic */ #define SSH2_MSG_USERAUTH_REQUEST 50 #define SSH2_MSG_USERAUTH_FAILURE 51 #define SSH2_MSG_USERAUTH_SUCCESS 52 #define SSH2_MSG_USERAUTH_BANNER 53 /* user authentication: method specific, can be reused */ #define SSH2_MSG_USERAUTH_PK_OK 60 #define SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ 60 #define SSH2_MSG_USERAUTH_INFO_REQUEST 60 #define SSH2_MSG_USERAUTH_INFO_RESPONSE 61 /* connection protocol: generic */ #define SSH2_MSG_GLOBAL_REQUEST 80 #define SSH2_MSG_REQUEST_SUCCESS 81 #define SSH2_MSG_REQUEST_FAILURE 82 /* channel related messages */ #define SSH2_MSG_CHANNEL_OPEN 90 #define SSH2_MSG_CHANNEL_OPEN_CONFIRMATION 91 #define SSH2_MSG_CHANNEL_OPEN_FAILURE 92 #define SSH2_MSG_CHANNEL_WINDOW_ADJUST 93 #define SSH2_MSG_CHANNEL_DATA 94 #define SSH2_MSG_CHANNEL_EXTENDED_DATA 95 #define SSH2_MSG_CHANNEL_EOF 96 #define SSH2_MSG_CHANNEL_CLOSE 97 #define SSH2_MSG_CHANNEL_REQUEST 98 #define SSH2_MSG_CHANNEL_SUCCESS 99 #define SSH2_MSG_CHANNEL_FAILURE 100 /* disconnect reason code */ #define SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT 1 #define SSH2_DISCONNECT_PROTOCOL_ERROR 2 #define SSH2_DISCONNECT_KEY_EXCHANGE_FAILED 3 #define SSH2_DISCONNECT_HOST_AUTHENTICATION_FAILED 4 #define SSH2_DISCONNECT_RESERVED 4 #define SSH2_DISCONNECT_MAC_ERROR 5 #define SSH2_DISCONNECT_COMPRESSION_ERROR 6 #define SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE 7 #define SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED 8 #define SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE 9 #define SSH2_DISCONNECT_CONNECTION_LOST 10 #define SSH2_DISCONNECT_BY_APPLICATION 11 #define SSH2_DISCONNECT_TOO_MANY_CONNECTIONS 12 #define SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER 13 #define SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE 14 #define SSH2_DISCONNECT_ILLEGAL_USER_NAME 15 /* misc */ #define SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED 1 #define SSH2_OPEN_CONNECT_FAILED 2 #define SSH2_OPEN_UNKNOWN_CHANNEL_TYPE 3 #define SSH2_OPEN_RESOURCE_SHORTAGE 4 #define SSH2_EXTENDED_DATA_STDERR 1 /* Certificate types for OpenSSH certificate keys extension */ #define SSH2_CERT_TYPE_USER 1 #define SSH2_CERT_TYPE_HOST 2 diff --git a/ssh_config.0 b/ssh_config.0 index 1b2a0e5d6d24..aec4901e946c 100644 --- a/ssh_config.0 +++ b/ssh_config.0 @@ -1,1344 +1,1363 @@ SSH_CONFIG(5) File Formats Manual SSH_CONFIG(5) NAME ssh_config M-bM-^@M-^S OpenSSH client configuration file DESCRIPTION ssh(1) obtains configuration data from the following sources in the following order: 1. command-line options 2. user's configuration file (~/.ssh/config) 3. system-wide configuration file (/etc/ssh/ssh_config) Unless noted otherwise, for each parameter, the first obtained value will be used. The configuration files contain sections separated by Host specifications, and that section is only applied for hosts that match one of the patterns given in the specification. The matched host name is usually the one given on the command line (see the CanonicalizeHostname option for exceptions). Since the first obtained value for each parameter is used, more host- specific declarations should be given near the beginning of the file, and general defaults at the end. The file contains keyword-argument pairs, one per line. Lines starting with M-bM-^@M-^X#M-bM-^@M-^Y and empty lines are interpreted as comments. Arguments may optionally be enclosed in double quotes (") in order to represent arguments containing spaces. Configuration options may be separated by whitespace or optional whitespace and exactly one M-bM-^@M-^X=M-bM-^@M-^Y; the latter format is useful to avoid the need to quote whitespace when specifying configuration options using the ssh, scp, and sftp -o option. The possible keywords and their meanings are as follows (note that keywords are case-insensitive and arguments are case-sensitive): Host Restricts the following declarations (up to the next Host or Match keyword) to be only for those hosts that match one of the patterns given after the keyword. If more than one pattern is provided, they should be separated by whitespace. A single M-bM-^@M-^X*M-bM-^@M-^Y as a pattern can be used to provide global defaults for all hosts. The host is usually the hostname argument given on the command line (see the CanonicalizeHostname keyword for exceptions). A pattern entry may be negated by prefixing it with an exclamation mark (M-bM-^@M-^X!M-bM-^@M-^Y). If a negated entry is matched, then the Host entry is ignored, regardless of whether any other patterns on the line match. Negated matches are therefore useful to provide exceptions for wildcard matches. See PATTERNS for more information on patterns. Match Restricts the following declarations (up to the next Host or Match keyword) to be used only when the conditions following the Match keyword are satisfied. Match conditions are specified using one or more criteria or the single token all which always matches. The available criteria keywords are: canonical, final, exec, localnetwork, host, originalhost, Tag, user, and localuser. The all criteria must appear alone or immediately after canonical or final. Other criteria may be combined arbitrarily. All criteria but all, canonical, and final require an argument. Criteria may be negated by prepending an exclamation mark (M-bM-^@M-^X!M-bM-^@M-^Y). The canonical keyword matches only when the configuration file is being re-parsed after hostname canonicalization (see the CanonicalizeHostname option). This may be useful to specify conditions that work with canonical host names only. The final keyword requests that the configuration be re-parsed (regardless of whether CanonicalizeHostname is enabled), and matches only during this final pass. If CanonicalizeHostname is enabled, then canonical and final match during the same pass. The exec keyword executes the specified command under the user's shell. If the command returns a zero exit status then the condition is considered true. Commands containing whitespace characters must be quoted. Arguments to exec accept the tokens described in the TOKENS section. The localnetwork keyword matches the addresses of active local network interfaces against the supplied list of networks in CIDR format. This may be convenient for varying the effective configuration on devices that roam between networks. Note that network address is not a trustworthy criteria in many situations (e.g. when the network is automatically configured using DHCP) and so caution should be applied if using it to control security- sensitive configuration. The other keywords' criteria must be single entries or comma- separated lists and may use the wildcard and negation operators described in the PATTERNS section. The criteria for the host keyword are matched against the target hostname, after any substitution by the Hostname or CanonicalizeHostname options. The originalhost keyword matches against the hostname as it was specified on the command-line. The tagged keyword matches a tag name specified by a prior Tag directive or on the ssh(1) command- line using the -P flag. The user keyword matches against the target username on the remote host. The localuser keyword matches against the name of the local user running ssh(1) (this keyword may be useful in system-wide ssh_config files). AddKeysToAgent Specifies whether keys should be automatically added to a running ssh-agent(1). If this option is set to yes and a key is loaded from a file, the key and its passphrase are added to the agent with the default lifetime, as if by ssh-add(1). If this option is set to ask, ssh(1) will require confirmation using the SSH_ASKPASS program before adding a key (see ssh-add(1) for details). If this option is set to confirm, each use of the key must be confirmed, as if the -c option was specified to ssh-add(1). If this option is set to no, no keys are added to the agent. Alternately, this option may be specified as a time interval using the format described in the TIME FORMATS section of sshd_config(5) to specify the key's lifetime in ssh-agent(1), after which it will automatically be removed. The argument must be no (the default), yes, confirm (optionally followed by a time interval), ask or a time interval. AddressFamily Specifies which address family to use when connecting. Valid arguments are any (the default), inet (use IPv4 only), or inet6 (use IPv6 only). BatchMode If set to yes, user interaction such as password prompts and host key confirmation requests will be disabled. This option is useful in scripts and other batch jobs where no user is present to interact with ssh(1). The argument must be yes or no (the default). BindAddress Use the specified address on the local machine as the source address of the connection. Only useful on systems with more than one address. BindInterface Use the address of the specified interface on the local machine as the source address of the connection. CanonicalDomains When CanonicalizeHostname is enabled, this option specifies the list of domain suffixes in which to search for the specified destination host. CanonicalizeFallbackLocal Specifies whether to fail with an error when hostname canonicalization fails. The default, yes, will attempt to look up the unqualified hostname using the system resolver's search rules. A value of no will cause ssh(1) to fail instantly if CanonicalizeHostname is enabled and the target hostname cannot be found in any of the domains specified by CanonicalDomains. CanonicalizeHostname Controls whether explicit hostname canonicalization is performed. The default, no, is not to perform any name rewriting and let the system resolver handle all hostname lookups. If set to yes then, for connections that do not use a ProxyCommand or ProxyJump, ssh(1) will attempt to canonicalize the hostname specified on the command line using the CanonicalDomains suffixes and CanonicalizePermittedCNAMEs rules. If CanonicalizeHostname is set to always, then canonicalization is applied to proxied connections too. If this option is enabled, then the configuration files are processed again using the new target name to pick up any new configuration in matching Host and Match stanzas. A value of none disables the use of a ProxyJump host. CanonicalizeMaxDots Specifies the maximum number of dot characters in a hostname before canonicalization is disabled. The default, 1, allows a single dot (i.e. hostname.subdomain). CanonicalizePermittedCNAMEs Specifies rules to determine whether CNAMEs should be followed when canonicalizing hostnames. The rules consist of one or more arguments of source_domain_list:target_domain_list, where source_domain_list is a pattern-list of domains that may follow CNAMEs in canonicalization, and target_domain_list is a pattern- list of domains that they may resolve to. For example, "*.a.example.com:*.b.example.com,*.c.example.com" will allow hostnames matching "*.a.example.com" to be canonicalized to names in the "*.b.example.com" or "*.c.example.com" domains. A single argument of "none" causes no CNAMEs to be considered for canonicalization. This is the default behaviour. CASignatureAlgorithms Specifies which algorithms are allowed for signing of certificates by certificate authorities (CAs). The default is: ssh-ed25519,ecdsa-sha2-nistp256, ecdsa-sha2-nistp384,ecdsa-sha2-nistp521, sk-ssh-ed25519@openssh.com, sk-ecdsa-sha2-nistp256@openssh.com, rsa-sha2-512,rsa-sha2-256 If the specified list begins with a M-bM-^@M-^X+M-bM-^@M-^Y character, then the specified algorithms will be appended to the default set instead of replacing them. If the specified list begins with a M-bM-^@M-^X-M-bM-^@M-^Y character, then the specified algorithms (including wildcards) will be removed from the default set instead of replacing them. ssh(1) will not accept host certificates signed using algorithms other than those specified. CertificateFile Specifies a file from which the user's certificate is read. A corresponding private key must be provided separately in order to use this certificate either from an IdentityFile directive or -i flag to ssh(1), via ssh-agent(1), or via a PKCS11Provider or SecurityKeyProvider. Arguments to CertificateFile may use the tilde syntax to refer to a user's home directory, the tokens described in the TOKENS section and environment variables as described in the ENVIRONMENT VARIABLES section. It is possible to have multiple certificate files specified in configuration files; these certificates will be tried in sequence. Multiple CertificateFile directives will add to the list of certificates used for authentication. CheckHostIP If set to yes, ssh(1) will additionally check the host IP address in the known_hosts file. This allows it to detect if a host key changed due to DNS spoofing and will add addresses of destination hosts to ~/.ssh/known_hosts in the process, regardless of the setting of StrictHostKeyChecking. If the option is set to no (the default), the check will not be executed. Ciphers Specifies the ciphers allowed and their order of preference. Multiple ciphers must be comma-separated. If the specified list begins with a M-bM-^@M-^X+M-bM-^@M-^Y character, then the specified ciphers will be appended to the default set instead of replacing them. If the specified list begins with a M-bM-^@M-^X-M-bM-^@M-^Y character, then the specified ciphers (including wildcards) will be removed from the default set instead of replacing them. If the specified list begins with a M-bM-^@M-^X^M-bM-^@M-^Y character, then the specified ciphers will be placed at the head of the default set. The supported ciphers are: 3des-cbc aes128-cbc aes192-cbc aes256-cbc aes128-ctr aes192-ctr aes256-ctr aes128-gcm@openssh.com aes256-gcm@openssh.com chacha20-poly1305@openssh.com The default is: chacha20-poly1305@openssh.com, aes128-ctr,aes192-ctr,aes256-ctr, aes128-gcm@openssh.com,aes256-gcm@openssh.com The list of available ciphers may also be obtained using "ssh -Q cipher". ClearAllForwardings Specifies that all local, remote, and dynamic port forwardings specified in the configuration files or on the command line be cleared. This option is primarily useful when used from the ssh(1) command line to clear port forwardings set in configuration files, and is automatically set by scp(1) and sftp(1). The argument must be yes or no (the default). Compression Specifies whether to use compression. The argument must be yes or no (the default). ConnectionAttempts Specifies the number of tries (one per second) to make before exiting. The argument must be an integer. This may be useful in scripts if the connection sometimes fails. The default is 1. ConnectTimeout Specifies the timeout (in seconds) used when connecting to the SSH server, instead of using the default system TCP timeout. This timeout is applied both to establishing the connection and to performing the initial SSH protocol handshake and key exchange. ControlMaster Enables the sharing of multiple sessions over a single network connection. When set to yes, ssh(1) will listen for connections on a control socket specified using the ControlPath argument. Additional sessions can connect to this socket using the same ControlPath with ControlMaster set to no (the default). These sessions will try to reuse the master instance's network connection rather than initiating new ones, but will fall back to connecting normally if the control socket does not exist, or is not listening. Setting this to ask will cause ssh(1) to listen for control connections, but require confirmation using ssh-askpass(1). If the ControlPath cannot be opened, ssh(1) will continue without connecting to a master instance. X11 and ssh-agent(1) forwarding is supported over these multiplexed connections, however the display and agent forwarded will be the one belonging to the master connection i.e. it is not possible to forward multiple displays or agents. Two additional options allow for opportunistic multiplexing: try to use a master connection but fall back to creating a new one if one does not already exist. These options are: auto and autoask. The latter requires confirmation like the ask option. ControlPath Specify the path to the control socket used for connection sharing as described in the ControlMaster section above or the string none to disable connection sharing. Arguments to ControlPath may use the tilde syntax to refer to a user's home directory, the tokens described in the TOKENS section and environment variables as described in the ENVIRONMENT VARIABLES section. It is recommended that any ControlPath used for opportunistic connection sharing include at least %h, %p, and %r (or alternatively %C) and be placed in a directory that is not writable by other users. This ensures that shared connections are uniquely identified. ControlPersist When used in conjunction with ControlMaster, specifies that the master connection should remain open in the background (waiting for future client connections) after the initial client connection has been closed. If set to no (the default), then the master connection will not be placed into the background, and will close as soon as the initial client connection is closed. If set to yes or 0, then the master connection will remain in the background indefinitely (until killed or closed via a mechanism such as the "ssh -O exit"). If set to a time in seconds, or a time in any of the formats documented in sshd_config(5), then the backgrounded master connection will automatically terminate after it has remained idle (with no client connections) for the specified time. DynamicForward Specifies that a TCP port on the local machine be forwarded over the secure channel, and the application protocol is then used to determine where to connect to from the remote machine. The argument must be [bind_address:]port. IPv6 addresses can be specified by enclosing addresses in square brackets. By default, the local port is bound in accordance with the GatewayPorts setting. However, an explicit bind_address may be used to bind the connection to a specific address. The bind_address of localhost indicates that the listening port be bound for local use only, while an empty address or M-bM-^@M-^X*M-bM-^@M-^Y indicates that the port should be available from all interfaces. Currently the SOCKS4 and SOCKS5 protocols are supported, and ssh(1) will act as a SOCKS server. Multiple forwardings may be specified, and additional forwardings can be given on the command line. Only the superuser can forward privileged ports. EnableEscapeCommandline Enables the command line option in the EscapeChar menu for interactive sessions (default M-bM-^@M-^X~CM-bM-^@M-^Y). By default, the command line is disabled. EnableSSHKeysign Setting this option to yes in the global client configuration file /etc/ssh/ssh_config enables the use of the helper program ssh-keysign(8) during HostbasedAuthentication. The argument must be yes or no (the default). This option should be placed in the non-hostspecific section. See ssh-keysign(8) for more information. EscapeChar Sets the escape character (default: M-bM-^@M-^X~M-bM-^@M-^Y). The escape character can also be set on the command line. The argument should be a single character, M-bM-^@M-^X^M-bM-^@M-^Y followed by a letter, or none to disable the escape character entirely (making the connection transparent for binary data). ExitOnForwardFailure Specifies whether ssh(1) should terminate the connection if it cannot set up all requested dynamic, tunnel, local, and remote port forwardings, (e.g. if either end is unable to bind and listen on a specified port). Note that ExitOnForwardFailure does not apply to connections made over port forwardings and will not, for example, cause ssh(1) to exit if TCP connections to the ultimate forwarding destination fail. The argument must be yes or no (the default). FingerprintHash Specifies the hash algorithm used when displaying key fingerprints. Valid options are: md5 and sha256 (the default). ForkAfterAuthentication Requests ssh to go to background just before command execution. This is useful if ssh is going to ask for passwords or passphrases, but the user wants it in the background. This implies the StdinNull configuration option being set to M-bM-^@M-^\yesM-bM-^@M-^]. The recommended way to start X11 programs at a remote site is with something like ssh -f host xterm, which is the same as ssh host xterm if the ForkAfterAuthentication configuration option is set to M-bM-^@M-^\yesM-bM-^@M-^]. If the ExitOnForwardFailure configuration option is set to M-bM-^@M-^\yesM-bM-^@M-^], then a client started with the ForkAfterAuthentication configuration option being set to M-bM-^@M-^\yesM-bM-^@M-^] will wait for all remote port forwards to be successfully established before placing itself in the background. The argument to this keyword must be yes (same as the -f option) or no (the default). ForwardAgent Specifies whether the connection to the authentication agent (if any) will be forwarded to the remote machine. The argument may be yes, no (the default), an explicit path to an agent socket or the name of an environment variable (beginning with M-bM-^@M-^X$M-bM-^@M-^Y) in which to find the path. Agent forwarding should be enabled with caution. Users with the ability to bypass file permissions on the remote host (for the agent's Unix-domain socket) can access the local agent through the forwarded connection. An attacker cannot obtain key material from the agent, however they can perform operations on the keys that enable them to authenticate using the identities loaded into the agent. ForwardX11 Specifies whether X11 connections will be automatically redirected over the secure channel and DISPLAY set. The argument must be yes or no (the default). X11 forwarding should be enabled with caution. Users with the ability to bypass file permissions on the remote host (for the user's X11 authorization database) can access the local X11 display through the forwarded connection. An attacker may then be able to perform activities such as keystroke monitoring if the ForwardX11Trusted option is also enabled. ForwardX11Timeout Specify a timeout for untrusted X11 forwarding using the format described in the TIME FORMATS section of sshd_config(5). X11 connections received by ssh(1) after this time will be refused. Setting ForwardX11Timeout to zero will disable the timeout and permit X11 forwarding for the life of the connection. The default is to disable untrusted X11 forwarding after twenty minutes has elapsed. ForwardX11Trusted If this option is set to yes, remote X11 clients will have full access to the original X11 display. If this option is set to no (the default), remote X11 clients will be considered untrusted and prevented from stealing or tampering with data belonging to trusted X11 clients. Furthermore, the xauth(1) token used for the session will be set to expire after 20 minutes. Remote clients will be refused access after this time. See the X11 SECURITY extension specification for full details on the restrictions imposed on untrusted clients. GatewayPorts Specifies whether remote hosts are allowed to connect to local forwarded ports. By default, ssh(1) binds local port forwardings to the loopback address. This prevents other remote hosts from connecting to forwarded ports. GatewayPorts can be used to specify that ssh should bind local port forwardings to the wildcard address, thus allowing remote hosts to connect to forwarded ports. The argument must be yes or no (the default). GlobalKnownHostsFile Specifies one or more files to use for the global host key database, separated by whitespace. The default is /etc/ssh/ssh_known_hosts, /etc/ssh/ssh_known_hosts2. GSSAPIAuthentication Specifies whether user authentication based on GSSAPI is allowed. The default is no. GSSAPIDelegateCredentials Forward (delegate) credentials to the server. The default is no. HashKnownHosts Indicates that ssh(1) should hash host names and addresses when they are added to ~/.ssh/known_hosts. These hashed names may be used normally by ssh(1) and sshd(8), but they do not visually reveal identifying information if the file's contents are disclosed. The default is no. Note that existing names and addresses in known hosts files will not be converted automatically, but may be manually hashed using ssh-keygen(1). HostbasedAcceptedAlgorithms Specifies the signature algorithms that will be used for hostbased authentication as a comma-separated list of patterns. Alternately if the specified list begins with a M-bM-^@M-^X+M-bM-^@M-^Y character, then the specified signature algorithms will be appended to the default set instead of replacing them. If the specified list begins with a M-bM-^@M-^X-M-bM-^@M-^Y character, then the specified signature algorithms (including wildcards) will be removed from the default set instead of replacing them. If the specified list begins with a M-bM-^@M-^X^M-bM-^@M-^Y character, then the specified signature algorithms will be placed at the head of the default set. The default for this option is: ssh-ed25519-cert-v01@openssh.com, ecdsa-sha2-nistp256-cert-v01@openssh.com, ecdsa-sha2-nistp384-cert-v01@openssh.com, ecdsa-sha2-nistp521-cert-v01@openssh.com, sk-ssh-ed25519-cert-v01@openssh.com, sk-ecdsa-sha2-nistp256-cert-v01@openssh.com, rsa-sha2-512-cert-v01@openssh.com, rsa-sha2-256-cert-v01@openssh.com, ssh-ed25519, ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521, sk-ssh-ed25519@openssh.com, sk-ecdsa-sha2-nistp256@openssh.com, rsa-sha2-512,rsa-sha2-256 The -Q option of ssh(1) may be used to list supported signature algorithms. This was formerly named HostbasedKeyTypes. HostbasedAuthentication Specifies whether to try rhosts based authentication with public key authentication. The argument must be yes or no (the default). HostKeyAlgorithms Specifies the host key signature algorithms that the client wants to use in order of preference. Alternately if the specified list begins with a M-bM-^@M-^X+M-bM-^@M-^Y character, then the specified signature algorithms will be appended to the default set instead of replacing them. If the specified list begins with a M-bM-^@M-^X-M-bM-^@M-^Y character, then the specified signature algorithms (including wildcards) will be removed from the default set instead of replacing them. If the specified list begins with a M-bM-^@M-^X^M-bM-^@M-^Y character, then the specified signature algorithms will be placed at the head of the default set. The default for this option is: ssh-ed25519-cert-v01@openssh.com, ecdsa-sha2-nistp256-cert-v01@openssh.com, ecdsa-sha2-nistp384-cert-v01@openssh.com, ecdsa-sha2-nistp521-cert-v01@openssh.com, sk-ssh-ed25519-cert-v01@openssh.com, sk-ecdsa-sha2-nistp256-cert-v01@openssh.com, rsa-sha2-512-cert-v01@openssh.com, rsa-sha2-256-cert-v01@openssh.com, ssh-ed25519, ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521, sk-ecdsa-sha2-nistp256@openssh.com, sk-ssh-ed25519@openssh.com, rsa-sha2-512,rsa-sha2-256 If hostkeys are known for the destination host then this default is modified to prefer their algorithms. The list of available signature algorithms may also be obtained using "ssh -Q HostKeyAlgorithms". HostKeyAlias Specifies an alias that should be used instead of the real host name when looking up or saving the host key in the host key database files and when validating host certificates. This option is useful for tunneling SSH connections or for multiple servers running on a single host. Hostname Specifies the real host name to log into. This can be used to specify nicknames or abbreviations for hosts. Arguments to Hostname accept the tokens described in the TOKENS section. Numeric IP addresses are also permitted (both on the command line and in Hostname specifications). The default is the name given on the command line. IdentitiesOnly Specifies that ssh(1) should only use the configured authentication identity and certificate files (either the default files, or those explicitly configured in the ssh_config files or passed on the ssh(1) command-line), even if ssh-agent(1) or a PKCS11Provider or SecurityKeyProvider offers more identities. The argument to this keyword must be yes or no (the default). This option is intended for situations where ssh-agent offers many different identities. IdentityAgent Specifies the UNIX-domain socket used to communicate with the authentication agent. This option overrides the SSH_AUTH_SOCK environment variable and can be used to select a specific agent. Setting the socket name to none disables the use of an authentication agent. If the string "SSH_AUTH_SOCK" is specified, the location of the socket will be read from the SSH_AUTH_SOCK environment variable. Otherwise if the specified value begins with a M-bM-^@M-^X$M-bM-^@M-^Y character, then it will be treated as an environment variable containing the location of the socket. Arguments to IdentityAgent may use the tilde syntax to refer to a user's home directory, the tokens described in the TOKENS section and environment variables as described in the ENVIRONMENT VARIABLES section. IdentityFile Specifies a file from which the user's DSA, ECDSA, authenticator- hosted ECDSA, Ed25519, authenticator-hosted Ed25519 or RSA authentication identity is read. You can also specify a public key file to use the corresponding private key that is loaded in ssh-agent(1) when the private key file is not present locally. The default is ~/.ssh/id_rsa, ~/.ssh/id_ecdsa, ~/.ssh/id_ecdsa_sk, ~/.ssh/id_ed25519, ~/.ssh/id_ed25519_sk and ~/.ssh/id_dsa. Additionally, any identities represented by the authentication agent will be used for authentication unless IdentitiesOnly is set. If no certificates have been explicitly specified by CertificateFile, ssh(1) will try to load certificate information from the filename obtained by appending -cert.pub to the path of a specified IdentityFile. Arguments to IdentityFile may use the tilde syntax to refer to a user's home directory or the tokens described in the TOKENS section. It is possible to have multiple identity files specified in configuration files; all these identities will be tried in sequence. Multiple IdentityFile directives will add to the list of identities tried (this behaviour differs from that of other configuration directives). IdentityFile may be used in conjunction with IdentitiesOnly to select which identities in an agent are offered during authentication. IdentityFile may also be used in conjunction with CertificateFile in order to provide any certificate also needed for authentication with the identity. IgnoreUnknown Specifies a pattern-list of unknown options to be ignored if they are encountered in configuration parsing. This may be used to suppress errors if ssh_config contains options that are unrecognised by ssh(1). It is recommended that IgnoreUnknown be listed early in the configuration file as it will not be applied to unknown options that appear before it. Include Include the specified configuration file(s). Multiple pathnames may be specified and each pathname may contain glob(7) wildcards and, for user configurations, shell-like M-bM-^@M-^X~M-bM-^@M-^Y references to user home directories. Wildcards will be expanded and processed in lexical order. Files without absolute paths are assumed to be in ~/.ssh if included in a user configuration file or /etc/ssh if included from the system configuration file. Include directive may appear inside a Match or Host block to perform conditional inclusion. IPQoS Specifies the IPv4 type-of-service or DSCP class for connections. Accepted values are af11, af12, af13, af21, af22, af23, af31, af32, af33, af41, af42, af43, cs0, cs1, cs2, cs3, cs4, cs5, cs6, cs7, ef, le, lowdelay, throughput, reliability, a numeric value, or none to use the operating system default. This option may take one or two arguments, separated by whitespace. If one argument is specified, it is used as the packet class unconditionally. If two values are specified, the first is automatically selected for interactive sessions and the second for non-interactive sessions. The default is af21 (Low-Latency Data) for interactive sessions and cs1 (Lower Effort) for non- interactive sessions. KbdInteractiveAuthentication Specifies whether to use keyboard-interactive authentication. The argument to this keyword must be yes (the default) or no. ChallengeResponseAuthentication is a deprecated alias for this. KbdInteractiveDevices Specifies the list of methods to use in keyboard-interactive authentication. Multiple method names must be comma-separated. The default is to use the server specified list. The methods available vary depending on what the server supports. For an OpenSSH server, it may be zero or more of: bsdauth and pam. KexAlgorithms Specifies the available KEX (Key Exchange) algorithms. Multiple algorithms must be comma-separated. If the specified list begins with a M-bM-^@M-^X+M-bM-^@M-^Y character, then the specified algorithms will be appended to the default set instead of replacing them. If the specified list begins with a M-bM-^@M-^X-M-bM-^@M-^Y character, then the specified algorithms (including wildcards) will be removed from the default set instead of replacing them. If the specified list begins with a M-bM-^@M-^X^M-bM-^@M-^Y character, then the specified algorithms will be placed at the head of the default set. The default is: sntrup761x25519-sha512@openssh.com, curve25519-sha256,curve25519-sha256@libssh.org, ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521, diffie-hellman-group-exchange-sha256, diffie-hellman-group16-sha512, diffie-hellman-group18-sha512, diffie-hellman-group14-sha256 The list of available key exchange algorithms may also be obtained using "ssh -Q kex". KnownHostsCommand Specifies a command to use to obtain a list of host keys, in addition to those listed in UserKnownHostsFile and GlobalKnownHostsFile. This command is executed after the files have been read. It may write host key lines to standard output in identical format to the usual files (described in the VERIFYING HOST KEYS section in ssh(1)). Arguments to KnownHostsCommand accept the tokens described in the TOKENS section. The command may be invoked multiple times per connection: once when preparing the preference list of host key algorithms to use, again to obtain the host key for the requested host name and, if CheckHostIP is enabled, one more time to obtain the host key matching the server's address. If the command exits abnormally or returns a non-zero exit status then the connection is terminated. LocalCommand Specifies a command to execute on the local machine after successfully connecting to the server. The command string extends to the end of the line, and is executed with the user's shell. Arguments to LocalCommand accept the tokens described in the TOKENS section. The command is run synchronously and does not have access to the session of the ssh(1) that spawned it. It should not be used for interactive commands. This directive is ignored unless PermitLocalCommand has been enabled. LocalForward Specifies that a TCP port on the local machine be forwarded over the secure channel to the specified host and port from the remote machine. The first argument specifies the listener and may be [bind_address:]port or a Unix domain socket path. The second argument is the destination and may be host:hostport or a Unix domain socket path if the remote host supports it. IPv6 addresses can be specified by enclosing addresses in square brackets. Multiple forwardings may be specified, and additional forwardings can be given on the command line. Only the superuser can forward privileged ports. By default, the local port is bound in accordance with the GatewayPorts setting. However, an explicit bind_address may be used to bind the connection to a specific address. The bind_address of localhost indicates that the listening port be bound for local use only, while an empty address or M-bM-^@M-^X*M-bM-^@M-^Y indicates that the port should be available from all interfaces. Unix domain socket paths may use the tokens described in the TOKENS section and environment variables as described in the ENVIRONMENT VARIABLES section. LogLevel Gives the verbosity level that is used when logging messages from ssh(1). The possible values are: QUIET, FATAL, ERROR, INFO, VERBOSE, DEBUG, DEBUG1, DEBUG2, and DEBUG3. The default is INFO. DEBUG and DEBUG1 are equivalent. DEBUG2 and DEBUG3 each specify higher levels of verbose output. LogVerbose Specify one or more overrides to LogLevel. An override consists of a pattern lists that matches the source file, function and line number to force detailed logging for. For example, an override pattern of: kex.c:*:1000,*:kex_exchange_identification():*,packet.c:* would enable detailed logging for line 1000 of kex.c, everything in the kex_exchange_identification() function, and all code in the packet.c file. This option is intended for debugging and no overrides are enabled by default. MACs Specifies the MAC (message authentication code) algorithms in order of preference. The MAC algorithm is used for data integrity protection. Multiple algorithms must be comma- separated. If the specified list begins with a M-bM-^@M-^X+M-bM-^@M-^Y character, then the specified algorithms will be appended to the default set instead of replacing them. If the specified list begins with a M-bM-^@M-^X-M-bM-^@M-^Y character, then the specified algorithms (including wildcards) will be removed from the default set instead of replacing them. If the specified list begins with a M-bM-^@M-^X^M-bM-^@M-^Y character, then the specified algorithms will be placed at the head of the default set. The algorithms that contain "-etm" calculate the MAC after encryption (encrypt-then-mac). These are considered safer and their use recommended. The default is: umac-64-etm@openssh.com,umac-128-etm@openssh.com, hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com, hmac-sha1-etm@openssh.com, umac-64@openssh.com,umac-128@openssh.com, hmac-sha2-256,hmac-sha2-512,hmac-sha1 The list of available MAC algorithms may also be obtained using "ssh -Q mac". NoHostAuthenticationForLocalhost Disable host authentication for localhost (loopback addresses). The argument to this keyword must be yes or no (the default). NumberOfPasswordPrompts Specifies the number of password prompts before giving up. The argument to this keyword must be an integer. The default is 3. + ObscureKeystrokeTiming + Specifies whether ssh(1) should try to obscure inter-keystroke + timings from passive observers of network traffic. If enabled, + then for interactive sessions, ssh(1) will send keystrokes at + fixed intervals of a few tens of milliseconds and will send fake + keystroke packets for some time after typing ceases. The + argument to this keyword must be yes, no or an interval specifier + of the form interval:milliseconds (e.g. interval:80 for 80 + milliseconds). The default is to obscure keystrokes using a 20ms + packet interval. Note that smaller intervals will result in + higher fake keystroke packet rates. + PasswordAuthentication Specifies whether to use password authentication. The argument to this keyword must be yes (the default) or no. PermitLocalCommand Allow local command execution via the LocalCommand option or using the !command escape sequence in ssh(1). The argument must be yes or no (the default). PermitRemoteOpen Specifies the destinations to which remote TCP port forwarding is permitted when RemoteForward is used as a SOCKS proxy. The forwarding specification must be one of the following forms: PermitRemoteOpen host:port PermitRemoteOpen IPv4_addr:port PermitRemoteOpen [IPv6_addr]:port Multiple forwards may be specified by separating them with whitespace. An argument of any can be used to remove all restrictions and permit any forwarding requests. An argument of none can be used to prohibit all forwarding requests. The wildcard M-bM-^@M-^X*M-bM-^@M-^Y can be used for host or port to allow all hosts or ports respectively. Otherwise, no pattern matching or address lookups are performed on supplied names. PKCS11Provider Specifies which PKCS#11 provider to use or none to indicate that no provider should be used (the default). The argument to this keyword is a path to the PKCS#11 shared library ssh(1) should use to communicate with a PKCS#11 token providing keys for user authentication. Port Specifies the port number to connect on the remote host. The default is 22. PreferredAuthentications Specifies the order in which the client should try authentication methods. This allows a client to prefer one method (e.g. keyboard-interactive) over another method (e.g. password). The default is: gssapi-with-mic,hostbased,publickey, keyboard-interactive,password ProxyCommand Specifies the command to use to connect to the server. The command string extends to the end of the line, and is executed using the user's shell M-bM-^@M-^XexecM-bM-^@M-^Y directive to avoid a lingering shell process. Arguments to ProxyCommand accept the tokens described in the TOKENS section. The command can be basically anything, and should read from its standard input and write to its standard output. It should eventually connect an sshd(8) server running on some machine, or execute sshd -i somewhere. Host key management will be done using the Hostname of the host being connected (defaulting to the name typed by the user). Setting the command to none disables this option entirely. Note that CheckHostIP is not available for connects with a proxy command. This directive is useful in conjunction with nc(1) and its proxy support. For example, the following directive would connect via an HTTP proxy at 192.0.2.0: ProxyCommand /usr/bin/nc -X connect -x 192.0.2.0:8080 %h %p ProxyJump Specifies one or more jump proxies as either [user@]host[:port] or an ssh URI. Multiple proxies may be separated by comma characters and will be visited sequentially. Setting this option will cause ssh(1) to connect to the target host by first making a ssh(1) connection to the specified ProxyJump host and then establishing a TCP forwarding to the ultimate target from there. Setting the host to none disables this option entirely. Note that this option will compete with the ProxyCommand option - whichever is specified first will prevent later instances of the other from taking effect. Note also that the configuration for the destination host (either supplied via the command-line or the configuration file) is not generally applied to jump hosts. ~/.ssh/config should be used if specific configuration is required for jump hosts. ProxyUseFdpass Specifies that ProxyCommand will pass a connected file descriptor back to ssh(1) instead of continuing to execute and pass data. The default is no. PubkeyAcceptedAlgorithms Specifies the signature algorithms that will be used for public key authentication as a comma-separated list of patterns. If the specified list begins with a M-bM-^@M-^X+M-bM-^@M-^Y character, then the algorithms after it will be appended to the default instead of replacing it. If the specified list begins with a M-bM-^@M-^X-M-bM-^@M-^Y character, then the specified algorithms (including wildcards) will be removed from the default set instead of replacing them. If the specified list begins with a M-bM-^@M-^X^M-bM-^@M-^Y character, then the specified algorithms will be placed at the head of the default set. The default for this option is: ssh-ed25519-cert-v01@openssh.com, ecdsa-sha2-nistp256-cert-v01@openssh.com, ecdsa-sha2-nistp384-cert-v01@openssh.com, ecdsa-sha2-nistp521-cert-v01@openssh.com, sk-ssh-ed25519-cert-v01@openssh.com, sk-ecdsa-sha2-nistp256-cert-v01@openssh.com, rsa-sha2-512-cert-v01@openssh.com, rsa-sha2-256-cert-v01@openssh.com, ssh-ed25519, ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521, sk-ssh-ed25519@openssh.com, sk-ecdsa-sha2-nistp256@openssh.com, rsa-sha2-512,rsa-sha2-256 The list of available signature algorithms may also be obtained using "ssh -Q PubkeyAcceptedAlgorithms". PubkeyAuthentication Specifies whether to try public key authentication. The argument to this keyword must be yes (the default), no, unbound or host-bound. The final two options enable public key authentication while respectively disabling or enabling the OpenSSH host-bound authentication protocol extension required for restricted ssh-agent(1) forwarding. RekeyLimit Specifies the maximum amount of data that may be transmitted or received before the session key is renegotiated, optionally followed by a maximum amount of time that may pass before the session key is renegotiated. The first argument is specified in bytes and may have a suffix of M-bM-^@M-^XKM-bM-^@M-^Y, M-bM-^@M-^XMM-bM-^@M-^Y, or M-bM-^@M-^XGM-bM-^@M-^Y to indicate Kilobytes, Megabytes, or Gigabytes, respectively. The default is between M-bM-^@M-^X1GM-bM-^@M-^Y and M-bM-^@M-^X4GM-bM-^@M-^Y, depending on the cipher. The optional second value is specified in seconds and may use any of the units documented in the TIME FORMATS section of sshd_config(5). The default value for RekeyLimit is default none, which means that rekeying is performed after the cipher's default amount of data has been sent or received and no time based rekeying is done. RemoteCommand Specifies a command to execute on the remote machine after successfully connecting to the server. The command string extends to the end of the line, and is executed with the user's shell. Arguments to RemoteCommand accept the tokens described in the TOKENS section. RemoteForward Specifies that a TCP port on the remote machine be forwarded over the secure channel. The remote port may either be forwarded to a specified host and port from the local machine, or may act as a SOCKS 4/5 proxy that allows a remote client to connect to arbitrary destinations from the local machine. The first argument is the listening specification and may be [bind_address:]port or, if the remote host supports it, a Unix domain socket path. If forwarding to a specific destination then the second argument must be host:hostport or a Unix domain socket path, otherwise if no destination argument is specified then the remote forwarding will be established as a SOCKS proxy. When acting as a SOCKS proxy, the destination of the connection can be restricted by PermitRemoteOpen. IPv6 addresses can be specified by enclosing addresses in square brackets. Multiple forwardings may be specified, and additional forwardings can be given on the command line. Privileged ports can be forwarded only when logging in as root on the remote machine. Unix domain socket paths may use the tokens described in the TOKENS section and environment variables as described in the ENVIRONMENT VARIABLES section. If the port argument is 0, the listen port will be dynamically allocated on the server and reported to the client at run time. If the bind_address is not specified, the default is to only bind to loopback addresses. If the bind_address is M-bM-^@M-^X*M-bM-^@M-^Y or an empty string, then the forwarding is requested to listen on all interfaces. Specifying a remote bind_address will only succeed if the server's GatewayPorts option is enabled (see sshd_config(5)). RequestTTY Specifies whether to request a pseudo-tty for the session. The argument may be one of: no (never request a TTY), yes (always request a TTY when standard input is a TTY), force (always request a TTY) or auto (request a TTY when opening a login session). This option mirrors the -t and -T flags for ssh(1). RequiredRSASize Specifies the minimum RSA key size (in bits) that ssh(1) will accept. User authentication keys smaller than this limit will be ignored. Servers that present host keys smaller than this limit will cause the connection to be terminated. The default is 1024 bits. Note that this limit may only be raised from the default. RevokedHostKeys Specifies revoked host public keys. Keys listed in this file will be refused for host authentication. Note that if this file does not exist or is not readable, then host authentication will be refused for all hosts. Keys may be specified as a text file, listing one public key per line, or as an OpenSSH Key Revocation List (KRL) as generated by ssh-keygen(1). For more information on KRLs, see the KEY REVOCATION LISTS section in ssh-keygen(1). Arguments to RevokedHostKeys may use the tilde syntax to refer to a user's home directory, the tokens described in the TOKENS section and environment variables as described in the ENVIRONMENT VARIABLES section. SecurityKeyProvider Specifies a path to a library that will be used when loading any FIDO authenticator-hosted keys, overriding the default of using the built-in USB HID support. If the specified value begins with a M-bM-^@M-^X$M-bM-^@M-^Y character, then it will be treated as an environment variable containing the path to the library. SendEnv Specifies what variables from the local environ(7) should be sent to the server. The server must also support it, and the server must be configured to accept these environment variables. Note that the TERM environment variable is always sent whenever a pseudo-terminal is requested as it is required by the protocol. Refer to AcceptEnv in sshd_config(5) for how to configure the server. Variables are specified by name, which may contain wildcard characters. Multiple environment variables may be separated by whitespace or spread across multiple SendEnv directives. See PATTERNS for more information on patterns. It is possible to clear previously set SendEnv variable names by prefixing patterns with -. The default is not to send any environment variables. ServerAliveCountMax Sets the number of server alive messages (see below) which may be sent without ssh(1) receiving any messages back from the server. If this threshold is reached while server alive messages are being sent, ssh will disconnect from the server, terminating the session. It is important to note that the use of server alive messages is very different from TCPKeepAlive (below). The server alive messages are sent through the encrypted channel and therefore will not be spoofable. The TCP keepalive option enabled by TCPKeepAlive is spoofable. The server alive mechanism is valuable when the client or server depend on knowing when a connection has become unresponsive. The default value is 3. If, for example, ServerAliveInterval (see below) is set to 15 and ServerAliveCountMax is left at the default, if the server becomes unresponsive, ssh will disconnect after approximately 45 seconds. ServerAliveInterval Sets a timeout interval in seconds after which if no data has been received from the server, ssh(1) will send a message through the encrypted channel to request a response from the server. The default is 0, indicating that these messages will not be sent to the server. SessionType May be used to either request invocation of a subsystem on the remote system, or to prevent the execution of a remote command at all. The latter is useful for just forwarding ports. The argument to this keyword must be none (same as the -N option), subsystem (same as the -s option) or default (shell or command execution). SetEnv Directly specify one or more environment variables and their contents to be sent to the server. Similarly to SendEnv, with the exception of the TERM variable, the server must be prepared to accept the environment variable. StdinNull Redirects stdin from /dev/null (actually, prevents reading from stdin). Either this or the equivalent -n option must be used when ssh is run in the background. The argument to this keyword must be yes (same as the -n option) or no (the default). StreamLocalBindMask Sets the octal file creation mode mask (umask) used when creating a Unix-domain socket file for local or remote port forwarding. This option is only used for port forwarding to a Unix-domain socket file. The default value is 0177, which creates a Unix-domain socket file that is readable and writable only by the owner. Note that not all operating systems honor the file mode on Unix-domain socket files. StreamLocalBindUnlink Specifies whether to remove an existing Unix-domain socket file for local or remote port forwarding before creating a new one. If the socket file already exists and StreamLocalBindUnlink is not enabled, ssh will be unable to forward the port to the Unix- domain socket file. This option is only used for port forwarding to a Unix-domain socket file. The argument must be yes or no (the default). StrictHostKeyChecking If this flag is set to yes, ssh(1) will never automatically add host keys to the ~/.ssh/known_hosts file, and refuses to connect to hosts whose host key has changed. This provides maximum protection against man-in-the-middle (MITM) attacks, though it can be annoying when the /etc/ssh/ssh_known_hosts file is poorly maintained or when connections to new hosts are frequently made. This option forces the user to manually add all new hosts. If this flag is set to accept-new then ssh will automatically add new host keys to the user's known_hosts file, but will not permit connections to hosts with changed host keys. If this flag is set to no or off, ssh will automatically add new host keys to the user known hosts files and allow connections to hosts with changed hostkeys to proceed, subject to some restrictions. If this flag is set to ask (the default), new host keys will be added to the user known host files only after the user has confirmed that is what they really want to do, and ssh will refuse to connect to hosts whose host key has changed. The host keys of known hosts will be verified automatically in all cases. SyslogFacility Gives the facility code that is used when logging messages from ssh(1). The possible values are: DAEMON, USER, AUTH, LOCAL0, LOCAL1, LOCAL2, LOCAL3, LOCAL4, LOCAL5, LOCAL6, LOCAL7. The default is USER. TCPKeepAlive Specifies whether the system should send TCP keepalive messages to the other side. If they are sent, death of the connection or crash of one of the machines will be properly noticed. However, this means that connections will die if the route is down temporarily, and some people find it annoying. The default is yes (to send TCP keepalive messages), and the client will notice if the network goes down or the remote host dies. This is important in scripts, and many users want it too. To disable TCP keepalive messages, the value should be set to no. See also ServerAliveInterval for protocol-level keepalives. Tag Specify a configuration tag name that may be later used by a Match directive to select a block of configuration. Tunnel Request tun(4) device forwarding between the client and the server. The argument must be yes, point-to-point (layer 3), ethernet (layer 2), or no (the default). Specifying yes requests the default tunnel mode, which is point-to-point. TunnelDevice Specifies the tun(4) devices to open on the client (local_tun) and the server (remote_tun). The argument must be local_tun[:remote_tun]. The devices may be specified by numerical ID or the keyword any, which uses the next available tunnel device. If remote_tun is not specified, it defaults to any. The default is any:any. UpdateHostKeys Specifies whether ssh(1) should accept notifications of additional hostkeys from the server sent after authentication has completed and add them to UserKnownHostsFile. The argument must be yes, no or ask. This option allows learning alternate hostkeys for a server and supports graceful key rotation by allowing a server to send replacement public keys before old ones are removed. Additional hostkeys are only accepted if the key used to authenticate the host was already trusted or explicitly accepted by the user, the host was authenticated via UserKnownHostsFile (i.e. not GlobalKnownHostsFile) and the host was authenticated using a plain key and not a certificate. UpdateHostKeys is enabled by default if the user has not overridden the default UserKnownHostsFile setting and has not enabled VerifyHostKeyDNS, otherwise UpdateHostKeys will be set to no. If UpdateHostKeys is set to ask, then the user is asked to confirm the modifications to the known_hosts file. Confirmation is currently incompatible with ControlPersist, and will be disabled if it is enabled. Presently, only sshd(8) from OpenSSH 6.8 and greater support the "hostkeys@openssh.com" protocol extension used to inform the client of all the server's hostkeys. User Specifies the user to log in as. This can be useful when a different user name is used on different machines. This saves the trouble of having to remember to give the user name on the command line. UserKnownHostsFile Specifies one or more files to use for the user host key database, separated by whitespace. Each filename may use tilde notation to refer to the user's home directory, the tokens described in the TOKENS section and environment variables as described in the ENVIRONMENT VARIABLES section. A value of none causes ssh(1) to ignore any user-specific known hosts files. The default is ~/.ssh/known_hosts, ~/.ssh/known_hosts2. VerifyHostKeyDNS Specifies whether to verify the remote key using DNS and SSHFP resource records. If this option is set to yes, the client will implicitly trust keys that match a secure fingerprint from DNS. Insecure fingerprints will be handled as if this option was set to ask. If this option is set to ask, information on fingerprint match will be displayed, but the user will still need to confirm new host keys according to the StrictHostKeyChecking option. The default is no. See also VERIFYING HOST KEYS in ssh(1). VisualHostKey If this flag is set to yes, an ASCII art representation of the remote host key fingerprint is printed in addition to the fingerprint string at login and for unknown host keys. If this flag is set to no (the default), no fingerprint strings are printed at login and only the fingerprint string will be printed for unknown host keys. XAuthLocation Specifies the full pathname of the xauth(1) program. The default is /usr/X11R6/bin/xauth. PATTERNS A pattern consists of zero or more non-whitespace characters, M-bM-^@M-^X*M-bM-^@M-^Y (a wildcard that matches zero or more characters), or M-bM-^@M-^X?M-bM-^@M-^Y (a wildcard that matches exactly one character). For example, to specify a set of declarations for any host in the ".co.uk" set of domains, the following pattern could be used: Host *.co.uk The following pattern would match any host in the 192.168.0.[0-9] network range: Host 192.168.0.? A pattern-list is a comma-separated list of patterns. Patterns within pattern-lists may be negated by preceding them with an exclamation mark (M-bM-^@M-^X!M-bM-^@M-^Y). For example, to allow a key to be used from anywhere within an organization except from the "dialup" pool, the following entry (in authorized_keys) could be used: from="!*.dialup.example.com,*.example.com" Note that a negated match will never produce a positive result by itself. For example, attempting to match "host3" against the following pattern- list will fail: from="!host1,!host2" The solution here is to include a term that will yield a positive match, such as a wildcard: from="!host1,!host2,*" TOKENS Arguments to some keywords can make use of tokens, which are expanded at runtime: %% A literal M-bM-^@M-^X%M-bM-^@M-^Y. %C Hash of %l%h%p%r. %d Local user's home directory. %f The fingerprint of the server's host key. %H The known_hosts hostname or address that is being searched for. %h The remote hostname. %I A string describing the reason for a KnownHostsCommand execution: either ADDRESS when looking up a host by address (only when CheckHostIP is enabled), HOSTNAME when searching by hostname, or ORDER when preparing the host key algorithm preference list to use for the destination host. %i The local user ID. %K The base64 encoded host key. %k The host key alias if specified, otherwise the original remote hostname given on the command line. %L The local hostname. %l The local hostname, including the domain name. %n The original remote hostname, as given on the command line. %p The remote port. %r The remote username. %T The local tun(4) or tap(4) network interface assigned if tunnel forwarding was requested, or "NONE" otherwise. %t The type of the server host key, e.g. ssh-ed25519. %u The local username. CertificateFile, ControlPath, IdentityAgent, IdentityFile, KnownHostsCommand, LocalForward, Match exec, RemoteCommand, RemoteForward, RevokedHostKeys, and UserKnownHostsFile accept the tokens %%, %C, %d, %h, %i, %k, %L, %l, %n, %p, %r, and %u. KnownHostsCommand additionally accepts the tokens %f, %H, %I, %K and %t. Hostname accepts the tokens %% and %h. LocalCommand accepts all tokens. ProxyCommand and ProxyJump accept the tokens %%, %h, %n, %p, and %r. + Note that some of these directives build commands for execution via the + shell. Because ssh(1) performs no filtering or escaping of characters + that have special meaning in shell commands (e.g. quotes), it is the + user's reposibility to ensure that the arguments passed to ssh(1) do not + contain such characters and that tokens are appropriately quoted when + used. + ENVIRONMENT VARIABLES Arguments to some keywords can be expanded at runtime from environment variables on the client by enclosing them in ${}, for example ${HOME}/.ssh would refer to the user's .ssh directory. If a specified environment variable does not exist then an error will be returned and the setting for that keyword will be ignored. The keywords CertificateFile, ControlPath, IdentityAgent, IdentityFile, KnownHostsCommand, and UserKnownHostsFile support environment variables. The keywords LocalForward and RemoteForward support environment variables only for Unix domain socket paths. FILES ~/.ssh/config This is the per-user configuration file. The format of this file is described above. This file is used by the SSH client. Because of the potential for abuse, this file must have strict permissions: read/write for the user, and not writable by others. /etc/ssh/ssh_config Systemwide configuration file. This file provides defaults for those values that are not specified in the user's configuration file, and for those users who do not have a configuration file. This file must be world-readable. SEE ALSO ssh(1) AUTHORS OpenSSH is a derivative of the original and free ssh 1.2.12 release by Tatu Ylonen. Aaron Campbell, Bob Beck, Markus Friedl, Niels Provos, Theo de Raadt and Dug Song removed many bugs, re-added newer features and created OpenSSH. Markus Friedl contributed the support for SSH protocol versions 1.5 and 2.0. -OpenBSD 7.3 July 17, 2023 OpenBSD 7.3 +OpenBSD 7.3 October 4, 2023 OpenBSD 7.3 diff --git a/ssh_config.5 b/ssh_config.5 index ab8d1021d6cb..367305d2cf9d 100644 --- a/ssh_config.5 +++ b/ssh_config.5 @@ -1,2243 +1,2272 @@ .\" .\" Author: Tatu Ylonen .\" Copyright (c) 1995 Tatu Ylonen , Espoo, Finland .\" All rights reserved .\" .\" As far as I am concerned, the code I have written for this software .\" can be used freely for any purpose. Any derived versions of this .\" software must be clearly marked as such, and if the derived work is .\" incompatible with the protocol description in the RFC file, it must be .\" called by a name other than "ssh" or "Secure Shell". .\" .\" Copyright (c) 1999,2000 Markus Friedl. All rights reserved. .\" Copyright (c) 1999 Aaron Campbell. All rights reserved. .\" Copyright (c) 1999 Theo de Raadt. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR .\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES .\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. .\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, .\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT .\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, .\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY .\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.\" $OpenBSD: ssh_config.5,v 1.383 2023/07/17 05:36:14 jsg Exp $ -.Dd $Mdocdate: July 17 2023 $ +.\" $OpenBSD: ssh_config.5,v 1.387 2023/10/04 04:03:50 djm Exp $ +.Dd $Mdocdate: October 4 2023 $ .Dt SSH_CONFIG 5 .Os .Sh NAME .Nm ssh_config .Nd OpenSSH client configuration file .Sh DESCRIPTION .Xr ssh 1 obtains configuration data from the following sources in the following order: .Pp .Bl -enum -offset indent -compact .It command-line options .It user's configuration file .Pq Pa ~/.ssh/config .It system-wide configuration file .Pq Pa /etc/ssh/ssh_config .El .Pp Unless noted otherwise, for each parameter, the first obtained value will be used. The configuration files contain sections separated by .Cm Host specifications, and that section is only applied for hosts that match one of the patterns given in the specification. The matched host name is usually the one given on the command line (see the .Cm CanonicalizeHostname option for exceptions). .Pp Since the first obtained value for each parameter is used, more host-specific declarations should be given near the beginning of the file, and general defaults at the end. .Pp The file contains keyword-argument pairs, one per line. Lines starting with .Ql # and empty lines are interpreted as comments. Arguments may optionally be enclosed in double quotes .Pq \&" in order to represent arguments containing spaces. Configuration options may be separated by whitespace or optional whitespace and exactly one .Ql = ; the latter format is useful to avoid the need to quote whitespace when specifying configuration options using the .Nm ssh , .Nm scp , and .Nm sftp .Fl o option. .Pp The possible keywords and their meanings are as follows (note that keywords are case-insensitive and arguments are case-sensitive): .Bl -tag -width Ds .It Cm Host Restricts the following declarations (up to the next .Cm Host or .Cm Match keyword) to be only for those hosts that match one of the patterns given after the keyword. If more than one pattern is provided, they should be separated by whitespace. A single .Ql * as a pattern can be used to provide global defaults for all hosts. The host is usually the .Ar hostname argument given on the command line (see the .Cm CanonicalizeHostname keyword for exceptions). .Pp A pattern entry may be negated by prefixing it with an exclamation mark .Pq Sq !\& . If a negated entry is matched, then the .Cm Host entry is ignored, regardless of whether any other patterns on the line match. Negated matches are therefore useful to provide exceptions for wildcard matches. .Pp See .Sx PATTERNS for more information on patterns. .It Cm Match Restricts the following declarations (up to the next .Cm Host or .Cm Match keyword) to be used only when the conditions following the .Cm Match keyword are satisfied. Match conditions are specified using one or more criteria or the single token .Cm all which always matches. The available criteria keywords are: .Cm canonical , .Cm final , .Cm exec , .Cm localnetwork , .Cm host , .Cm originalhost , .Cm Tag , .Cm user , and .Cm localuser . The .Cm all criteria must appear alone or immediately after .Cm canonical or .Cm final . Other criteria may be combined arbitrarily. All criteria but .Cm all , .Cm canonical , and .Cm final require an argument. Criteria may be negated by prepending an exclamation mark .Pq Sq !\& . .Pp The .Cm canonical keyword matches only when the configuration file is being re-parsed after hostname canonicalization (see the .Cm CanonicalizeHostname option). This may be useful to specify conditions that work with canonical host names only. .Pp The .Cm final keyword requests that the configuration be re-parsed (regardless of whether .Cm CanonicalizeHostname is enabled), and matches only during this final pass. If .Cm CanonicalizeHostname is enabled, then .Cm canonical and .Cm final match during the same pass. .Pp The .Cm exec keyword executes the specified command under the user's shell. If the command returns a zero exit status then the condition is considered true. Commands containing whitespace characters must be quoted. Arguments to .Cm exec accept the tokens described in the .Sx TOKENS section. .Pp The .Cm localnetwork keyword matches the addresses of active local network interfaces against the supplied list of networks in CIDR format. This may be convenient for varying the effective configuration on devices that roam between networks. Note that network address is not a trustworthy criteria in many situations (e.g. when the network is automatically configured using DHCP) and so caution should be applied if using it to control security-sensitive configuration. .Pp The other keywords' criteria must be single entries or comma-separated lists and may use the wildcard and negation operators described in the .Sx PATTERNS section. The criteria for the .Cm host keyword are matched against the target hostname, after any substitution by the .Cm Hostname or .Cm CanonicalizeHostname options. The .Cm originalhost keyword matches against the hostname as it was specified on the command-line. The .Cm tagged keyword matches a tag name specified by a prior .Cm Tag directive or on the .Xr ssh 1 command-line using the .Fl P flag. The .Cm user keyword matches against the target username on the remote host. The .Cm localuser keyword matches against the name of the local user running .Xr ssh 1 (this keyword may be useful in system-wide .Nm files). .It Cm AddKeysToAgent Specifies whether keys should be automatically added to a running .Xr ssh-agent 1 . If this option is set to .Cm yes and a key is loaded from a file, the key and its passphrase are added to the agent with the default lifetime, as if by .Xr ssh-add 1 . If this option is set to .Cm ask , .Xr ssh 1 will require confirmation using the .Ev SSH_ASKPASS program before adding a key (see .Xr ssh-add 1 for details). If this option is set to .Cm confirm , each use of the key must be confirmed, as if the .Fl c option was specified to .Xr ssh-add 1 . If this option is set to .Cm no , no keys are added to the agent. Alternately, this option may be specified as a time interval using the format described in the .Sx TIME FORMATS section of .Xr sshd_config 5 to specify the key's lifetime in .Xr ssh-agent 1 , after which it will automatically be removed. The argument must be .Cm no (the default), .Cm yes , .Cm confirm (optionally followed by a time interval), .Cm ask or a time interval. .It Cm AddressFamily Specifies which address family to use when connecting. Valid arguments are .Cm any (the default), .Cm inet (use IPv4 only), or .Cm inet6 (use IPv6 only). .It Cm BatchMode If set to .Cm yes , user interaction such as password prompts and host key confirmation requests will be disabled. This option is useful in scripts and other batch jobs where no user is present to interact with .Xr ssh 1 . The argument must be .Cm yes or .Cm no (the default). .It Cm BindAddress Use the specified address on the local machine as the source address of the connection. Only useful on systems with more than one address. .It Cm BindInterface Use the address of the specified interface on the local machine as the source address of the connection. .It Cm CanonicalDomains When .Cm CanonicalizeHostname is enabled, this option specifies the list of domain suffixes in which to search for the specified destination host. .It Cm CanonicalizeFallbackLocal Specifies whether to fail with an error when hostname canonicalization fails. The default, .Cm yes , will attempt to look up the unqualified hostname using the system resolver's search rules. A value of .Cm no will cause .Xr ssh 1 to fail instantly if .Cm CanonicalizeHostname is enabled and the target hostname cannot be found in any of the domains specified by .Cm CanonicalDomains . .It Cm CanonicalizeHostname Controls whether explicit hostname canonicalization is performed. The default, .Cm no , is not to perform any name rewriting and let the system resolver handle all hostname lookups. If set to .Cm yes then, for connections that do not use a .Cm ProxyCommand or .Cm ProxyJump , .Xr ssh 1 will attempt to canonicalize the hostname specified on the command line using the .Cm CanonicalDomains suffixes and .Cm CanonicalizePermittedCNAMEs rules. If .Cm CanonicalizeHostname is set to .Cm always , then canonicalization is applied to proxied connections too. .Pp If this option is enabled, then the configuration files are processed again using the new target name to pick up any new configuration in matching .Cm Host and .Cm Match stanzas. A value of .Cm none disables the use of a .Cm ProxyJump host. .It Cm CanonicalizeMaxDots Specifies the maximum number of dot characters in a hostname before canonicalization is disabled. The default, 1, allows a single dot (i.e. hostname.subdomain). .It Cm CanonicalizePermittedCNAMEs Specifies rules to determine whether CNAMEs should be followed when canonicalizing hostnames. The rules consist of one or more arguments of .Ar source_domain_list : Ns Ar target_domain_list , where .Ar source_domain_list is a pattern-list of domains that may follow CNAMEs in canonicalization, and .Ar target_domain_list is a pattern-list of domains that they may resolve to. .Pp For example, .Qq *.a.example.com:*.b.example.com,*.c.example.com will allow hostnames matching .Qq *.a.example.com to be canonicalized to names in the .Qq *.b.example.com or .Qq *.c.example.com domains. .Pp A single argument of .Qq none causes no CNAMEs to be considered for canonicalization. This is the default behaviour. .It Cm CASignatureAlgorithms Specifies which algorithms are allowed for signing of certificates by certificate authorities (CAs). The default is: .Bd -literal -offset indent ssh-ed25519,ecdsa-sha2-nistp256, ecdsa-sha2-nistp384,ecdsa-sha2-nistp521, sk-ssh-ed25519@openssh.com, sk-ecdsa-sha2-nistp256@openssh.com, rsa-sha2-512,rsa-sha2-256 .Ed .Pp If the specified list begins with a .Sq + character, then the specified algorithms will be appended to the default set instead of replacing them. If the specified list begins with a .Sq - character, then the specified algorithms (including wildcards) will be removed from the default set instead of replacing them. .Pp .Xr ssh 1 will not accept host certificates signed using algorithms other than those specified. .It Cm CertificateFile Specifies a file from which the user's certificate is read. A corresponding private key must be provided separately in order to use this certificate either from an .Cm IdentityFile directive or .Fl i flag to .Xr ssh 1 , via .Xr ssh-agent 1 , or via a .Cm PKCS11Provider or .Cm SecurityKeyProvider . .Pp Arguments to .Cm CertificateFile may use the tilde syntax to refer to a user's home directory, the tokens described in the .Sx TOKENS section and environment variables as described in the .Sx ENVIRONMENT VARIABLES section. .Pp It is possible to have multiple certificate files specified in configuration files; these certificates will be tried in sequence. Multiple .Cm CertificateFile directives will add to the list of certificates used for authentication. .It Cm CheckHostIP If set to .Cm yes , .Xr ssh 1 will additionally check the host IP address in the .Pa known_hosts file. This allows it to detect if a host key changed due to DNS spoofing and will add addresses of destination hosts to .Pa ~/.ssh/known_hosts in the process, regardless of the setting of .Cm StrictHostKeyChecking . If the option is set to .Cm no (the default), the check will not be executed. .It Cm Ciphers Specifies the ciphers allowed and their order of preference. Multiple ciphers must be comma-separated. If the specified list begins with a .Sq + character, then the specified ciphers will be appended to the default set instead of replacing them. If the specified list begins with a .Sq - character, then the specified ciphers (including wildcards) will be removed from the default set instead of replacing them. If the specified list begins with a .Sq ^ character, then the specified ciphers will be placed at the head of the default set. .Pp The supported ciphers are: .Bd -literal -offset indent 3des-cbc aes128-cbc aes192-cbc aes256-cbc aes128-ctr aes192-ctr aes256-ctr aes128-gcm@openssh.com aes256-gcm@openssh.com chacha20-poly1305@openssh.com .Ed .Pp The default is: .Bd -literal -offset indent chacha20-poly1305@openssh.com, aes128-ctr,aes192-ctr,aes256-ctr, aes128-gcm@openssh.com,aes256-gcm@openssh.com .Ed .Pp The list of available ciphers may also be obtained using .Qq ssh -Q cipher . .It Cm ClearAllForwardings Specifies that all local, remote, and dynamic port forwardings specified in the configuration files or on the command line be cleared. This option is primarily useful when used from the .Xr ssh 1 command line to clear port forwardings set in configuration files, and is automatically set by .Xr scp 1 and .Xr sftp 1 . The argument must be .Cm yes or .Cm no (the default). .It Cm Compression Specifies whether to use compression. The argument must be .Cm yes or .Cm no (the default). .It Cm ConnectionAttempts Specifies the number of tries (one per second) to make before exiting. The argument must be an integer. This may be useful in scripts if the connection sometimes fails. The default is 1. .It Cm ConnectTimeout Specifies the timeout (in seconds) used when connecting to the SSH server, instead of using the default system TCP timeout. This timeout is applied both to establishing the connection and to performing the initial SSH protocol handshake and key exchange. .It Cm ControlMaster Enables the sharing of multiple sessions over a single network connection. When set to .Cm yes , .Xr ssh 1 will listen for connections on a control socket specified using the .Cm ControlPath argument. Additional sessions can connect to this socket using the same .Cm ControlPath with .Cm ControlMaster set to .Cm no (the default). These sessions will try to reuse the master instance's network connection rather than initiating new ones, but will fall back to connecting normally if the control socket does not exist, or is not listening. .Pp Setting this to .Cm ask will cause .Xr ssh 1 to listen for control connections, but require confirmation using .Xr ssh-askpass 1 . If the .Cm ControlPath cannot be opened, .Xr ssh 1 will continue without connecting to a master instance. .Pp X11 and .Xr ssh-agent 1 forwarding is supported over these multiplexed connections, however the display and agent forwarded will be the one belonging to the master connection i.e. it is not possible to forward multiple displays or agents. .Pp Two additional options allow for opportunistic multiplexing: try to use a master connection but fall back to creating a new one if one does not already exist. These options are: .Cm auto and .Cm autoask . The latter requires confirmation like the .Cm ask option. .It Cm ControlPath Specify the path to the control socket used for connection sharing as described in the .Cm ControlMaster section above or the string .Cm none to disable connection sharing. Arguments to .Cm ControlPath may use the tilde syntax to refer to a user's home directory, the tokens described in the .Sx TOKENS section and environment variables as described in the .Sx ENVIRONMENT VARIABLES section. It is recommended that any .Cm ControlPath used for opportunistic connection sharing include at least %h, %p, and %r (or alternatively %C) and be placed in a directory that is not writable by other users. This ensures that shared connections are uniquely identified. .It Cm ControlPersist When used in conjunction with .Cm ControlMaster , specifies that the master connection should remain open in the background (waiting for future client connections) after the initial client connection has been closed. If set to .Cm no (the default), then the master connection will not be placed into the background, and will close as soon as the initial client connection is closed. If set to .Cm yes or 0, then the master connection will remain in the background indefinitely (until killed or closed via a mechanism such as the .Qq ssh -O exit ) . If set to a time in seconds, or a time in any of the formats documented in .Xr sshd_config 5 , then the backgrounded master connection will automatically terminate after it has remained idle (with no client connections) for the specified time. .It Cm DynamicForward Specifies that a TCP port on the local machine be forwarded over the secure channel, and the application protocol is then used to determine where to connect to from the remote machine. .Pp The argument must be .Sm off .Oo Ar bind_address : Oc Ar port . .Sm on IPv6 addresses can be specified by enclosing addresses in square brackets. By default, the local port is bound in accordance with the .Cm GatewayPorts setting. However, an explicit .Ar bind_address may be used to bind the connection to a specific address. The .Ar bind_address of .Cm localhost indicates that the listening port be bound for local use only, while an empty address or .Sq * indicates that the port should be available from all interfaces. .Pp Currently the SOCKS4 and SOCKS5 protocols are supported, and .Xr ssh 1 will act as a SOCKS server. Multiple forwardings may be specified, and additional forwardings can be given on the command line. Only the superuser can forward privileged ports. .It Cm EnableEscapeCommandline Enables the command line option in the .Cm EscapeChar menu for interactive sessions (default .Ql ~C ) . By default, the command line is disabled. .It Cm EnableSSHKeysign Setting this option to .Cm yes in the global client configuration file .Pa /etc/ssh/ssh_config enables the use of the helper program .Xr ssh-keysign 8 during .Cm HostbasedAuthentication . The argument must be .Cm yes or .Cm no (the default). This option should be placed in the non-hostspecific section. See .Xr ssh-keysign 8 for more information. .It Cm EscapeChar Sets the escape character (default: .Ql ~ ) . The escape character can also be set on the command line. The argument should be a single character, .Ql ^ followed by a letter, or .Cm none to disable the escape character entirely (making the connection transparent for binary data). .It Cm ExitOnForwardFailure Specifies whether .Xr ssh 1 should terminate the connection if it cannot set up all requested dynamic, tunnel, local, and remote port forwardings, (e.g.\& if either end is unable to bind and listen on a specified port). Note that .Cm ExitOnForwardFailure does not apply to connections made over port forwardings and will not, for example, cause .Xr ssh 1 to exit if TCP connections to the ultimate forwarding destination fail. The argument must be .Cm yes or .Cm no (the default). .It Cm FingerprintHash Specifies the hash algorithm used when displaying key fingerprints. Valid options are: .Cm md5 and .Cm sha256 (the default). .It Cm ForkAfterAuthentication Requests .Nm ssh to go to background just before command execution. This is useful if .Nm ssh is going to ask for passwords or passphrases, but the user wants it in the background. This implies the .Cm StdinNull configuration option being set to .Dq yes . The recommended way to start X11 programs at a remote site is with something like .Ic ssh -f host xterm , which is the same as .Ic ssh host xterm if the .Cm ForkAfterAuthentication configuration option is set to .Dq yes . .Pp If the .Cm ExitOnForwardFailure configuration option is set to .Dq yes , then a client started with the .Cm ForkAfterAuthentication configuration option being set to .Dq yes will wait for all remote port forwards to be successfully established before placing itself in the background. The argument to this keyword must be .Cm yes (same as the .Fl f option) or .Cm no (the default). .It Cm ForwardAgent Specifies whether the connection to the authentication agent (if any) will be forwarded to the remote machine. The argument may be .Cm yes , .Cm no (the default), an explicit path to an agent socket or the name of an environment variable (beginning with .Sq $ ) in which to find the path. .Pp Agent forwarding should be enabled with caution. Users with the ability to bypass file permissions on the remote host (for the agent's Unix-domain socket) can access the local agent through the forwarded connection. An attacker cannot obtain key material from the agent, however they can perform operations on the keys that enable them to authenticate using the identities loaded into the agent. .It Cm ForwardX11 Specifies whether X11 connections will be automatically redirected over the secure channel and .Ev DISPLAY set. The argument must be .Cm yes or .Cm no (the default). .Pp X11 forwarding should be enabled with caution. Users with the ability to bypass file permissions on the remote host (for the user's X11 authorization database) can access the local X11 display through the forwarded connection. An attacker may then be able to perform activities such as keystroke monitoring if the .Cm ForwardX11Trusted option is also enabled. .It Cm ForwardX11Timeout Specify a timeout for untrusted X11 forwarding using the format described in the .Sx TIME FORMATS section of .Xr sshd_config 5 . X11 connections received by .Xr ssh 1 after this time will be refused. Setting .Cm ForwardX11Timeout to zero will disable the timeout and permit X11 forwarding for the life of the connection. The default is to disable untrusted X11 forwarding after twenty minutes has elapsed. .It Cm ForwardX11Trusted If this option is set to .Cm yes , remote X11 clients will have full access to the original X11 display. .Pp If this option is set to .Cm no (the default), remote X11 clients will be considered untrusted and prevented from stealing or tampering with data belonging to trusted X11 clients. Furthermore, the .Xr xauth 1 token used for the session will be set to expire after 20 minutes. Remote clients will be refused access after this time. .Pp See the X11 SECURITY extension specification for full details on the restrictions imposed on untrusted clients. .It Cm GatewayPorts Specifies whether remote hosts are allowed to connect to local forwarded ports. By default, .Xr ssh 1 binds local port forwardings to the loopback address. This prevents other remote hosts from connecting to forwarded ports. .Cm GatewayPorts can be used to specify that ssh should bind local port forwardings to the wildcard address, thus allowing remote hosts to connect to forwarded ports. The argument must be .Cm yes or .Cm no (the default). .It Cm GlobalKnownHostsFile Specifies one or more files to use for the global host key database, separated by whitespace. The default is .Pa /etc/ssh/ssh_known_hosts , .Pa /etc/ssh/ssh_known_hosts2 . .It Cm GSSAPIAuthentication Specifies whether user authentication based on GSSAPI is allowed. The default is .Cm no . .It Cm GSSAPIDelegateCredentials Forward (delegate) credentials to the server. The default is .Cm no . .It Cm HashKnownHosts Indicates that .Xr ssh 1 should hash host names and addresses when they are added to .Pa ~/.ssh/known_hosts . These hashed names may be used normally by .Xr ssh 1 and .Xr sshd 8 , but they do not visually reveal identifying information if the file's contents are disclosed. The default is .Cm no . Note that existing names and addresses in known hosts files will not be converted automatically, but may be manually hashed using .Xr ssh-keygen 1 . .It Cm HostbasedAcceptedAlgorithms Specifies the signature algorithms that will be used for hostbased authentication as a comma-separated list of patterns. Alternately if the specified list begins with a .Sq + character, then the specified signature algorithms will be appended to the default set instead of replacing them. If the specified list begins with a .Sq - character, then the specified signature algorithms (including wildcards) will be removed from the default set instead of replacing them. If the specified list begins with a .Sq ^ character, then the specified signature algorithms will be placed at the head of the default set. The default for this option is: .Bd -literal -offset 3n ssh-ed25519-cert-v01@openssh.com, ecdsa-sha2-nistp256-cert-v01@openssh.com, ecdsa-sha2-nistp384-cert-v01@openssh.com, ecdsa-sha2-nistp521-cert-v01@openssh.com, sk-ssh-ed25519-cert-v01@openssh.com, sk-ecdsa-sha2-nistp256-cert-v01@openssh.com, rsa-sha2-512-cert-v01@openssh.com, rsa-sha2-256-cert-v01@openssh.com, ssh-ed25519, ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521, sk-ssh-ed25519@openssh.com, sk-ecdsa-sha2-nistp256@openssh.com, rsa-sha2-512,rsa-sha2-256 .Ed .Pp The .Fl Q option of .Xr ssh 1 may be used to list supported signature algorithms. This was formerly named HostbasedKeyTypes. .It Cm HostbasedAuthentication Specifies whether to try rhosts based authentication with public key authentication. The argument must be .Cm yes or .Cm no (the default). .It Cm HostKeyAlgorithms Specifies the host key signature algorithms that the client wants to use in order of preference. Alternately if the specified list begins with a .Sq + character, then the specified signature algorithms will be appended to the default set instead of replacing them. If the specified list begins with a .Sq - character, then the specified signature algorithms (including wildcards) will be removed from the default set instead of replacing them. If the specified list begins with a .Sq ^ character, then the specified signature algorithms will be placed at the head of the default set. The default for this option is: .Bd -literal -offset 3n ssh-ed25519-cert-v01@openssh.com, ecdsa-sha2-nistp256-cert-v01@openssh.com, ecdsa-sha2-nistp384-cert-v01@openssh.com, ecdsa-sha2-nistp521-cert-v01@openssh.com, sk-ssh-ed25519-cert-v01@openssh.com, sk-ecdsa-sha2-nistp256-cert-v01@openssh.com, rsa-sha2-512-cert-v01@openssh.com, rsa-sha2-256-cert-v01@openssh.com, ssh-ed25519, ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521, sk-ecdsa-sha2-nistp256@openssh.com, sk-ssh-ed25519@openssh.com, rsa-sha2-512,rsa-sha2-256 .Ed .Pp If hostkeys are known for the destination host then this default is modified to prefer their algorithms. .Pp The list of available signature algorithms may also be obtained using .Qq ssh -Q HostKeyAlgorithms . .It Cm HostKeyAlias Specifies an alias that should be used instead of the real host name when looking up or saving the host key in the host key database files and when validating host certificates. This option is useful for tunneling SSH connections or for multiple servers running on a single host. .It Cm Hostname Specifies the real host name to log into. This can be used to specify nicknames or abbreviations for hosts. Arguments to .Cm Hostname accept the tokens described in the .Sx TOKENS section. Numeric IP addresses are also permitted (both on the command line and in .Cm Hostname specifications). The default is the name given on the command line. .It Cm IdentitiesOnly Specifies that .Xr ssh 1 should only use the configured authentication identity and certificate files (either the default files, or those explicitly configured in the .Nm files or passed on the .Xr ssh 1 command-line), even if .Xr ssh-agent 1 or a .Cm PKCS11Provider or .Cm SecurityKeyProvider offers more identities. The argument to this keyword must be .Cm yes or .Cm no (the default). This option is intended for situations where ssh-agent offers many different identities. .It Cm IdentityAgent Specifies the .Ux Ns -domain socket used to communicate with the authentication agent. .Pp This option overrides the .Ev SSH_AUTH_SOCK environment variable and can be used to select a specific agent. Setting the socket name to .Cm none disables the use of an authentication agent. If the string .Qq SSH_AUTH_SOCK is specified, the location of the socket will be read from the .Ev SSH_AUTH_SOCK environment variable. Otherwise if the specified value begins with a .Sq $ character, then it will be treated as an environment variable containing the location of the socket. .Pp Arguments to .Cm IdentityAgent may use the tilde syntax to refer to a user's home directory, the tokens described in the .Sx TOKENS section and environment variables as described in the .Sx ENVIRONMENT VARIABLES section. .It Cm IdentityFile Specifies a file from which the user's DSA, ECDSA, authenticator-hosted ECDSA, Ed25519, authenticator-hosted Ed25519 or RSA authentication identity is read. You can also specify a public key file to use the corresponding private key that is loaded in .Xr ssh-agent 1 when the private key file is not present locally. The default is .Pa ~/.ssh/id_rsa , .Pa ~/.ssh/id_ecdsa , .Pa ~/.ssh/id_ecdsa_sk , .Pa ~/.ssh/id_ed25519 , .Pa ~/.ssh/id_ed25519_sk and .Pa ~/.ssh/id_dsa . Additionally, any identities represented by the authentication agent will be used for authentication unless .Cm IdentitiesOnly is set. If no certificates have been explicitly specified by .Cm CertificateFile , .Xr ssh 1 will try to load certificate information from the filename obtained by appending .Pa -cert.pub to the path of a specified .Cm IdentityFile . .Pp Arguments to .Cm IdentityFile may use the tilde syntax to refer to a user's home directory or the tokens described in the .Sx TOKENS section. .Pp It is possible to have multiple identity files specified in configuration files; all these identities will be tried in sequence. Multiple .Cm IdentityFile directives will add to the list of identities tried (this behaviour differs from that of other configuration directives). .Pp .Cm IdentityFile may be used in conjunction with .Cm IdentitiesOnly to select which identities in an agent are offered during authentication. .Cm IdentityFile may also be used in conjunction with .Cm CertificateFile in order to provide any certificate also needed for authentication with the identity. .It Cm IgnoreUnknown Specifies a pattern-list of unknown options to be ignored if they are encountered in configuration parsing. This may be used to suppress errors if .Nm contains options that are unrecognised by .Xr ssh 1 . It is recommended that .Cm IgnoreUnknown be listed early in the configuration file as it will not be applied to unknown options that appear before it. .It Cm Include Include the specified configuration file(s). Multiple pathnames may be specified and each pathname may contain .Xr glob 7 wildcards and, for user configurations, shell-like .Sq ~ references to user home directories. Wildcards will be expanded and processed in lexical order. Files without absolute paths are assumed to be in .Pa ~/.ssh if included in a user configuration file or .Pa /etc/ssh if included from the system configuration file. .Cm Include directive may appear inside a .Cm Match or .Cm Host block to perform conditional inclusion. .It Cm IPQoS Specifies the IPv4 type-of-service or DSCP class for connections. Accepted values are .Cm af11 , .Cm af12 , .Cm af13 , .Cm af21 , .Cm af22 , .Cm af23 , .Cm af31 , .Cm af32 , .Cm af33 , .Cm af41 , .Cm af42 , .Cm af43 , .Cm cs0 , .Cm cs1 , .Cm cs2 , .Cm cs3 , .Cm cs4 , .Cm cs5 , .Cm cs6 , .Cm cs7 , .Cm ef , .Cm le , .Cm lowdelay , .Cm throughput , .Cm reliability , a numeric value, or .Cm none to use the operating system default. This option may take one or two arguments, separated by whitespace. If one argument is specified, it is used as the packet class unconditionally. If two values are specified, the first is automatically selected for interactive sessions and the second for non-interactive sessions. The default is .Cm af21 (Low-Latency Data) for interactive sessions and .Cm cs1 (Lower Effort) for non-interactive sessions. .It Cm KbdInteractiveAuthentication Specifies whether to use keyboard-interactive authentication. The argument to this keyword must be .Cm yes (the default) or .Cm no . .Cm ChallengeResponseAuthentication is a deprecated alias for this. .It Cm KbdInteractiveDevices Specifies the list of methods to use in keyboard-interactive authentication. Multiple method names must be comma-separated. The default is to use the server specified list. The methods available vary depending on what the server supports. For an OpenSSH server, it may be zero or more of: .Cm bsdauth and .Cm pam . .It Cm KexAlgorithms Specifies the available KEX (Key Exchange) algorithms. Multiple algorithms must be comma-separated. If the specified list begins with a .Sq + character, then the specified algorithms will be appended to the default set instead of replacing them. If the specified list begins with a .Sq - character, then the specified algorithms (including wildcards) will be removed from the default set instead of replacing them. If the specified list begins with a .Sq ^ character, then the specified algorithms will be placed at the head of the default set. The default is: .Bd -literal -offset indent sntrup761x25519-sha512@openssh.com, curve25519-sha256,curve25519-sha256@libssh.org, ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521, diffie-hellman-group-exchange-sha256, diffie-hellman-group16-sha512, diffie-hellman-group18-sha512, diffie-hellman-group14-sha256 .Ed .Pp The list of available key exchange algorithms may also be obtained using .Qq ssh -Q kex . .It Cm KnownHostsCommand Specifies a command to use to obtain a list of host keys, in addition to those listed in .Cm UserKnownHostsFile and .Cm GlobalKnownHostsFile . This command is executed after the files have been read. It may write host key lines to standard output in identical format to the usual files (described in the .Sx VERIFYING HOST KEYS section in .Xr ssh 1 ) . Arguments to .Cm KnownHostsCommand accept the tokens described in the .Sx TOKENS section. The command may be invoked multiple times per connection: once when preparing the preference list of host key algorithms to use, again to obtain the host key for the requested host name and, if .Cm CheckHostIP is enabled, one more time to obtain the host key matching the server's address. If the command exits abnormally or returns a non-zero exit status then the connection is terminated. .It Cm LocalCommand Specifies a command to execute on the local machine after successfully connecting to the server. The command string extends to the end of the line, and is executed with the user's shell. Arguments to .Cm LocalCommand accept the tokens described in the .Sx TOKENS section. .Pp The command is run synchronously and does not have access to the session of the .Xr ssh 1 that spawned it. It should not be used for interactive commands. .Pp This directive is ignored unless .Cm PermitLocalCommand has been enabled. .It Cm LocalForward Specifies that a TCP port on the local machine be forwarded over the secure channel to the specified host and port from the remote machine. The first argument specifies the listener and may be .Sm off .Oo Ar bind_address : Oc Ar port .Sm on or a Unix domain socket path. The second argument is the destination and may be .Ar host : Ns Ar hostport or a Unix domain socket path if the remote host supports it. .Pp IPv6 addresses can be specified by enclosing addresses in square brackets. Multiple forwardings may be specified, and additional forwardings can be given on the command line. Only the superuser can forward privileged ports. By default, the local port is bound in accordance with the .Cm GatewayPorts setting. However, an explicit .Ar bind_address may be used to bind the connection to a specific address. The .Ar bind_address of .Cm localhost indicates that the listening port be bound for local use only, while an empty address or .Sq * indicates that the port should be available from all interfaces. Unix domain socket paths may use the tokens described in the .Sx TOKENS section and environment variables as described in the .Sx ENVIRONMENT VARIABLES section. .It Cm LogLevel Gives the verbosity level that is used when logging messages from .Xr ssh 1 . The possible values are: QUIET, FATAL, ERROR, INFO, VERBOSE, DEBUG, DEBUG1, DEBUG2, and DEBUG3. The default is INFO. DEBUG and DEBUG1 are equivalent. DEBUG2 and DEBUG3 each specify higher levels of verbose output. .It Cm LogVerbose Specify one or more overrides to LogLevel. An override consists of a pattern lists that matches the source file, function and line number to force detailed logging for. For example, an override pattern of: .Bd -literal -offset indent kex.c:*:1000,*:kex_exchange_identification():*,packet.c:* .Ed .Pp would enable detailed logging for line 1000 of .Pa kex.c , everything in the .Fn kex_exchange_identification function, and all code in the .Pa packet.c file. This option is intended for debugging and no overrides are enabled by default. .It Cm MACs Specifies the MAC (message authentication code) algorithms in order of preference. The MAC algorithm is used for data integrity protection. Multiple algorithms must be comma-separated. If the specified list begins with a .Sq + character, then the specified algorithms will be appended to the default set instead of replacing them. If the specified list begins with a .Sq - character, then the specified algorithms (including wildcards) will be removed from the default set instead of replacing them. If the specified list begins with a .Sq ^ character, then the specified algorithms will be placed at the head of the default set. .Pp The algorithms that contain .Qq -etm calculate the MAC after encryption (encrypt-then-mac). These are considered safer and their use recommended. .Pp The default is: .Bd -literal -offset indent umac-64-etm@openssh.com,umac-128-etm@openssh.com, hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com, hmac-sha1-etm@openssh.com, umac-64@openssh.com,umac-128@openssh.com, hmac-sha2-256,hmac-sha2-512,hmac-sha1 .Ed .Pp The list of available MAC algorithms may also be obtained using .Qq ssh -Q mac . .It Cm NoHostAuthenticationForLocalhost Disable host authentication for localhost (loopback addresses). The argument to this keyword must be .Cm yes or .Cm no (the default). .It Cm NumberOfPasswordPrompts Specifies the number of password prompts before giving up. The argument to this keyword must be an integer. The default is 3. +.It Cm ObscureKeystrokeTiming +Specifies whether +.Xr ssh 1 +should try to obscure inter-keystroke timings from passive observers of +network traffic. +If enabled, then for interactive sessions, +.Xr ssh 1 +will send keystrokes at fixed intervals of a few tens of milliseconds +and will send fake keystroke packets for some time after typing ceases. +The argument to this keyword must be +.Cm yes , +.Cm no +or an interval specifier of the form +.Cm interval:milliseconds +(e.g.\& +.Cm interval:80 +for 80 milliseconds). +The default is to obscure keystrokes using a 20ms packet interval. +Note that smaller intervals will result in higher fake keystroke packet rates. .It Cm PasswordAuthentication Specifies whether to use password authentication. The argument to this keyword must be .Cm yes (the default) or .Cm no . .It Cm PermitLocalCommand Allow local command execution via the .Ic LocalCommand option or using the .Ic !\& Ns Ar command escape sequence in .Xr ssh 1 . The argument must be .Cm yes or .Cm no (the default). .It Cm PermitRemoteOpen Specifies the destinations to which remote TCP port forwarding is permitted when .Cm RemoteForward is used as a SOCKS proxy. The forwarding specification must be one of the following forms: .Pp .Bl -item -offset indent -compact .It .Cm PermitRemoteOpen .Sm off .Ar host : port .Sm on .It .Cm PermitRemoteOpen .Sm off .Ar IPv4_addr : port .Sm on .It .Cm PermitRemoteOpen .Sm off .Ar \&[ IPv6_addr \&] : port .Sm on .El .Pp Multiple forwards may be specified by separating them with whitespace. An argument of .Cm any can be used to remove all restrictions and permit any forwarding requests. An argument of .Cm none can be used to prohibit all forwarding requests. The wildcard .Sq * can be used for host or port to allow all hosts or ports respectively. Otherwise, no pattern matching or address lookups are performed on supplied names. .It Cm PKCS11Provider Specifies which PKCS#11 provider to use or .Cm none to indicate that no provider should be used (the default). The argument to this keyword is a path to the PKCS#11 shared library .Xr ssh 1 should use to communicate with a PKCS#11 token providing keys for user authentication. .It Cm Port Specifies the port number to connect on the remote host. The default is 22. .It Cm PreferredAuthentications Specifies the order in which the client should try authentication methods. This allows a client to prefer one method (e.g.\& .Cm keyboard-interactive ) over another method (e.g.\& .Cm password ) . The default is: .Bd -literal -offset indent gssapi-with-mic,hostbased,publickey, keyboard-interactive,password .Ed .It Cm ProxyCommand Specifies the command to use to connect to the server. The command string extends to the end of the line, and is executed using the user's shell .Ql exec directive to avoid a lingering shell process. .Pp Arguments to .Cm ProxyCommand accept the tokens described in the .Sx TOKENS section. The command can be basically anything, and should read from its standard input and write to its standard output. It should eventually connect an .Xr sshd 8 server running on some machine, or execute .Ic sshd -i somewhere. Host key management will be done using the .Cm Hostname of the host being connected (defaulting to the name typed by the user). Setting the command to .Cm none disables this option entirely. Note that .Cm CheckHostIP is not available for connects with a proxy command. .Pp This directive is useful in conjunction with .Xr nc 1 and its proxy support. For example, the following directive would connect via an HTTP proxy at 192.0.2.0: .Bd -literal -offset 3n ProxyCommand /usr/bin/nc -X connect -x 192.0.2.0:8080 %h %p .Ed .It Cm ProxyJump Specifies one or more jump proxies as either .Xo .Sm off .Op Ar user No @ .Ar host .Op : Ns Ar port .Sm on or an ssh URI .Xc . Multiple proxies may be separated by comma characters and will be visited sequentially. Setting this option will cause .Xr ssh 1 to connect to the target host by first making a .Xr ssh 1 connection to the specified .Cm ProxyJump host and then establishing a TCP forwarding to the ultimate target from there. Setting the host to .Cm none disables this option entirely. .Pp Note that this option will compete with the .Cm ProxyCommand option - whichever is specified first will prevent later instances of the other from taking effect. .Pp Note also that the configuration for the destination host (either supplied via the command-line or the configuration file) is not generally applied to jump hosts. .Pa ~/.ssh/config should be used if specific configuration is required for jump hosts. .It Cm ProxyUseFdpass Specifies that .Cm ProxyCommand will pass a connected file descriptor back to .Xr ssh 1 instead of continuing to execute and pass data. The default is .Cm no . .It Cm PubkeyAcceptedAlgorithms Specifies the signature algorithms that will be used for public key authentication as a comma-separated list of patterns. If the specified list begins with a .Sq + character, then the algorithms after it will be appended to the default instead of replacing it. If the specified list begins with a .Sq - character, then the specified algorithms (including wildcards) will be removed from the default set instead of replacing them. If the specified list begins with a .Sq ^ character, then the specified algorithms will be placed at the head of the default set. The default for this option is: .Bd -literal -offset 3n ssh-ed25519-cert-v01@openssh.com, ecdsa-sha2-nistp256-cert-v01@openssh.com, ecdsa-sha2-nistp384-cert-v01@openssh.com, ecdsa-sha2-nistp521-cert-v01@openssh.com, sk-ssh-ed25519-cert-v01@openssh.com, sk-ecdsa-sha2-nistp256-cert-v01@openssh.com, rsa-sha2-512-cert-v01@openssh.com, rsa-sha2-256-cert-v01@openssh.com, ssh-ed25519, ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521, sk-ssh-ed25519@openssh.com, sk-ecdsa-sha2-nistp256@openssh.com, rsa-sha2-512,rsa-sha2-256 .Ed .Pp The list of available signature algorithms may also be obtained using .Qq ssh -Q PubkeyAcceptedAlgorithms . .It Cm PubkeyAuthentication Specifies whether to try public key authentication. The argument to this keyword must be .Cm yes (the default), .Cm no , .Cm unbound or .Cm host-bound . The final two options enable public key authentication while respectively disabling or enabling the OpenSSH host-bound authentication protocol extension required for restricted .Xr ssh-agent 1 forwarding. .It Cm RekeyLimit Specifies the maximum amount of data that may be transmitted or received before the session key is renegotiated, optionally followed by a maximum amount of time that may pass before the session key is renegotiated. The first argument is specified in bytes and may have a suffix of .Sq K , .Sq M , or .Sq G to indicate Kilobytes, Megabytes, or Gigabytes, respectively. The default is between .Sq 1G and .Sq 4G , depending on the cipher. The optional second value is specified in seconds and may use any of the units documented in the TIME FORMATS section of .Xr sshd_config 5 . The default value for .Cm RekeyLimit is .Cm default none , which means that rekeying is performed after the cipher's default amount of data has been sent or received and no time based rekeying is done. .It Cm RemoteCommand Specifies a command to execute on the remote machine after successfully connecting to the server. The command string extends to the end of the line, and is executed with the user's shell. Arguments to .Cm RemoteCommand accept the tokens described in the .Sx TOKENS section. .It Cm RemoteForward Specifies that a TCP port on the remote machine be forwarded over the secure channel. The remote port may either be forwarded to a specified host and port from the local machine, or may act as a SOCKS 4/5 proxy that allows a remote client to connect to arbitrary destinations from the local machine. The first argument is the listening specification and may be .Sm off .Oo Ar bind_address : Oc Ar port .Sm on or, if the remote host supports it, a Unix domain socket path. If forwarding to a specific destination then the second argument must be .Ar host : Ns Ar hostport or a Unix domain socket path, otherwise if no destination argument is specified then the remote forwarding will be established as a SOCKS proxy. When acting as a SOCKS proxy, the destination of the connection can be restricted by .Cm PermitRemoteOpen . .Pp IPv6 addresses can be specified by enclosing addresses in square brackets. Multiple forwardings may be specified, and additional forwardings can be given on the command line. Privileged ports can be forwarded only when logging in as root on the remote machine. Unix domain socket paths may use the tokens described in the .Sx TOKENS section and environment variables as described in the .Sx ENVIRONMENT VARIABLES section. .Pp If the .Ar port argument is 0, the listen port will be dynamically allocated on the server and reported to the client at run time. .Pp If the .Ar bind_address is not specified, the default is to only bind to loopback addresses. If the .Ar bind_address is .Ql * or an empty string, then the forwarding is requested to listen on all interfaces. Specifying a remote .Ar bind_address will only succeed if the server's .Cm GatewayPorts option is enabled (see .Xr sshd_config 5 ) . .It Cm RequestTTY Specifies whether to request a pseudo-tty for the session. The argument may be one of: .Cm no (never request a TTY), .Cm yes (always request a TTY when standard input is a TTY), .Cm force (always request a TTY) or .Cm auto (request a TTY when opening a login session). This option mirrors the .Fl t and .Fl T flags for .Xr ssh 1 . .It Cm RequiredRSASize Specifies the minimum RSA key size (in bits) that .Xr ssh 1 will accept. User authentication keys smaller than this limit will be ignored. Servers that present host keys smaller than this limit will cause the connection to be terminated. The default is .Cm 1024 bits. Note that this limit may only be raised from the default. .It Cm RevokedHostKeys Specifies revoked host public keys. Keys listed in this file will be refused for host authentication. Note that if this file does not exist or is not readable, then host authentication will be refused for all hosts. Keys may be specified as a text file, listing one public key per line, or as an OpenSSH Key Revocation List (KRL) as generated by .Xr ssh-keygen 1 . For more information on KRLs, see the KEY REVOCATION LISTS section in .Xr ssh-keygen 1 . Arguments to .Cm RevokedHostKeys may use the tilde syntax to refer to a user's home directory, the tokens described in the .Sx TOKENS section and environment variables as described in the .Sx ENVIRONMENT VARIABLES section. .It Cm SecurityKeyProvider Specifies a path to a library that will be used when loading any FIDO authenticator-hosted keys, overriding the default of using the built-in USB HID support. .Pp If the specified value begins with a .Sq $ character, then it will be treated as an environment variable containing the path to the library. .It Cm SendEnv Specifies what variables from the local .Xr environ 7 should be sent to the server. The server must also support it, and the server must be configured to accept these environment variables. Note that the .Ev TERM environment variable is always sent whenever a pseudo-terminal is requested as it is required by the protocol. Refer to .Cm AcceptEnv in .Xr sshd_config 5 for how to configure the server. Variables are specified by name, which may contain wildcard characters. Multiple environment variables may be separated by whitespace or spread across multiple .Cm SendEnv directives. .Pp See .Sx PATTERNS for more information on patterns. .Pp It is possible to clear previously set .Cm SendEnv variable names by prefixing patterns with .Pa - . The default is not to send any environment variables. .It Cm ServerAliveCountMax Sets the number of server alive messages (see below) which may be sent without .Xr ssh 1 receiving any messages back from the server. If this threshold is reached while server alive messages are being sent, ssh will disconnect from the server, terminating the session. It is important to note that the use of server alive messages is very different from .Cm TCPKeepAlive (below). The server alive messages are sent through the encrypted channel and therefore will not be spoofable. The TCP keepalive option enabled by .Cm TCPKeepAlive is spoofable. The server alive mechanism is valuable when the client or server depend on knowing when a connection has become unresponsive. .Pp The default value is 3. If, for example, .Cm ServerAliveInterval (see below) is set to 15 and .Cm ServerAliveCountMax is left at the default, if the server becomes unresponsive, ssh will disconnect after approximately 45 seconds. .It Cm ServerAliveInterval Sets a timeout interval in seconds after which if no data has been received from the server, .Xr ssh 1 will send a message through the encrypted channel to request a response from the server. The default is 0, indicating that these messages will not be sent to the server. .It Cm SessionType May be used to either request invocation of a subsystem on the remote system, or to prevent the execution of a remote command at all. The latter is useful for just forwarding ports. The argument to this keyword must be .Cm none (same as the .Fl N option), .Cm subsystem (same as the .Fl s option) or .Cm default (shell or command execution). .It Cm SetEnv Directly specify one or more environment variables and their contents to be sent to the server. Similarly to .Cm SendEnv , with the exception of the .Ev TERM variable, the server must be prepared to accept the environment variable. .It Cm StdinNull Redirects stdin from .Pa /dev/null (actually, prevents reading from stdin). Either this or the equivalent .Fl n option must be used when .Nm ssh is run in the background. The argument to this keyword must be .Cm yes (same as the .Fl n option) or .Cm no (the default). .It Cm StreamLocalBindMask Sets the octal file creation mode mask .Pq umask used when creating a Unix-domain socket file for local or remote port forwarding. This option is only used for port forwarding to a Unix-domain socket file. .Pp The default value is 0177, which creates a Unix-domain socket file that is readable and writable only by the owner. Note that not all operating systems honor the file mode on Unix-domain socket files. .It Cm StreamLocalBindUnlink Specifies whether to remove an existing Unix-domain socket file for local or remote port forwarding before creating a new one. If the socket file already exists and .Cm StreamLocalBindUnlink is not enabled, .Nm ssh will be unable to forward the port to the Unix-domain socket file. This option is only used for port forwarding to a Unix-domain socket file. .Pp The argument must be .Cm yes or .Cm no (the default). .It Cm StrictHostKeyChecking If this flag is set to .Cm yes , .Xr ssh 1 will never automatically add host keys to the .Pa ~/.ssh/known_hosts file, and refuses to connect to hosts whose host key has changed. This provides maximum protection against man-in-the-middle (MITM) attacks, though it can be annoying when the .Pa /etc/ssh/ssh_known_hosts file is poorly maintained or when connections to new hosts are frequently made. This option forces the user to manually add all new hosts. .Pp If this flag is set to .Cm accept-new then ssh will automatically add new host keys to the user's .Pa known_hosts file, but will not permit connections to hosts with changed host keys. If this flag is set to .Cm no or .Cm off , ssh will automatically add new host keys to the user known hosts files and allow connections to hosts with changed hostkeys to proceed, subject to some restrictions. If this flag is set to .Cm ask (the default), new host keys will be added to the user known host files only after the user has confirmed that is what they really want to do, and ssh will refuse to connect to hosts whose host key has changed. The host keys of known hosts will be verified automatically in all cases. .It Cm SyslogFacility Gives the facility code that is used when logging messages from .Xr ssh 1 . The possible values are: DAEMON, USER, AUTH, LOCAL0, LOCAL1, LOCAL2, LOCAL3, LOCAL4, LOCAL5, LOCAL6, LOCAL7. The default is USER. .It Cm TCPKeepAlive Specifies whether the system should send TCP keepalive messages to the other side. If they are sent, death of the connection or crash of one of the machines will be properly noticed. However, this means that connections will die if the route is down temporarily, and some people find it annoying. .Pp The default is .Cm yes (to send TCP keepalive messages), and the client will notice if the network goes down or the remote host dies. This is important in scripts, and many users want it too. .Pp To disable TCP keepalive messages, the value should be set to .Cm no . See also .Cm ServerAliveInterval for protocol-level keepalives. .It Cm Tag Specify a configuration tag name that may be later used by a .Cm Match directive to select a block of configuration. .It Cm Tunnel Request .Xr tun 4 device forwarding between the client and the server. The argument must be .Cm yes , .Cm point-to-point (layer 3), .Cm ethernet (layer 2), or .Cm no (the default). Specifying .Cm yes requests the default tunnel mode, which is .Cm point-to-point . .It Cm TunnelDevice Specifies the .Xr tun 4 devices to open on the client .Pq Ar local_tun and the server .Pq Ar remote_tun . .Pp The argument must be .Sm off .Ar local_tun Op : Ar remote_tun . .Sm on The devices may be specified by numerical ID or the keyword .Cm any , which uses the next available tunnel device. If .Ar remote_tun is not specified, it defaults to .Cm any . The default is .Cm any:any . .It Cm UpdateHostKeys Specifies whether .Xr ssh 1 should accept notifications of additional hostkeys from the server sent after authentication has completed and add them to .Cm UserKnownHostsFile . The argument must be .Cm yes , .Cm no or .Cm ask . This option allows learning alternate hostkeys for a server and supports graceful key rotation by allowing a server to send replacement public keys before old ones are removed. .Pp Additional hostkeys are only accepted if the key used to authenticate the host was already trusted or explicitly accepted by the user, the host was authenticated via .Cm UserKnownHostsFile (i.e. not .Cm GlobalKnownHostsFile ) and the host was authenticated using a plain key and not a certificate. .Pp .Cm UpdateHostKeys is enabled by default if the user has not overridden the default .Cm UserKnownHostsFile setting and has not enabled .Cm VerifyHostKeyDNS , otherwise .Cm UpdateHostKeys will be set to .Cm no . .Pp If .Cm UpdateHostKeys is set to .Cm ask , then the user is asked to confirm the modifications to the known_hosts file. Confirmation is currently incompatible with .Cm ControlPersist , and will be disabled if it is enabled. .Pp Presently, only .Xr sshd 8 from OpenSSH 6.8 and greater support the .Qq hostkeys@openssh.com protocol extension used to inform the client of all the server's hostkeys. .It Cm User Specifies the user to log in as. This can be useful when a different user name is used on different machines. This saves the trouble of having to remember to give the user name on the command line. .It Cm UserKnownHostsFile Specifies one or more files to use for the user host key database, separated by whitespace. Each filename may use tilde notation to refer to the user's home directory, the tokens described in the .Sx TOKENS section and environment variables as described in the .Sx ENVIRONMENT VARIABLES section. A value of .Cm none causes .Xr ssh 1 to ignore any user-specific known hosts files. The default is .Pa ~/.ssh/known_hosts , .Pa ~/.ssh/known_hosts2 . .It Cm VerifyHostKeyDNS Specifies whether to verify the remote key using DNS and SSHFP resource records. If this option is set to .Cm yes , the client will implicitly trust keys that match a secure fingerprint from DNS. Insecure fingerprints will be handled as if this option was set to .Cm ask . If this option is set to .Cm ask , information on fingerprint match will be displayed, but the user will still need to confirm new host keys according to the .Cm StrictHostKeyChecking option. The default is .Cm no . .Pp See also .Sx VERIFYING HOST KEYS in .Xr ssh 1 . .It Cm VisualHostKey If this flag is set to .Cm yes , an ASCII art representation of the remote host key fingerprint is printed in addition to the fingerprint string at login and for unknown host keys. If this flag is set to .Cm no (the default), no fingerprint strings are printed at login and only the fingerprint string will be printed for unknown host keys. .It Cm XAuthLocation Specifies the full pathname of the .Xr xauth 1 program. The default is .Pa /usr/X11R6/bin/xauth . .El .Sh PATTERNS A .Em pattern consists of zero or more non-whitespace characters, .Sq * (a wildcard that matches zero or more characters), or .Sq ?\& (a wildcard that matches exactly one character). For example, to specify a set of declarations for any host in the .Qq .co.uk set of domains, the following pattern could be used: .Pp .Dl Host *.co.uk .Pp The following pattern would match any host in the 192.168.0.[0-9] network range: .Pp .Dl Host 192.168.0.? .Pp A .Em pattern-list is a comma-separated list of patterns. Patterns within pattern-lists may be negated by preceding them with an exclamation mark .Pq Sq !\& . For example, to allow a key to be used from anywhere within an organization except from the .Qq dialup pool, the following entry (in authorized_keys) could be used: .Pp .Dl from=\&"!*.dialup.example.com,*.example.com\&" .Pp Note that a negated match will never produce a positive result by itself. For example, attempting to match .Qq host3 against the following pattern-list will fail: .Pp .Dl from=\&"!host1,!host2\&" .Pp The solution here is to include a term that will yield a positive match, such as a wildcard: .Pp .Dl from=\&"!host1,!host2,*\&" .Sh TOKENS Arguments to some keywords can make use of tokens, which are expanded at runtime: .Pp .Bl -tag -width XXXX -offset indent -compact .It %% A literal .Sq % . .It \&%C Hash of %l%h%p%r. .It %d Local user's home directory. .It %f The fingerprint of the server's host key. .It %H The .Pa known_hosts hostname or address that is being searched for. .It %h The remote hostname. .It \%%I A string describing the reason for a .Cm KnownHostsCommand execution: either .Cm ADDRESS when looking up a host by address (only when .Cm CheckHostIP is enabled), .Cm HOSTNAME when searching by hostname, or .Cm ORDER when preparing the host key algorithm preference list to use for the destination host. .It %i The local user ID. .It %K The base64 encoded host key. .It %k The host key alias if specified, otherwise the original remote hostname given on the command line. .It %L The local hostname. .It %l The local hostname, including the domain name. .It %n The original remote hostname, as given on the command line. .It %p The remote port. .It %r The remote username. .It \&%T The local .Xr tun 4 or .Xr tap 4 network interface assigned if tunnel forwarding was requested, or .Qq NONE otherwise. .It %t The type of the server host key, e.g. .Cm ssh-ed25519 . .It %u The local username. .El .Pp .Cm CertificateFile , .Cm ControlPath , .Cm IdentityAgent , .Cm IdentityFile , .Cm KnownHostsCommand , .Cm LocalForward , .Cm Match exec , .Cm RemoteCommand , .Cm RemoteForward , .Cm RevokedHostKeys , and .Cm UserKnownHostsFile accept the tokens %%, %C, %d, %h, %i, %k, %L, %l, %n, %p, %r, and %u. .Pp .Cm KnownHostsCommand additionally accepts the tokens %f, %H, %I, %K and %t. .Pp .Cm Hostname accepts the tokens %% and %h. .Pp .Cm LocalCommand accepts all tokens. .Pp .Cm ProxyCommand and .Cm ProxyJump accept the tokens %%, %h, %n, %p, and %r. +.Pp +Note that some of these directives build commands for execution via the shell. +Because +.Xr ssh 1 +performs no filtering or escaping of characters that have special meaning in +shell commands (e.g. quotes), it is the user's reposibility to ensure that +the arguments passed to +.Xr ssh 1 +do not contain such characters and that tokens are appropriately quoted +when used. .Sh ENVIRONMENT VARIABLES Arguments to some keywords can be expanded at runtime from environment variables on the client by enclosing them in .Ic ${} , for example .Ic ${HOME}/.ssh would refer to the user's .ssh directory. If a specified environment variable does not exist then an error will be returned and the setting for that keyword will be ignored. .Pp The keywords .Cm CertificateFile , .Cm ControlPath , .Cm IdentityAgent , .Cm IdentityFile , .Cm KnownHostsCommand , and .Cm UserKnownHostsFile support environment variables. The keywords .Cm LocalForward and .Cm RemoteForward support environment variables only for Unix domain socket paths. .Sh FILES .Bl -tag -width Ds .It Pa ~/.ssh/config This is the per-user configuration file. The format of this file is described above. This file is used by the SSH client. Because of the potential for abuse, this file must have strict permissions: read/write for the user, and not writable by others. .It Pa /etc/ssh/ssh_config Systemwide configuration file. This file provides defaults for those values that are not specified in the user's configuration file, and for those users who do not have a configuration file. This file must be world-readable. .El .Sh SEE ALSO .Xr ssh 1 .Sh AUTHORS .An -nosplit OpenSSH is a derivative of the original and free ssh 1.2.12 release by .An Tatu Ylonen . .An Aaron Campbell , Bob Beck , Markus Friedl , .An Niels Provos , Theo de Raadt and .An Dug Song removed many bugs, re-added newer features and created OpenSSH. .An Markus Friedl contributed the support for SSH protocol versions 1.5 and 2.0. diff --git a/sshd.0 b/sshd.0 index c048037105ea..98855e8d129d 100644 --- a/sshd.0 +++ b/sshd.0 @@ -1,686 +1,686 @@ SSHD(8) System Manager's Manual SSHD(8) NAME sshd M-bM-^@M-^S OpenSSH daemon SYNOPSIS sshd [-46DdeGiqTtV] [-C connection_spec] [-c host_certificate_file] [-E log_file] [-f config_file] [-g login_grace_time] [-h host_key_file] [-o option] [-p port] [-u len] DESCRIPTION sshd (OpenSSH Daemon) is the daemon program for ssh(1). It provides secure encrypted communications between two untrusted hosts over an insecure network. sshd listens for connections from clients. It is normally started at boot from /etc/rc. It forks a new daemon for each incoming connection. The forked daemons handle key exchange, encryption, authentication, command execution, and data exchange. sshd can be configured using command-line options or a configuration file (by default sshd_config(5)); command-line options override values specified in the configuration file. sshd rereads its configuration file when it receives a hangup signal, SIGHUP, by executing itself with the name and options it was started with, e.g. /usr/sbin/sshd. The options are as follows: -4 Forces sshd to use IPv4 addresses only. -6 Forces sshd to use IPv6 addresses only. -C connection_spec Specify the connection parameters to use for the -T extended test mode. If provided, any Match directives in the configuration file that would apply are applied before the configuration is written to standard output. The connection parameters are supplied as keyword=value pairs and may be supplied in any order, either with multiple -C options or as a comma-separated list. The keywords are M-bM-^@M-^\addrM-bM-^@M-^], M-bM-^@M-^\userM-bM-^@M-^], M-bM-^@M-^\hostM-bM-^@M-^], M-bM-^@M-^\laddrM-bM-^@M-^], M-bM-^@M-^\lportM-bM-^@M-^], and M-bM-^@M-^\rdomainM-bM-^@M-^] and correspond to source address, user, resolved source host name, local address, local port number and routing domain respectively. -c host_certificate_file Specifies a path to a certificate file to identify sshd during key exchange. The certificate file must match a host key file specified using the -h option or the HostKey configuration directive. -D When this option is specified, sshd will not detach and does not become a daemon. This allows easy monitoring of sshd. -d Debug mode. The server sends verbose debug output to standard error, and does not put itself in the background. The server also will not fork(2) and will only process one connection. This option is only intended for debugging for the server. Multiple -d options increase the debugging level. Maximum is 3. -E log_file Append debug logs to log_file instead of the system log. -e Write debug logs to standard error instead of the system log. -f config_file Specifies the name of the configuration file. The default is /etc/ssh/sshd_config. sshd refuses to start if there is no configuration file. -G Parse and print configuration file. Check the validity of the configuration file, output the effective configuration to stdout and then exit. Optionally, Match rules may be applied by specifying the connection parameters using one or more -C options. -g login_grace_time Gives the grace time for clients to authenticate themselves (default 120 seconds). If the client fails to authenticate the user within this many seconds, the server disconnects and exits. A value of zero indicates no limit. -h host_key_file Specifies a file from which a host key is read. This option must be given if sshd is not run as root (as the normal host key files are normally not readable by anyone but root). The default is /etc/ssh/ssh_host_ecdsa_key, /etc/ssh/ssh_host_ed25519_key and /etc/ssh/ssh_host_rsa_key. It is possible to have multiple host key files for the different host key algorithms. -i Specifies that sshd is being run from inetd(8). -o option Can be used to give options in the format used in the configuration file. This is useful for specifying options for which there is no separate command-line flag. For full details of the options, and their values, see sshd_config(5). -p port Specifies the port on which the server listens for connections (default 22). Multiple port options are permitted. Ports specified in the configuration file with the Port option are ignored when a command-line port is specified. Ports specified using the ListenAddress option override command-line ports. -q Quiet mode. Nothing is sent to the system log. Normally the beginning, authentication, and termination of each connection is logged. -T Extended test mode. Check the validity of the configuration file, output the effective configuration to stdout and then exit. Optionally, Match rules may be applied by specifying the connection parameters using one or more -C options. This is similar to the -G flag, but it includes the additional testing performed by the -t flag. -t Test mode. Only check the validity of the configuration file and sanity of the keys. This is useful for updating sshd reliably as configuration options may change. -u len This option is used to specify the size of the field in the utmp structure that holds the remote host name. If the resolved host name is longer than len, the dotted decimal value will be used instead. This allows hosts with very long host names that overflow this field to still be uniquely identified. Specifying -u0 indicates that only dotted decimal addresses should be put into the utmp file. -u0 may also be used to prevent sshd from making DNS requests unless the authentication mechanism or configuration requires it. Authentication mechanisms that may require DNS include HostbasedAuthentication and using a from="pattern-list" option in a key file. Configuration options that require DNS include using a USER@HOST pattern in AllowUsers or DenyUsers. -V Display the version number and exit. AUTHENTICATION The OpenSSH SSH daemon supports SSH protocol 2 only. Each host has a host-specific key, used to identify the host. Whenever a client connects, the daemon responds with its public host key. The client compares the host key against its own database to verify that it has not changed. Forward secrecy is provided through a Diffie-Hellman key agreement. This key agreement results in a shared session key. The rest of the session is encrypted using a symmetric cipher. The client selects the encryption algorithm to use from those offered by the server. Additionally, session integrity is provided through a cryptographic message authentication code (MAC). Finally, the server and the client enter an authentication dialog. The client tries to authenticate itself using host-based authentication, public key authentication, challenge-response authentication, or password authentication. Regardless of the authentication type, the account is checked to ensure that it is accessible. An account is not accessible if it is locked, listed in DenyUsers or its group is listed in DenyGroups . The definition of a locked account is system dependent. Some platforms have their own account database (eg AIX) and some modify the passwd field ( M-bM-^@M-^X*LK*M-bM-^@M-^Y on Solaris and UnixWare, M-bM-^@M-^X*M-bM-^@M-^Y on HP-UX, containing M-bM-^@M-^XNologinM-bM-^@M-^Y on Tru64, a leading M-bM-^@M-^X*LOCKED*M-bM-^@M-^Y on FreeBSD and a leading M-bM-^@M-^X!M-bM-^@M-^Y on most Linuxes). If there is a requirement to disable password authentication for the account while allowing still public-key, then the passwd field should be set to something other than these values (eg M-bM-^@M-^XNPM-bM-^@M-^Y or M-bM-^@M-^X*NP*M-bM-^@M-^Y ). If the client successfully authenticates itself, a dialog for preparing the session is entered. At this time the client may request things like allocating a pseudo-tty, forwarding X11 connections, forwarding TCP connections, or forwarding the authentication agent connection over the secure channel. After this, the client either requests an interactive shell or execution - or a non-interactive command, which sshd will execute via the user's + of a non-interactive command, which sshd will execute via the user's shell using its -c option. The sides then enter session mode. In this mode, either side may send data at any time, and such data is forwarded to/from the shell or command on the server side, and the user terminal in the client side. When the user program terminates and all forwarded X11 and other connections have been closed, the server sends command exit status to the client, and both sides exit. LOGIN PROCESS When a user successfully logs in, sshd does the following: 1. If the login is on a tty, and no command has been specified, prints last login time and /etc/motd (unless prevented in the configuration file or by ~/.hushlogin; see the FILES section). 2. If the login is on a tty, records login time. 3. Checks /etc/nologin; if it exists, prints contents and quits (unless root). 4. Changes to run with normal user privileges. 5. Sets up basic environment. 6. Reads the file ~/.ssh/environment, if it exists, and users are allowed to change their environment. See the PermitUserEnvironment option in sshd_config(5). 7. Changes to user's home directory. 8. If ~/.ssh/rc exists and the sshd_config(5) PermitUserRC option is set, runs it; else if /etc/ssh/sshrc exists, runs it; otherwise runs xauth(1). The M-bM-^@M-^\rcM-bM-^@M-^] files are given the X11 authentication protocol and cookie in standard input. See SSHRC, below. 9. Runs user's shell or command. All commands are run under the user's login shell as specified in the system password database. SSHRC If the file ~/.ssh/rc exists, sh(1) runs it after reading the environment files but before starting the user's shell or command. It must not produce any output on stdout; stderr must be used instead. If X11 forwarding is in use, it will receive the "proto cookie" pair in its standard input (and DISPLAY in its environment). The script must call xauth(1) because sshd will not run xauth automatically to add X11 cookies. The primary purpose of this file is to run any initialization routines which may be needed before the user's home directory becomes accessible; AFS is a particular example of such an environment. This file will probably contain some initialization code followed by something similar to: if read proto cookie && [ -n "$DISPLAY" ]; then if [ `echo $DISPLAY | cut -c1-10` = 'localhost:' ]; then # X11UseLocalhost=yes echo add unix:`echo $DISPLAY | cut -c11-` $proto $cookie else # X11UseLocalhost=no echo add $DISPLAY $proto $cookie fi | xauth -q - fi If this file does not exist, /etc/ssh/sshrc is run, and if that does not exist either, xauth is used to add the cookie. AUTHORIZED_KEYS FILE FORMAT AuthorizedKeysFile specifies the files containing public keys for public key authentication; if this option is not specified, the default is ~/.ssh/authorized_keys and ~/.ssh/authorized_keys2. Each line of the file contains one key (empty lines and lines starting with a M-bM-^@M-^X#M-bM-^@M-^Y are ignored as comments). Public keys consist of the following space- separated fields: options, keytype, base64-encoded key, comment. The options field is optional. The supported key types are: sk-ecdsa-sha2-nistp256@openssh.com ecdsa-sha2-nistp256 ecdsa-sha2-nistp384 ecdsa-sha2-nistp521 sk-ssh-ed25519@openssh.com ssh-ed25519 ssh-dss ssh-rsa The comment field is not used for anything (but may be convenient for the user to identify the key). Note that lines in this file can be several hundred bytes long (because of the size of the public key encoding) up to a limit of 8 kilobytes, which permits RSA keys up to 16 kilobits. You don't want to type them in; instead, copy the id_dsa.pub, id_ecdsa.pub, id_ecdsa_sk.pub, id_ed25519.pub, id_ed25519_sk.pub, or the id_rsa.pub file and edit it. sshd enforces a minimum RSA key modulus size of 1024 bits. The options (if present) consist of comma-separated option specifications. No spaces are permitted, except within double quotes. The following option specifications are supported (note that option keywords are case-insensitive): agent-forwarding Enable authentication agent forwarding previously disabled by the restrict option. cert-authority Specifies that the listed key is a certification authority (CA) that is trusted to validate signed certificates for user authentication. Certificates may encode access restrictions similar to these key options. If both certificate restrictions and key options are present, the most restrictive union of the two is applied. command="command" Specifies that the command is executed whenever this key is used for authentication. The command supplied by the user (if any) is ignored. The command is run on a pty if the client requests a pty; otherwise it is run without a tty. If an 8-bit clean channel is required, one must not request a pty or should specify no-pty. A quote may be included in the command by quoting it with a backslash. This option might be useful to restrict certain public keys to perform just a specific operation. An example might be a key that permits remote backups but nothing else. Note that the client may specify TCP and/or X11 forwarding unless they are explicitly prohibited, e.g. using the restrict key option. The command originally supplied by the client is available in the SSH_ORIGINAL_COMMAND environment variable. Note that this option applies to shell, command or subsystem execution. Also note that this command may be superseded by a sshd_config(5) ForceCommand directive. If a command is specified and a forced-command is embedded in a certificate used for authentication, then the certificate will be accepted only if the two commands are identical. environment="NAME=value" Specifies that the string is to be added to the environment when logging in using this key. Environment variables set this way override other default environment values. Multiple options of this type are permitted. Environment processing is disabled by default and is controlled via the PermitUserEnvironment option. expiry-time="timespec" Specifies a time after which the key will not be accepted. The time may be specified as a YYYYMMDD[Z] date or a YYYYMMDDHHMM[SS][Z] time. Dates and times will be interpreted in the system time zone unless suffixed by a Z character, in which case they will be interpreted in the UTC time zone. from="pattern-list" Specifies that in addition to public key authentication, either the canonical name of the remote host or its IP address must be present in the comma-separated list of patterns. See PATTERNS in ssh_config(5) for more information on patterns. In addition to the wildcard matching that may be applied to hostnames or addresses, a from stanza may match IP addresses using CIDR address/masklen notation. The purpose of this option is to optionally increase security: public key authentication by itself does not trust the network or name servers or anything (but the key); however, if somebody somehow steals the key, the key permits an intruder to log in from anywhere in the world. This additional option makes using a stolen key more difficult (name servers and/or routers would have to be compromised in addition to just the key). no-agent-forwarding Forbids authentication agent forwarding when this key is used for authentication. no-port-forwarding Forbids TCP forwarding when this key is used for authentication. Any port forward requests by the client will return an error. This might be used, e.g. in connection with the command option. no-pty Prevents tty allocation (a request to allocate a pty will fail). no-user-rc Disables execution of ~/.ssh/rc. no-X11-forwarding Forbids X11 forwarding when this key is used for authentication. Any X11 forward requests by the client will return an error. permitlisten="[host:]port" Limit remote port forwarding with the ssh(1) -R option such that it may only listen on the specified host (optional) and port. IPv6 addresses can be specified by enclosing the address in square brackets. Multiple permitlisten options may be applied separated by commas. Hostnames may include wildcards as described in the PATTERNS section in ssh_config(5). A port specification of * matches any port. Note that the setting of GatewayPorts may further restrict listen addresses. Note that ssh(1) will send a hostname of M-bM-^@M-^\localhostM-bM-^@M-^] if a listen host was not specified when the forwarding was requested, and that this name is treated differently to the explicit localhost addresses M-bM-^@M-^\127.0.0.1M-bM-^@M-^] and M-bM-^@M-^\::1M-bM-^@M-^]. permitopen="host:port" Limit local port forwarding with the ssh(1) -L option such that it may only connect to the specified host and port. IPv6 addresses can be specified by enclosing the address in square brackets. Multiple permitopen options may be applied separated by commas. No pattern matching or name lookup is performed on the specified hostnames, they must be literal host names and/or addresses. A port specification of * matches any port. port-forwarding Enable port forwarding previously disabled by the restrict option. principals="principals" On a cert-authority line, specifies allowed principals for certificate authentication as a comma-separated list. At least one name from the list must appear in the certificate's list of principals for the certificate to be accepted. This option is ignored for keys that are not marked as trusted certificate signers using the cert-authority option. pty Permits tty allocation previously disabled by the restrict option. no-touch-required Do not require demonstration of user presence for signatures made using this key. This option only makes sense for the FIDO authenticator algorithms ecdsa-sk and ed25519-sk. verify-required Require that signatures made using this key attest that they verified the user, e.g. via a PIN. This option only makes sense for the FIDO authenticator algorithms ecdsa-sk and ed25519-sk. restrict Enable all restrictions, i.e. disable port, agent and X11 forwarding, as well as disabling PTY allocation and execution of ~/.ssh/rc. If any future restriction capabilities are added to authorized_keys files, they will be included in this set. tunnel="n" Force a tun(4) device on the server. Without this option, the next available device will be used if the client requests a tunnel. user-rc Enables execution of ~/.ssh/rc previously disabled by the restrict option. X11-forwarding Permits X11 forwarding previously disabled by the restrict option. An example authorized_keys file: # Comments are allowed at start of line. Blank lines are allowed. # Plain key, no restrictions ssh-rsa ... # Forced command, disable PTY and all forwarding restrict,command="dump /home" ssh-rsa ... # Restriction of ssh -L forwarding destinations permitopen="192.0.2.1:80",permitopen="192.0.2.2:25" ssh-rsa ... # Restriction of ssh -R forwarding listeners permitlisten="localhost:8080",permitlisten="[::1]:22000" ssh-rsa ... # Configuration for tunnel forwarding tunnel="0",command="sh /etc/netstart tun0" ssh-rsa ... # Override of restriction to allow PTY allocation restrict,pty,command="nethack" ssh-rsa ... # Allow FIDO key without requiring touch no-touch-required sk-ecdsa-sha2-nistp256@openssh.com ... # Require user-verification (e.g. PIN or biometric) for FIDO key verify-required sk-ecdsa-sha2-nistp256@openssh.com ... # Trust CA key, allow touch-less FIDO if requested in certificate cert-authority,no-touch-required,principals="user_a" ssh-rsa ... SSH_KNOWN_HOSTS FILE FORMAT The /etc/ssh/ssh_known_hosts and ~/.ssh/known_hosts files contain host public keys for all known hosts. The global file should be prepared by the administrator (optional), and the per-user file is maintained automatically: whenever the user connects to an unknown host, its key is added to the per-user file. Each line in these files contains the following fields: marker (optional), hostnames, keytype, base64-encoded key, comment. The fields are separated by spaces. The marker is optional, but if it is present then it must be one of M-bM-^@M-^\@cert-authorityM-bM-^@M-^], to indicate that the line contains a certification authority (CA) key, or M-bM-^@M-^\@revokedM-bM-^@M-^], to indicate that the key contained on the line is revoked and must not ever be accepted. Only one marker should be used on a key line. Hostnames is a comma-separated list of patterns (M-bM-^@M-^X*M-bM-^@M-^Y and M-bM-^@M-^X?M-bM-^@M-^Y act as wildcards); each pattern in turn is matched against the host name. When sshd is authenticating a client, such as when using HostbasedAuthentication, this will be the canonical client host name. When ssh(1) is authenticating a server, this will be the host name given by the user, the value of the ssh(1) HostkeyAlias if it was specified, or the canonical server hostname if the ssh(1) CanonicalizeHostname option was used. A pattern may also be preceded by M-bM-^@M-^X!M-bM-^@M-^Y to indicate negation: if the host name matches a negated pattern, it is not accepted (by that line) even if it matched another pattern on the line. A hostname or address may optionally be enclosed within M-bM-^@M-^X[M-bM-^@M-^Y and M-bM-^@M-^X]M-bM-^@M-^Y brackets then followed by M-bM-^@M-^X:M-bM-^@M-^Y and a non-standard port number. Alternately, hostnames may be stored in a hashed form which hides host names and addresses should the file's contents be disclosed. Hashed hostnames start with a M-bM-^@M-^X|M-bM-^@M-^Y character. Only one hashed hostname may appear on a single line and none of the above negation or wildcard operators may be applied. The keytype and base64-encoded key are taken directly from the host key; they can be obtained, for example, from /etc/ssh/ssh_host_rsa_key.pub. The optional comment field continues to the end of the line, and is not used. Lines starting with M-bM-^@M-^X#M-bM-^@M-^Y and empty lines are ignored as comments. When performing host authentication, authentication is accepted if any matching line has the proper key; either one that matches exactly or, if the server has presented a certificate for authentication, the key of the certification authority that signed the certificate. For a key to be trusted as a certification authority, it must use the M-bM-^@M-^\@cert-authorityM-bM-^@M-^] marker described above. The known hosts file also provides a facility to mark keys as revoked, for example when it is known that the associated private key has been stolen. Revoked keys are specified by including the M-bM-^@M-^\@revokedM-bM-^@M-^] marker at the beginning of the key line, and are never accepted for authentication or as certification authorities, but instead will produce a warning from ssh(1) when they are encountered. It is permissible (but not recommended) to have several lines or different host keys for the same names. This will inevitably happen when short forms of host names from different domains are put in the file. It is possible that the files contain conflicting information; authentication is accepted if valid information can be found from either file. Note that the lines in these files are typically hundreds of characters long, and you definitely don't want to type in the host keys by hand. Rather, generate them by a script, ssh-keyscan(1) or by taking, for example, /etc/ssh/ssh_host_rsa_key.pub and adding the host names at the front. ssh-keygen(1) also offers some basic automated editing for ~/.ssh/known_hosts including removing hosts matching a host name and converting all host names to their hashed representations. An example ssh_known_hosts file: # Comments allowed at start of line cvs.example.net,192.0.2.10 ssh-rsa AAAA1234.....= # A hashed hostname |1|JfKTdBh7rNbXkVAQCRp4OQoPfmI=|USECr3SWf1JUPsms5AqfD5QfxkM= ssh-rsa AAAA1234.....= # A revoked key @revoked * ssh-rsa AAAAB5W... # A CA key, accepted for any host in *.mydomain.com or *.mydomain.org @cert-authority *.mydomain.org,*.mydomain.com ssh-rsa AAAAB5W... FILES ~/.hushlogin This file is used to suppress printing the last login time and /etc/motd, if PrintLastLog and PrintMotd, respectively, are enabled. It does not suppress printing of the banner specified by Banner. ~/.rhosts This file is used for host-based authentication (see ssh(1) for more information). On some machines this file may need to be world-readable if the user's home directory is on an NFS partition, because sshd reads it as root. Additionally, this file must be owned by the user, and must not have write permissions for anyone else. The recommended permission for most machines is read/write for the user, and not accessible by others. ~/.shosts This file is used in exactly the same way as .rhosts, but allows host-based authentication without permitting login with rlogin/rsh. ~/.ssh/ This directory is the default location for all user-specific configuration and authentication information. There is no general requirement to keep the entire contents of this directory secret, but the recommended permissions are read/write/execute for the user, and not accessible by others. ~/.ssh/authorized_keys Lists the public keys (DSA, ECDSA, Ed25519, RSA) that can be used for logging in as this user. The format of this file is described above. The content of the file is not highly sensitive, but the recommended permissions are read/write for the user, and not accessible by others. If this file, the ~/.ssh directory, or the user's home directory are writable by other users, then the file could be modified or replaced by unauthorized users. In this case, sshd will not allow it to be used unless the StrictModes option has been set to M-bM-^@M-^\noM-bM-^@M-^]. ~/.ssh/environment This file is read into the environment at login (if it exists). It can only contain empty lines, comment lines (that start with M-bM-^@M-^X#M-bM-^@M-^Y), and assignment lines of the form name=value. The file should be writable only by the user; it need not be readable by anyone else. Environment processing is disabled by default and is controlled via the PermitUserEnvironment option. ~/.ssh/known_hosts Contains a list of host keys for all hosts the user has logged into that are not already in the systemwide list of known host keys. The format of this file is described above. This file should be writable only by root/the owner and can, but need not be, world-readable. ~/.ssh/rc Contains initialization routines to be run before the user's home directory becomes accessible. This file should be writable only by the user, and need not be readable by anyone else. /etc/hosts.equiv This file is for host-based authentication (see ssh(1)). It should only be writable by root. /etc/moduli Contains Diffie-Hellman groups used for the "Diffie-Hellman Group Exchange" key exchange method. The file format is described in moduli(5). If no usable groups are found in this file then fixed internal groups will be used. /etc/motd See motd(5). /etc/nologin If this file exists, sshd refuses to let anyone except root log in. The contents of the file are displayed to anyone trying to log in, and non-root connections are refused. The file should be world-readable. /etc/shosts.equiv This file is used in exactly the same way as hosts.equiv, but allows host-based authentication without permitting login with rlogin/rsh. /etc/ssh/ssh_host_ecdsa_key /etc/ssh/ssh_host_ed25519_key /etc/ssh/ssh_host_rsa_key These files contain the private parts of the host keys. These files should only be owned by root, readable only by root, and not accessible to others. Note that sshd does not start if these files are group/world-accessible. /etc/ssh/ssh_host_ecdsa_key.pub /etc/ssh/ssh_host_ed25519_key.pub /etc/ssh/ssh_host_rsa_key.pub These files contain the public parts of the host keys. These files should be world-readable but writable only by root. Their contents should match the respective private parts. These files are not really used for anything; they are provided for the convenience of the user so their contents can be copied to known hosts files. These files are created using ssh-keygen(1). /etc/ssh/ssh_known_hosts Systemwide list of known host keys. This file should be prepared by the system administrator to contain the public host keys of all machines in the organization. The format of this file is described above. This file should be writable only by root/the owner and should be world-readable. /etc/ssh/sshd_config Contains configuration data for sshd. The file format and configuration options are described in sshd_config(5). /etc/ssh/sshrc Similar to ~/.ssh/rc, it can be used to specify machine-specific login-time initializations globally. This file should be writable only by root, and should be world-readable. /var/empty chroot(2) directory used by sshd during privilege separation in the pre-authentication phase. The directory should not contain any files and must be owned by root and not group or world- writable. /var/run/sshd.pid Contains the process ID of the sshd listening for connections (if there are several daemons running concurrently for different ports, this contains the process ID of the one started last). The content of this file is not sensitive; it can be world- readable. SEE ALSO scp(1), sftp(1), ssh(1), ssh-add(1), ssh-agent(1), ssh-keygen(1), ssh-keyscan(1), chroot(2), login.conf(5), moduli(5), sshd_config(5), inetd(8), sftp-server(8) AUTHORS OpenSSH is a derivative of the original and free ssh 1.2.12 release by Tatu Ylonen. Aaron Campbell, Bob Beck, Markus Friedl, Niels Provos, Theo de Raadt and Dug Song removed many bugs, re-added newer features and created OpenSSH. Markus Friedl contributed the support for SSH protocol versions 1.5 and 2.0. Niels Provos and Markus Friedl contributed support for privilege separation. -OpenBSD 7.3 February 10, 2023 OpenBSD 7.3 +OpenBSD 7.3 September 19, 2023 OpenBSD 7.3 diff --git a/sshd.8 b/sshd.8 index 9c8f2fcaa86a..73d5e9232702 100644 --- a/sshd.8 +++ b/sshd.8 @@ -1,1044 +1,1044 @@ .\" .\" Author: Tatu Ylonen .\" Copyright (c) 1995 Tatu Ylonen , Espoo, Finland .\" All rights reserved .\" .\" As far as I am concerned, the code I have written for this software .\" can be used freely for any purpose. Any derived versions of this .\" software must be clearly marked as such, and if the derived work is .\" incompatible with the protocol description in the RFC file, it must be .\" called by a name other than "ssh" or "Secure Shell". .\" .\" Copyright (c) 1999,2000 Markus Friedl. All rights reserved. .\" Copyright (c) 1999 Aaron Campbell. All rights reserved. .\" Copyright (c) 1999 Theo de Raadt. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR .\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES .\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. .\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, .\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT .\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, .\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY .\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.\" $OpenBSD: sshd.8,v 1.324 2023/02/10 06:39:27 jmc Exp $ -.Dd $Mdocdate: February 10 2023 $ +.\" $OpenBSD: sshd.8,v 1.325 2023/09/19 20:37:07 deraadt Exp $ +.Dd $Mdocdate: September 19 2023 $ .Dt SSHD 8 .Os .Sh NAME .Nm sshd .Nd OpenSSH daemon .Sh SYNOPSIS .Nm sshd .Bk -words .Op Fl 46DdeGiqTtV .Op Fl C Ar connection_spec .Op Fl c Ar host_certificate_file .Op Fl E Ar log_file .Op Fl f Ar config_file .Op Fl g Ar login_grace_time .Op Fl h Ar host_key_file .Op Fl o Ar option .Op Fl p Ar port .Op Fl u Ar len .Ek .Sh DESCRIPTION .Nm (OpenSSH Daemon) is the daemon program for .Xr ssh 1 . It provides secure encrypted communications between two untrusted hosts over an insecure network. .Pp .Nm listens for connections from clients. It is normally started at boot from .Pa /etc/rc . It forks a new daemon for each incoming connection. The forked daemons handle key exchange, encryption, authentication, command execution, and data exchange. .Pp .Nm can be configured using command-line options or a configuration file (by default .Xr sshd_config 5 ) ; command-line options override values specified in the configuration file. .Nm rereads its configuration file when it receives a hangup signal, .Dv SIGHUP , by executing itself with the name and options it was started with, e.g.\& .Pa /usr/sbin/sshd . .Pp The options are as follows: .Bl -tag -width Ds .It Fl 4 Forces .Nm to use IPv4 addresses only. .It Fl 6 Forces .Nm to use IPv6 addresses only. .It Fl C Ar connection_spec Specify the connection parameters to use for the .Fl T extended test mode. If provided, any .Cm Match directives in the configuration file that would apply are applied before the configuration is written to standard output. The connection parameters are supplied as keyword=value pairs and may be supplied in any order, either with multiple .Fl C options or as a comma-separated list. The keywords are .Dq addr , .Dq user , .Dq host , .Dq laddr , .Dq lport , and .Dq rdomain and correspond to source address, user, resolved source host name, local address, local port number and routing domain respectively. .It Fl c Ar host_certificate_file Specifies a path to a certificate file to identify .Nm during key exchange. The certificate file must match a host key file specified using the .Fl h option or the .Cm HostKey configuration directive. .It Fl D When this option is specified, .Nm will not detach and does not become a daemon. This allows easy monitoring of .Nm sshd . .It Fl d Debug mode. The server sends verbose debug output to standard error, and does not put itself in the background. The server also will not .Xr fork 2 and will only process one connection. This option is only intended for debugging for the server. Multiple .Fl d options increase the debugging level. Maximum is 3. .It Fl E Ar log_file Append debug logs to .Ar log_file instead of the system log. .It Fl e Write debug logs to standard error instead of the system log. .It Fl f Ar config_file Specifies the name of the configuration file. The default is .Pa /etc/ssh/sshd_config . .Nm refuses to start if there is no configuration file. .It Fl G Parse and print configuration file. Check the validity of the configuration file, output the effective configuration to stdout and then exit. Optionally, .Cm Match rules may be applied by specifying the connection parameters using one or more .Fl C options. .It Fl g Ar login_grace_time Gives the grace time for clients to authenticate themselves (default 120 seconds). If the client fails to authenticate the user within this many seconds, the server disconnects and exits. A value of zero indicates no limit. .It Fl h Ar host_key_file Specifies a file from which a host key is read. This option must be given if .Nm is not run as root (as the normal host key files are normally not readable by anyone but root). The default is .Pa /etc/ssh/ssh_host_ecdsa_key , .Pa /etc/ssh/ssh_host_ed25519_key and .Pa /etc/ssh/ssh_host_rsa_key . It is possible to have multiple host key files for the different host key algorithms. .It Fl i Specifies that .Nm is being run from .Xr inetd 8 . .It Fl o Ar option Can be used to give options in the format used in the configuration file. This is useful for specifying options for which there is no separate command-line flag. For full details of the options, and their values, see .Xr sshd_config 5 . .It Fl p Ar port Specifies the port on which the server listens for connections (default 22). Multiple port options are permitted. Ports specified in the configuration file with the .Cm Port option are ignored when a command-line port is specified. Ports specified using the .Cm ListenAddress option override command-line ports. .It Fl q Quiet mode. Nothing is sent to the system log. Normally the beginning, authentication, and termination of each connection is logged. .It Fl T Extended test mode. Check the validity of the configuration file, output the effective configuration to stdout and then exit. Optionally, .Cm Match rules may be applied by specifying the connection parameters using one or more .Fl C options. This is similar to the .Fl G flag, but it includes the additional testing performed by the .Fl t flag. .It Fl t Test mode. Only check the validity of the configuration file and sanity of the keys. This is useful for updating .Nm reliably as configuration options may change. .It Fl u Ar len This option is used to specify the size of the field in the .Vt utmp structure that holds the remote host name. If the resolved host name is longer than .Ar len , the dotted decimal value will be used instead. This allows hosts with very long host names that overflow this field to still be uniquely identified. Specifying .Fl u0 indicates that only dotted decimal addresses should be put into the .Pa utmp file. .Fl u0 may also be used to prevent .Nm from making DNS requests unless the authentication mechanism or configuration requires it. Authentication mechanisms that may require DNS include .Cm HostbasedAuthentication and using a .Cm from="pattern-list" option in a key file. Configuration options that require DNS include using a USER@HOST pattern in .Cm AllowUsers or .Cm DenyUsers . .It Fl V Display the version number and exit. .El .Sh AUTHENTICATION The OpenSSH SSH daemon supports SSH protocol 2 only. Each host has a host-specific key, used to identify the host. Whenever a client connects, the daemon responds with its public host key. The client compares the host key against its own database to verify that it has not changed. Forward secrecy is provided through a Diffie-Hellman key agreement. This key agreement results in a shared session key. The rest of the session is encrypted using a symmetric cipher. The client selects the encryption algorithm to use from those offered by the server. Additionally, session integrity is provided through a cryptographic message authentication code (MAC). .Pp Finally, the server and the client enter an authentication dialog. The client tries to authenticate itself using host-based authentication, public key authentication, challenge-response authentication, or password authentication. .Pp Regardless of the authentication type, the account is checked to ensure that it is accessible. An account is not accessible if it is locked, listed in .Cm DenyUsers or its group is listed in .Cm DenyGroups \&. The definition of a locked account is system dependent. Some platforms have their own account database (eg AIX) and some modify the passwd field ( .Ql \&*LK\&* on Solaris and UnixWare, .Ql \&* on HP-UX, containing .Ql Nologin on Tru64, a leading .Ql \&*LOCKED\&* on FreeBSD and a leading .Ql \&! on most Linuxes). If there is a requirement to disable password authentication for the account while allowing still public-key, then the passwd field should be set to something other than these values (eg .Ql NP or .Ql \&*NP\&* ). .Pp If the client successfully authenticates itself, a dialog for preparing the session is entered. At this time the client may request things like allocating a pseudo-tty, forwarding X11 connections, forwarding TCP connections, or forwarding the authentication agent connection over the secure channel. .Pp After this, the client either requests an interactive shell or execution -or a non-interactive command, which +of a non-interactive command, which .Nm will execute via the user's shell using its .Fl c option. The sides then enter session mode. In this mode, either side may send data at any time, and such data is forwarded to/from the shell or command on the server side, and the user terminal in the client side. .Pp When the user program terminates and all forwarded X11 and other connections have been closed, the server sends command exit status to the client, and both sides exit. .Sh LOGIN PROCESS When a user successfully logs in, .Nm does the following: .Bl -enum -offset indent .It If the login is on a tty, and no command has been specified, prints last login time and .Pa /etc/motd (unless prevented in the configuration file or by .Pa ~/.hushlogin ; see the .Sx FILES section). .It If the login is on a tty, records login time. .It Checks .Pa /etc/nologin ; if it exists, prints contents and quits (unless root). .It Changes to run with normal user privileges. .It Sets up basic environment. .It Reads the file .Pa ~/.ssh/environment , if it exists, and users are allowed to change their environment. See the .Cm PermitUserEnvironment option in .Xr sshd_config 5 . .It Changes to user's home directory. .It If .Pa ~/.ssh/rc exists and the .Xr sshd_config 5 .Cm PermitUserRC option is set, runs it; else if .Pa /etc/ssh/sshrc exists, runs it; otherwise runs .Xr xauth 1 . The .Dq rc files are given the X11 authentication protocol and cookie in standard input. See .Sx SSHRC , below. .It Runs user's shell or command. All commands are run under the user's login shell as specified in the system password database. .El .Sh SSHRC If the file .Pa ~/.ssh/rc exists, .Xr sh 1 runs it after reading the environment files but before starting the user's shell or command. It must not produce any output on stdout; stderr must be used instead. If X11 forwarding is in use, it will receive the "proto cookie" pair in its standard input (and .Ev DISPLAY in its environment). The script must call .Xr xauth 1 because .Nm will not run xauth automatically to add X11 cookies. .Pp The primary purpose of this file is to run any initialization routines which may be needed before the user's home directory becomes accessible; AFS is a particular example of such an environment. .Pp This file will probably contain some initialization code followed by something similar to: .Bd -literal -offset 3n if read proto cookie && [ -n "$DISPLAY" ]; then if [ `echo $DISPLAY | cut -c1-10` = 'localhost:' ]; then # X11UseLocalhost=yes echo add unix:`echo $DISPLAY | cut -c11-` $proto $cookie else # X11UseLocalhost=no echo add $DISPLAY $proto $cookie fi | xauth -q - fi .Ed .Pp If this file does not exist, .Pa /etc/ssh/sshrc is run, and if that does not exist either, xauth is used to add the cookie. .Sh AUTHORIZED_KEYS FILE FORMAT .Cm AuthorizedKeysFile specifies the files containing public keys for public key authentication; if this option is not specified, the default is .Pa ~/.ssh/authorized_keys and .Pa ~/.ssh/authorized_keys2 . Each line of the file contains one key (empty lines and lines starting with a .Ql # are ignored as comments). Public keys consist of the following space-separated fields: options, keytype, base64-encoded key, comment. The options field is optional. The supported key types are: .Pp .Bl -item -compact -offset indent .It sk-ecdsa-sha2-nistp256@openssh.com .It ecdsa-sha2-nistp256 .It ecdsa-sha2-nistp384 .It ecdsa-sha2-nistp521 .It sk-ssh-ed25519@openssh.com .It ssh-ed25519 .It ssh-dss .It ssh-rsa .El .Pp The comment field is not used for anything (but may be convenient for the user to identify the key). .Pp Note that lines in this file can be several hundred bytes long (because of the size of the public key encoding) up to a limit of 8 kilobytes, which permits RSA keys up to 16 kilobits. You don't want to type them in; instead, copy the .Pa id_dsa.pub , .Pa id_ecdsa.pub , .Pa id_ecdsa_sk.pub , .Pa id_ed25519.pub , .Pa id_ed25519_sk.pub , or the .Pa id_rsa.pub file and edit it. .Pp .Nm enforces a minimum RSA key modulus size of 1024 bits. .Pp The options (if present) consist of comma-separated option specifications. No spaces are permitted, except within double quotes. The following option specifications are supported (note that option keywords are case-insensitive): .Bl -tag -width Ds .It Cm agent-forwarding Enable authentication agent forwarding previously disabled by the .Cm restrict option. .It Cm cert-authority Specifies that the listed key is a certification authority (CA) that is trusted to validate signed certificates for user authentication. .Pp Certificates may encode access restrictions similar to these key options. If both certificate restrictions and key options are present, the most restrictive union of the two is applied. .It Cm command="command" Specifies that the command is executed whenever this key is used for authentication. The command supplied by the user (if any) is ignored. The command is run on a pty if the client requests a pty; otherwise it is run without a tty. If an 8-bit clean channel is required, one must not request a pty or should specify .Cm no-pty . A quote may be included in the command by quoting it with a backslash. .Pp This option might be useful to restrict certain public keys to perform just a specific operation. An example might be a key that permits remote backups but nothing else. Note that the client may specify TCP and/or X11 forwarding unless they are explicitly prohibited, e.g. using the .Cm restrict key option. .Pp The command originally supplied by the client is available in the .Ev SSH_ORIGINAL_COMMAND environment variable. Note that this option applies to shell, command or subsystem execution. Also note that this command may be superseded by a .Xr sshd_config 5 .Cm ForceCommand directive. .Pp If a command is specified and a forced-command is embedded in a certificate used for authentication, then the certificate will be accepted only if the two commands are identical. .It Cm environment="NAME=value" Specifies that the string is to be added to the environment when logging in using this key. Environment variables set this way override other default environment values. Multiple options of this type are permitted. Environment processing is disabled by default and is controlled via the .Cm PermitUserEnvironment option. .It Cm expiry-time="timespec" Specifies a time after which the key will not be accepted. The time may be specified as a YYYYMMDD[Z] date or a YYYYMMDDHHMM[SS][Z] time. Dates and times will be interpreted in the system time zone unless suffixed by a Z character, in which case they will be interpreted in the UTC time zone. .It Cm from="pattern-list" Specifies that in addition to public key authentication, either the canonical name of the remote host or its IP address must be present in the comma-separated list of patterns. See PATTERNS in .Xr ssh_config 5 for more information on patterns. .Pp In addition to the wildcard matching that may be applied to hostnames or addresses, a .Cm from stanza may match IP addresses using CIDR address/masklen notation. .Pp The purpose of this option is to optionally increase security: public key authentication by itself does not trust the network or name servers or anything (but the key); however, if somebody somehow steals the key, the key permits an intruder to log in from anywhere in the world. This additional option makes using a stolen key more difficult (name servers and/or routers would have to be compromised in addition to just the key). .It Cm no-agent-forwarding Forbids authentication agent forwarding when this key is used for authentication. .It Cm no-port-forwarding Forbids TCP forwarding when this key is used for authentication. Any port forward requests by the client will return an error. This might be used, e.g. in connection with the .Cm command option. .It Cm no-pty Prevents tty allocation (a request to allocate a pty will fail). .It Cm no-user-rc Disables execution of .Pa ~/.ssh/rc . .It Cm no-X11-forwarding Forbids X11 forwarding when this key is used for authentication. Any X11 forward requests by the client will return an error. .It Cm permitlisten="[host:]port" Limit remote port forwarding with the .Xr ssh 1 .Fl R option such that it may only listen on the specified host (optional) and port. IPv6 addresses can be specified by enclosing the address in square brackets. Multiple .Cm permitlisten options may be applied separated by commas. Hostnames may include wildcards as described in the PATTERNS section in .Xr ssh_config 5 . A port specification of .Cm * matches any port. Note that the setting of .Cm GatewayPorts may further restrict listen addresses. Note that .Xr ssh 1 will send a hostname of .Dq localhost if a listen host was not specified when the forwarding was requested, and that this name is treated differently to the explicit localhost addresses .Dq 127.0.0.1 and .Dq ::1 . .It Cm permitopen="host:port" Limit local port forwarding with the .Xr ssh 1 .Fl L option such that it may only connect to the specified host and port. IPv6 addresses can be specified by enclosing the address in square brackets. Multiple .Cm permitopen options may be applied separated by commas. No pattern matching or name lookup is performed on the specified hostnames, they must be literal host names and/or addresses. A port specification of .Cm * matches any port. .It Cm port-forwarding Enable port forwarding previously disabled by the .Cm restrict option. .It Cm principals="principals" On a .Cm cert-authority line, specifies allowed principals for certificate authentication as a comma-separated list. At least one name from the list must appear in the certificate's list of principals for the certificate to be accepted. This option is ignored for keys that are not marked as trusted certificate signers using the .Cm cert-authority option. .It Cm pty Permits tty allocation previously disabled by the .Cm restrict option. .It Cm no-touch-required Do not require demonstration of user presence for signatures made using this key. This option only makes sense for the FIDO authenticator algorithms .Cm ecdsa-sk and .Cm ed25519-sk . .It Cm verify-required Require that signatures made using this key attest that they verified the user, e.g. via a PIN. This option only makes sense for the FIDO authenticator algorithms .Cm ecdsa-sk and .Cm ed25519-sk . .It Cm restrict Enable all restrictions, i.e. disable port, agent and X11 forwarding, as well as disabling PTY allocation and execution of .Pa ~/.ssh/rc . If any future restriction capabilities are added to authorized_keys files, they will be included in this set. .It Cm tunnel="n" Force a .Xr tun 4 device on the server. Without this option, the next available device will be used if the client requests a tunnel. .It Cm user-rc Enables execution of .Pa ~/.ssh/rc previously disabled by the .Cm restrict option. .It Cm X11-forwarding Permits X11 forwarding previously disabled by the .Cm restrict option. .El .Pp An example authorized_keys file: .Bd -literal -offset 3n # Comments are allowed at start of line. Blank lines are allowed. # Plain key, no restrictions ssh-rsa ... # Forced command, disable PTY and all forwarding restrict,command="dump /home" ssh-rsa ... # Restriction of ssh -L forwarding destinations permitopen="192.0.2.1:80",permitopen="192.0.2.2:25" ssh-rsa ... # Restriction of ssh -R forwarding listeners permitlisten="localhost:8080",permitlisten="[::1]:22000" ssh-rsa ... # Configuration for tunnel forwarding tunnel="0",command="sh /etc/netstart tun0" ssh-rsa ... # Override of restriction to allow PTY allocation restrict,pty,command="nethack" ssh-rsa ... # Allow FIDO key without requiring touch no-touch-required sk-ecdsa-sha2-nistp256@openssh.com ... # Require user-verification (e.g. PIN or biometric) for FIDO key verify-required sk-ecdsa-sha2-nistp256@openssh.com ... # Trust CA key, allow touch-less FIDO if requested in certificate cert-authority,no-touch-required,principals="user_a" ssh-rsa ... .Ed .Sh SSH_KNOWN_HOSTS FILE FORMAT The .Pa /etc/ssh/ssh_known_hosts and .Pa ~/.ssh/known_hosts files contain host public keys for all known hosts. The global file should be prepared by the administrator (optional), and the per-user file is maintained automatically: whenever the user connects to an unknown host, its key is added to the per-user file. .Pp Each line in these files contains the following fields: marker (optional), hostnames, keytype, base64-encoded key, comment. The fields are separated by spaces. .Pp The marker is optional, but if it is present then it must be one of .Dq @cert-authority , to indicate that the line contains a certification authority (CA) key, or .Dq @revoked , to indicate that the key contained on the line is revoked and must not ever be accepted. Only one marker should be used on a key line. .Pp Hostnames is a comma-separated list of patterns .Pf ( Ql * and .Ql \&? act as wildcards); each pattern in turn is matched against the host name. When .Nm sshd is authenticating a client, such as when using .Cm HostbasedAuthentication , this will be the canonical client host name. When .Xr ssh 1 is authenticating a server, this will be the host name given by the user, the value of the .Xr ssh 1 .Cm HostkeyAlias if it was specified, or the canonical server hostname if the .Xr ssh 1 .Cm CanonicalizeHostname option was used. .Pp A pattern may also be preceded by .Ql \&! to indicate negation: if the host name matches a negated pattern, it is not accepted (by that line) even if it matched another pattern on the line. A hostname or address may optionally be enclosed within .Ql \&[ and .Ql \&] brackets then followed by .Ql \&: and a non-standard port number. .Pp Alternately, hostnames may be stored in a hashed form which hides host names and addresses should the file's contents be disclosed. Hashed hostnames start with a .Ql | character. Only one hashed hostname may appear on a single line and none of the above negation or wildcard operators may be applied. .Pp The keytype and base64-encoded key are taken directly from the host key; they can be obtained, for example, from .Pa /etc/ssh/ssh_host_rsa_key.pub . The optional comment field continues to the end of the line, and is not used. .Pp Lines starting with .Ql # and empty lines are ignored as comments. .Pp When performing host authentication, authentication is accepted if any matching line has the proper key; either one that matches exactly or, if the server has presented a certificate for authentication, the key of the certification authority that signed the certificate. For a key to be trusted as a certification authority, it must use the .Dq @cert-authority marker described above. .Pp The known hosts file also provides a facility to mark keys as revoked, for example when it is known that the associated private key has been stolen. Revoked keys are specified by including the .Dq @revoked marker at the beginning of the key line, and are never accepted for authentication or as certification authorities, but instead will produce a warning from .Xr ssh 1 when they are encountered. .Pp It is permissible (but not recommended) to have several lines or different host keys for the same names. This will inevitably happen when short forms of host names from different domains are put in the file. It is possible that the files contain conflicting information; authentication is accepted if valid information can be found from either file. .Pp Note that the lines in these files are typically hundreds of characters long, and you definitely don't want to type in the host keys by hand. Rather, generate them by a script, .Xr ssh-keyscan 1 or by taking, for example, .Pa /etc/ssh/ssh_host_rsa_key.pub and adding the host names at the front. .Xr ssh-keygen 1 also offers some basic automated editing for .Pa ~/.ssh/known_hosts including removing hosts matching a host name and converting all host names to their hashed representations. .Pp An example ssh_known_hosts file: .Bd -literal -offset 3n # Comments allowed at start of line cvs.example.net,192.0.2.10 ssh-rsa AAAA1234.....= # A hashed hostname |1|JfKTdBh7rNbXkVAQCRp4OQoPfmI=|USECr3SWf1JUPsms5AqfD5QfxkM= ssh-rsa AAAA1234.....= # A revoked key @revoked * ssh-rsa AAAAB5W... # A CA key, accepted for any host in *.mydomain.com or *.mydomain.org @cert-authority *.mydomain.org,*.mydomain.com ssh-rsa AAAAB5W... .Ed .Sh FILES .Bl -tag -width Ds -compact .It Pa ~/.hushlogin This file is used to suppress printing the last login time and .Pa /etc/motd , if .Cm PrintLastLog and .Cm PrintMotd , respectively, are enabled. It does not suppress printing of the banner specified by .Cm Banner . .Pp .It Pa ~/.rhosts This file is used for host-based authentication (see .Xr ssh 1 for more information). On some machines this file may need to be world-readable if the user's home directory is on an NFS partition, because .Nm reads it as root. Additionally, this file must be owned by the user, and must not have write permissions for anyone else. The recommended permission for most machines is read/write for the user, and not accessible by others. .Pp .It Pa ~/.shosts This file is used in exactly the same way as .Pa .rhosts , but allows host-based authentication without permitting login with rlogin/rsh. .Pp .It Pa ~/.ssh/ This directory is the default location for all user-specific configuration and authentication information. There is no general requirement to keep the entire contents of this directory secret, but the recommended permissions are read/write/execute for the user, and not accessible by others. .Pp .It Pa ~/.ssh/authorized_keys Lists the public keys (DSA, ECDSA, Ed25519, RSA) that can be used for logging in as this user. The format of this file is described above. The content of the file is not highly sensitive, but the recommended permissions are read/write for the user, and not accessible by others. .Pp If this file, the .Pa ~/.ssh directory, or the user's home directory are writable by other users, then the file could be modified or replaced by unauthorized users. In this case, .Nm will not allow it to be used unless the .Cm StrictModes option has been set to .Dq no . .Pp .It Pa ~/.ssh/environment This file is read into the environment at login (if it exists). It can only contain empty lines, comment lines (that start with .Ql # ) , and assignment lines of the form name=value. The file should be writable only by the user; it need not be readable by anyone else. Environment processing is disabled by default and is controlled via the .Cm PermitUserEnvironment option. .Pp .It Pa ~/.ssh/known_hosts Contains a list of host keys for all hosts the user has logged into that are not already in the systemwide list of known host keys. The format of this file is described above. This file should be writable only by root/the owner and can, but need not be, world-readable. .Pp .It Pa ~/.ssh/rc Contains initialization routines to be run before the user's home directory becomes accessible. This file should be writable only by the user, and need not be readable by anyone else. .Pp .It Pa /etc/hosts.equiv This file is for host-based authentication (see .Xr ssh 1 ) . It should only be writable by root. .Pp .It Pa /etc/moduli Contains Diffie-Hellman groups used for the "Diffie-Hellman Group Exchange" key exchange method. The file format is described in .Xr moduli 5 . If no usable groups are found in this file then fixed internal groups will be used. .Pp .It Pa /etc/motd See .Xr motd 5 . .Pp .It Pa /etc/nologin If this file exists, .Nm refuses to let anyone except root log in. The contents of the file are displayed to anyone trying to log in, and non-root connections are refused. The file should be world-readable. .Pp .It Pa /etc/shosts.equiv This file is used in exactly the same way as .Pa hosts.equiv , but allows host-based authentication without permitting login with rlogin/rsh. .Pp .It Pa /etc/ssh/ssh_host_ecdsa_key .It Pa /etc/ssh/ssh_host_ed25519_key .It Pa /etc/ssh/ssh_host_rsa_key These files contain the private parts of the host keys. These files should only be owned by root, readable only by root, and not accessible to others. Note that .Nm does not start if these files are group/world-accessible. .Pp .It Pa /etc/ssh/ssh_host_ecdsa_key.pub .It Pa /etc/ssh/ssh_host_ed25519_key.pub .It Pa /etc/ssh/ssh_host_rsa_key.pub These files contain the public parts of the host keys. These files should be world-readable but writable only by root. Their contents should match the respective private parts. These files are not really used for anything; they are provided for the convenience of the user so their contents can be copied to known hosts files. These files are created using .Xr ssh-keygen 1 . .Pp .It Pa /etc/ssh/ssh_known_hosts Systemwide list of known host keys. This file should be prepared by the system administrator to contain the public host keys of all machines in the organization. The format of this file is described above. This file should be writable only by root/the owner and should be world-readable. .Pp .It Pa /etc/ssh/sshd_config Contains configuration data for .Nm sshd . The file format and configuration options are described in .Xr sshd_config 5 . .Pp .It Pa /etc/ssh/sshrc Similar to .Pa ~/.ssh/rc , it can be used to specify machine-specific login-time initializations globally. This file should be writable only by root, and should be world-readable. .Pp .It Pa /var/empty .Xr chroot 2 directory used by .Nm during privilege separation in the pre-authentication phase. The directory should not contain any files and must be owned by root and not group or world-writable. .Pp .It Pa /var/run/sshd.pid Contains the process ID of the .Nm listening for connections (if there are several daemons running concurrently for different ports, this contains the process ID of the one started last). The content of this file is not sensitive; it can be world-readable. .El .Sh SEE ALSO .Xr scp 1 , .Xr sftp 1 , .Xr ssh 1 , .Xr ssh-add 1 , .Xr ssh-agent 1 , .Xr ssh-keygen 1 , .Xr ssh-keyscan 1 , .Xr chroot 2 , .Xr login.conf 5 , .Xr moduli 5 , .Xr sshd_config 5 , .Xr inetd 8 , .Xr sftp-server 8 .Sh AUTHORS OpenSSH is a derivative of the original and free ssh 1.2.12 release by Tatu Ylonen. Aaron Campbell, Bob Beck, Markus Friedl, Niels Provos, Theo de Raadt and Dug Song removed many bugs, re-added newer features and created OpenSSH. Markus Friedl contributed the support for SSH protocol versions 1.5 and 2.0. Niels Provos and Markus Friedl contributed support for privilege separation. diff --git a/sshd.c b/sshd.c index 264e81ac705b..8524808f94b9 100644 --- a/sshd.c +++ b/sshd.c @@ -1,2464 +1,2464 @@ /* $OpenBSD: sshd.c,v 1.600 2023/03/08 04:43:12 guenther Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * This program is the ssh daemon. It listens for connections from clients, * and performs authentication, executes use commands or shell, and forwards * information to/from the application to the user client over an encrypted * connection. This can also handle forwarding of X11, TCP/IP, and * authentication agent connections. * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". * * SSH2 implementation: * Privilege Separation: * * Copyright (c) 2000, 2001, 2002 Markus Friedl. All rights reserved. * Copyright (c) 2002 Niels Provos. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" #include #include #include #ifdef HAVE_SYS_STAT_H # include #endif #ifdef HAVE_SYS_TIME_H # include #endif #include "openbsd-compat/sys-tree.h" #include "openbsd-compat/sys-queue.h" #include #include #include #include #ifdef HAVE_PATHS_H #include #endif #include #ifdef HAVE_POLL_H #include #endif #include #include #include #include #include #include #include #include #ifdef WITH_OPENSSL #include #include #include #include "openbsd-compat/openssl-compat.h" #endif #ifdef HAVE_SECUREWARE #include #include #endif #include "xmalloc.h" #include "ssh.h" #include "ssh2.h" #include "sshpty.h" #include "packet.h" #include "log.h" #include "sshbuf.h" #include "misc.h" #include "match.h" #include "servconf.h" #include "uidswap.h" #include "compat.h" #include "cipher.h" #include "digest.h" #include "sshkey.h" #include "kex.h" #include "authfile.h" #include "pathnames.h" #include "atomicio.h" #include "canohost.h" #include "hostfile.h" #include "auth.h" #include "authfd.h" #include "msg.h" #include "dispatch.h" #include "channels.h" #include "session.h" #include "monitor.h" #ifdef GSSAPI #include "ssh-gss.h" #endif #include "monitor_wrap.h" #include "ssh-sandbox.h" #include "auth-options.h" #include "version.h" #include "ssherr.h" #include "sk-api.h" #include "srclimit.h" #include "dh.h" /* Re-exec fds */ #define REEXEC_DEVCRYPTO_RESERVED_FD (STDERR_FILENO + 1) #define REEXEC_STARTUP_PIPE_FD (STDERR_FILENO + 2) #define REEXEC_CONFIG_PASS_FD (STDERR_FILENO + 3) #define REEXEC_MIN_FREE_FD (STDERR_FILENO + 4) extern char *__progname; /* Server configuration options. */ ServerOptions options; /* Name of the server configuration file. */ char *config_file_name = _PATH_SERVER_CONFIG_FILE; /* * Debug mode flag. This can be set on the command line. If debug * mode is enabled, extra debugging output will be sent to the system * log, the daemon will not go to background, and will exit after processing * the first connection. */ int debug_flag = 0; /* * Indicating that the daemon should only test the configuration and keys. * If test_flag > 1 ("-T" flag), then sshd will also dump the effective * configuration, optionally using connection information provided by the * "-C" flag. */ static int test_flag = 0; /* Flag indicating that the daemon is being started from inetd. */ static int inetd_flag = 0; /* Flag indicating that sshd should not detach and become a daemon. */ static int no_daemon_flag = 0; /* debug goes to stderr unless inetd_flag is set */ static int log_stderr = 0; /* Saved arguments to main(). */ static char **saved_argv; static int saved_argc; /* re-exec */ static int rexeced_flag = 0; static int rexec_flag = 1; static int rexec_argc = 0; static char **rexec_argv; /* * The sockets that the server is listening; this is used in the SIGHUP * signal handler. */ #define MAX_LISTEN_SOCKS 16 static int listen_socks[MAX_LISTEN_SOCKS]; static int num_listen_socks = 0; /* Daemon's agent connection */ int auth_sock = -1; static int have_agent = 0; /* * Any really sensitive data in the application is contained in this * structure. The idea is that this structure could be locked into memory so * that the pages do not get written into swap. However, there are some * problems. The private key contains BIGNUMs, and we do not (in principle) * have access to the internals of them, and locking just the structure is * not very useful. Currently, memory locking is not implemented. */ struct { struct sshkey **host_keys; /* all private host keys */ struct sshkey **host_pubkeys; /* all public host keys */ struct sshkey **host_certificates; /* all public host certificates */ int have_ssh2_key; } sensitive_data; /* This is set to true when a signal is received. */ static volatile sig_atomic_t received_sighup = 0; static volatile sig_atomic_t received_sigterm = 0; /* record remote hostname or ip */ u_int utmp_len = HOST_NAME_MAX+1; /* * startup_pipes/flags are used for tracking children of the listening sshd * process early in their lifespans. This tracking is needed for three things: * * 1) Implementing the MaxStartups limit of concurrent unauthenticated * connections. * 2) Avoiding a race condition for SIGHUP processing, where child processes * may have listen_socks open that could collide with main listener process * after it restarts. * 3) Ensuring that rexec'd sshd processes have received their initial state * from the parent listen process before handling SIGHUP. * * Child processes signal that they have completed closure of the listen_socks * and (if applicable) received their rexec state by sending a char over their * sock. Child processes signal that authentication has completed by closing * the sock (or by exiting). */ static int *startup_pipes = NULL; static int *startup_flags = NULL; /* Indicates child closed listener */ static int startup_pipe = -1; /* in child */ /* variables used for privilege separation */ int use_privsep = -1; struct monitor *pmonitor = NULL; int privsep_is_preauth = 1; static int privsep_chroot = 1; /* global connection state and authentication contexts */ Authctxt *the_authctxt = NULL; struct ssh *the_active_state; /* global key/cert auth options. XXX move to permanent ssh->authctxt? */ struct sshauthopt *auth_opts = NULL; /* sshd_config buffer */ struct sshbuf *cfg; /* Included files from the configuration file */ struct include_list includes = TAILQ_HEAD_INITIALIZER(includes); /* message to be displayed after login */ struct sshbuf *loginmsg; /* Unprivileged user */ struct passwd *privsep_pw = NULL; /* Prototypes for various functions defined later in this file. */ void destroy_sensitive_data(void); void demote_sensitive_data(void); static void do_ssh2_kex(struct ssh *); static char *listener_proctitle; /* * Close all listening sockets */ static void close_listen_socks(void) { int i; for (i = 0; i < num_listen_socks; i++) close(listen_socks[i]); num_listen_socks = 0; } static void close_startup_pipes(void) { int i; if (startup_pipes) for (i = 0; i < options.max_startups; i++) if (startup_pipes[i] != -1) close(startup_pipes[i]); } /* * Signal handler for SIGHUP. Sshd execs itself when it receives SIGHUP; * the effect is to reread the configuration file (and to regenerate * the server key). */ static void sighup_handler(int sig) { received_sighup = 1; } /* * Called from the main program after receiving SIGHUP. * Restarts the server. */ static void sighup_restart(void) { logit("Received SIGHUP; restarting."); if (options.pid_file != NULL) unlink(options.pid_file); platform_pre_restart(); close_listen_socks(); close_startup_pipes(); ssh_signal(SIGHUP, SIG_IGN); /* will be restored after exec */ execv(saved_argv[0], saved_argv); logit("RESTART FAILED: av[0]='%.100s', error: %.100s.", saved_argv[0], strerror(errno)); exit(1); } /* * Generic signal handler for terminating signals in the master daemon. */ static void sigterm_handler(int sig) { received_sigterm = sig; } /* * SIGCHLD handler. This is called whenever a child dies. This will then * reap any zombies left by exited children. */ static void main_sigchld_handler(int sig) { int save_errno = errno; pid_t pid; int status; while ((pid = waitpid(-1, &status, WNOHANG)) > 0 || (pid == -1 && errno == EINTR)) ; errno = save_errno; } /* * Signal handler for the alarm after the login grace period has expired. */ static void grace_alarm_handler(int sig) { /* * Try to kill any processes that we have spawned, E.g. authorized * keys command helpers or privsep children. */ if (getpgid(0) == getpid()) { ssh_signal(SIGTERM, SIG_IGN); kill(0, SIGTERM); } /* Log error and exit. */ sigdie("Timeout before authentication for %s port %d", ssh_remote_ipaddr(the_active_state), ssh_remote_port(the_active_state)); } /* Destroy the host and server keys. They will no longer be needed. */ void destroy_sensitive_data(void) { u_int i; for (i = 0; i < options.num_host_key_files; i++) { if (sensitive_data.host_keys[i]) { sshkey_free(sensitive_data.host_keys[i]); sensitive_data.host_keys[i] = NULL; } if (sensitive_data.host_certificates[i]) { sshkey_free(sensitive_data.host_certificates[i]); sensitive_data.host_certificates[i] = NULL; } } } /* Demote private to public keys for network child */ void demote_sensitive_data(void) { struct sshkey *tmp; u_int i; int r; for (i = 0; i < options.num_host_key_files; i++) { if (sensitive_data.host_keys[i]) { if ((r = sshkey_from_private( sensitive_data.host_keys[i], &tmp)) != 0) fatal_r(r, "could not demote host %s key", sshkey_type(sensitive_data.host_keys[i])); sshkey_free(sensitive_data.host_keys[i]); sensitive_data.host_keys[i] = tmp; } /* Certs do not need demotion */ } } static void reseed_prngs(void) { u_int32_t rnd[256]; #ifdef WITH_OPENSSL RAND_poll(); #endif arc4random_stir(); /* noop on recent arc4random() implementations */ arc4random_buf(rnd, sizeof(rnd)); /* let arc4random notice PID change */ #ifdef WITH_OPENSSL RAND_seed(rnd, sizeof(rnd)); /* give libcrypto a chance to notice the PID change */ if ((RAND_bytes((u_char *)rnd, 1)) != 1) fatal("%s: RAND_bytes failed", __func__); #endif explicit_bzero(rnd, sizeof(rnd)); } static void privsep_preauth_child(void) { gid_t gidset[1]; /* Enable challenge-response authentication for privilege separation */ privsep_challenge_enable(); #ifdef GSSAPI /* Cache supported mechanism OIDs for later use */ ssh_gssapi_prepare_supported_oids(); #endif reseed_prngs(); /* Demote the private keys to public keys. */ demote_sensitive_data(); /* Demote the child */ if (privsep_chroot) { /* Change our root directory */ if (chroot(_PATH_PRIVSEP_CHROOT_DIR) == -1) fatal("chroot(\"%s\"): %s", _PATH_PRIVSEP_CHROOT_DIR, strerror(errno)); if (chdir("/") == -1) fatal("chdir(\"/\"): %s", strerror(errno)); /* Drop our privileges */ debug3("privsep user:group %u:%u", (u_int)privsep_pw->pw_uid, (u_int)privsep_pw->pw_gid); gidset[0] = privsep_pw->pw_gid; if (setgroups(1, gidset) == -1) fatal("setgroups: %.100s", strerror(errno)); permanently_set_uid(privsep_pw); } } static int privsep_preauth(struct ssh *ssh) { int status, r; pid_t pid; struct ssh_sandbox *box = NULL; /* Set up unprivileged child process to deal with network data */ pmonitor = monitor_init(); /* Store a pointer to the kex for later rekeying */ pmonitor->m_pkex = &ssh->kex; if (use_privsep == PRIVSEP_ON) box = ssh_sandbox_init(pmonitor); pid = fork(); if (pid == -1) { fatal("fork of unprivileged child failed"); } else if (pid != 0) { debug2("Network child is on pid %ld", (long)pid); pmonitor->m_pid = pid; if (have_agent) { r = ssh_get_authentication_socket(&auth_sock); if (r != 0) { error_r(r, "Could not get agent socket"); have_agent = 0; } } if (box != NULL) ssh_sandbox_parent_preauth(box, pid); monitor_child_preauth(ssh, pmonitor); /* Wait for the child's exit status */ while (waitpid(pid, &status, 0) == -1) { if (errno == EINTR) continue; pmonitor->m_pid = -1; fatal_f("waitpid: %s", strerror(errno)); } privsep_is_preauth = 0; pmonitor->m_pid = -1; if (WIFEXITED(status)) { if (WEXITSTATUS(status) != 0) fatal_f("preauth child exited with status %d", WEXITSTATUS(status)); } else if (WIFSIGNALED(status)) fatal_f("preauth child terminated by signal %d", WTERMSIG(status)); if (box != NULL) ssh_sandbox_parent_finish(box); return 1; } else { /* child */ close(pmonitor->m_sendfd); close(pmonitor->m_log_recvfd); /* Arrange for logging to be sent to the monitor */ set_log_handler(mm_log_handler, pmonitor); privsep_preauth_child(); setproctitle("%s", "[net]"); if (box != NULL) ssh_sandbox_child(box); return 0; } } static void privsep_postauth(struct ssh *ssh, Authctxt *authctxt) { #ifdef DISABLE_FD_PASSING if (1) { #else if (authctxt->pw->pw_uid == 0) { #endif /* File descriptor passing is broken or root login */ use_privsep = 0; goto skip; } /* New socket pair */ monitor_reinit(pmonitor); pmonitor->m_pid = fork(); if (pmonitor->m_pid == -1) fatal("fork of unprivileged child failed"); else if (pmonitor->m_pid != 0) { verbose("User child is on pid %ld", (long)pmonitor->m_pid); sshbuf_reset(loginmsg); monitor_clear_keystate(ssh, pmonitor); monitor_child_postauth(ssh, pmonitor); /* NEVERREACHED */ exit(0); } /* child */ close(pmonitor->m_sendfd); pmonitor->m_sendfd = -1; /* Demote the private keys to public keys. */ demote_sensitive_data(); reseed_prngs(); /* Drop privileges */ do_setusercontext(authctxt->pw); skip: /* It is safe now to apply the key state */ monitor_apply_keystate(ssh, pmonitor); /* * Tell the packet layer that authentication was successful, since * this information is not part of the key state. */ ssh_packet_set_authenticated(ssh); } static void append_hostkey_type(struct sshbuf *b, const char *s) { int r; if (match_pattern_list(s, options.hostkeyalgorithms, 0) != 1) { debug3_f("%s key not permitted by HostkeyAlgorithms", s); return; } if ((r = sshbuf_putf(b, "%s%s", sshbuf_len(b) > 0 ? "," : "", s)) != 0) fatal_fr(r, "sshbuf_putf"); } static char * list_hostkey_types(void) { struct sshbuf *b; struct sshkey *key; char *ret; u_int i; if ((b = sshbuf_new()) == NULL) fatal_f("sshbuf_new failed"); for (i = 0; i < options.num_host_key_files; i++) { key = sensitive_data.host_keys[i]; if (key == NULL) key = sensitive_data.host_pubkeys[i]; if (key == NULL) continue; switch (key->type) { case KEY_RSA: /* for RSA we also support SHA2 signatures */ append_hostkey_type(b, "rsa-sha2-512"); append_hostkey_type(b, "rsa-sha2-256"); /* FALLTHROUGH */ case KEY_DSA: case KEY_ECDSA: case KEY_ED25519: case KEY_ECDSA_SK: case KEY_ED25519_SK: case KEY_XMSS: append_hostkey_type(b, sshkey_ssh_name(key)); break; } /* If the private key has a cert peer, then list that too */ key = sensitive_data.host_certificates[i]; if (key == NULL) continue; switch (key->type) { case KEY_RSA_CERT: /* for RSA we also support SHA2 signatures */ append_hostkey_type(b, "rsa-sha2-512-cert-v01@openssh.com"); append_hostkey_type(b, "rsa-sha2-256-cert-v01@openssh.com"); /* FALLTHROUGH */ case KEY_DSA_CERT: case KEY_ECDSA_CERT: case KEY_ED25519_CERT: case KEY_ECDSA_SK_CERT: case KEY_ED25519_SK_CERT: case KEY_XMSS_CERT: append_hostkey_type(b, sshkey_ssh_name(key)); break; } } if ((ret = sshbuf_dup_string(b)) == NULL) fatal_f("sshbuf_dup_string failed"); sshbuf_free(b); debug_f("%s", ret); return ret; } static struct sshkey * get_hostkey_by_type(int type, int nid, int need_private, struct ssh *ssh) { u_int i; struct sshkey *key; for (i = 0; i < options.num_host_key_files; i++) { switch (type) { case KEY_RSA_CERT: case KEY_DSA_CERT: case KEY_ECDSA_CERT: case KEY_ED25519_CERT: case KEY_ECDSA_SK_CERT: case KEY_ED25519_SK_CERT: case KEY_XMSS_CERT: key = sensitive_data.host_certificates[i]; break; default: key = sensitive_data.host_keys[i]; if (key == NULL && !need_private) key = sensitive_data.host_pubkeys[i]; break; } if (key == NULL || key->type != type) continue; switch (type) { case KEY_ECDSA: case KEY_ECDSA_SK: case KEY_ECDSA_CERT: case KEY_ECDSA_SK_CERT: if (key->ecdsa_nid != nid) continue; /* FALLTHROUGH */ default: return need_private ? sensitive_data.host_keys[i] : key; } } return NULL; } struct sshkey * get_hostkey_public_by_type(int type, int nid, struct ssh *ssh) { return get_hostkey_by_type(type, nid, 0, ssh); } struct sshkey * get_hostkey_private_by_type(int type, int nid, struct ssh *ssh) { return get_hostkey_by_type(type, nid, 1, ssh); } struct sshkey * get_hostkey_by_index(int ind) { if (ind < 0 || (u_int)ind >= options.num_host_key_files) return (NULL); return (sensitive_data.host_keys[ind]); } struct sshkey * get_hostkey_public_by_index(int ind, struct ssh *ssh) { if (ind < 0 || (u_int)ind >= options.num_host_key_files) return (NULL); return (sensitive_data.host_pubkeys[ind]); } int get_hostkey_index(struct sshkey *key, int compare, struct ssh *ssh) { u_int i; for (i = 0; i < options.num_host_key_files; i++) { if (sshkey_is_cert(key)) { if (key == sensitive_data.host_certificates[i] || (compare && sensitive_data.host_certificates[i] && sshkey_equal(key, sensitive_data.host_certificates[i]))) return (i); } else { if (key == sensitive_data.host_keys[i] || (compare && sensitive_data.host_keys[i] && sshkey_equal(key, sensitive_data.host_keys[i]))) return (i); if (key == sensitive_data.host_pubkeys[i] || (compare && sensitive_data.host_pubkeys[i] && sshkey_equal(key, sensitive_data.host_pubkeys[i]))) return (i); } } return (-1); } /* Inform the client of all hostkeys */ static void notify_hostkeys(struct ssh *ssh) { struct sshbuf *buf; struct sshkey *key; u_int i, nkeys; int r; char *fp; /* Some clients cannot cope with the hostkeys message, skip those. */ if (ssh->compat & SSH_BUG_HOSTKEYS) return; if ((buf = sshbuf_new()) == NULL) fatal_f("sshbuf_new"); for (i = nkeys = 0; i < options.num_host_key_files; i++) { key = get_hostkey_public_by_index(i, ssh); if (key == NULL || key->type == KEY_UNSPEC || sshkey_is_cert(key)) continue; fp = sshkey_fingerprint(key, options.fingerprint_hash, SSH_FP_DEFAULT); debug3_f("key %d: %s %s", i, sshkey_ssh_name(key), fp); free(fp); if (nkeys == 0) { /* * Start building the request when we find the * first usable key. */ if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || (r = sshpkt_put_cstring(ssh, "hostkeys-00@openssh.com")) != 0 || (r = sshpkt_put_u8(ssh, 0)) != 0) /* want reply */ sshpkt_fatal(ssh, r, "%s: start request", __func__); } /* Append the key to the request */ sshbuf_reset(buf); if ((r = sshkey_putb(key, buf)) != 0) fatal_fr(r, "couldn't put hostkey %d", i); if ((r = sshpkt_put_stringb(ssh, buf)) != 0) sshpkt_fatal(ssh, r, "%s: append key", __func__); nkeys++; } debug3_f("sent %u hostkeys", nkeys); if (nkeys == 0) fatal_f("no hostkeys"); if ((r = sshpkt_send(ssh)) != 0) sshpkt_fatal(ssh, r, "%s: send", __func__); sshbuf_free(buf); } /* * returns 1 if connection should be dropped, 0 otherwise. * dropping starts at connection #max_startups_begin with a probability * of (max_startups_rate/100). the probability increases linearly until * all connections are dropped for startups > max_startups */ static int should_drop_connection(int startups) { int p, r; if (startups < options.max_startups_begin) return 0; if (startups >= options.max_startups) return 1; if (options.max_startups_rate == 100) return 1; p = 100 - options.max_startups_rate; p *= startups - options.max_startups_begin; p /= options.max_startups - options.max_startups_begin; p += options.max_startups_rate; r = arc4random_uniform(100); debug_f("p %d, r %d", p, r); return (r < p) ? 1 : 0; } /* * Check whether connection should be accepted by MaxStartups. * Returns 0 if the connection is accepted. If the connection is refused, * returns 1 and attempts to send notification to client. * Logs when the MaxStartups condition is entered or exited, and periodically * while in that state. */ static int drop_connection(int sock, int startups, int notify_pipe) { char *laddr, *raddr; const char msg[] = "Exceeded MaxStartups\r\n"; static time_t last_drop, first_drop; static u_int ndropped; LogLevel drop_level = SYSLOG_LEVEL_VERBOSE; time_t now; now = monotime(); if (!should_drop_connection(startups) && srclimit_check_allow(sock, notify_pipe) == 1) { if (last_drop != 0 && startups < options.max_startups_begin - 1) { /* XXX maybe need better hysteresis here */ logit("exited MaxStartups throttling after %s, " "%u connections dropped", fmt_timeframe(now - first_drop), ndropped); last_drop = 0; } return 0; } #define SSHD_MAXSTARTUPS_LOG_INTERVAL (5 * 60) if (last_drop == 0) { error("beginning MaxStartups throttling"); drop_level = SYSLOG_LEVEL_INFO; first_drop = now; ndropped = 0; } else if (last_drop + SSHD_MAXSTARTUPS_LOG_INTERVAL < now) { /* Periodic logs */ error("in MaxStartups throttling for %s, " "%u connections dropped", fmt_timeframe(now - first_drop), ndropped + 1); drop_level = SYSLOG_LEVEL_INFO; } last_drop = now; ndropped++; laddr = get_local_ipaddr(sock); raddr = get_peer_ipaddr(sock); do_log2(drop_level, "drop connection #%d from [%s]:%d on [%s]:%d " "past MaxStartups", startups, raddr, get_peer_port(sock), laddr, get_local_port(sock)); free(laddr); free(raddr); /* best-effort notification to client */ (void)write(sock, msg, sizeof(msg) - 1); return 1; } static void usage(void) { fprintf(stderr, "%s, %s\n", SSH_RELEASE, SSH_OPENSSL_VERSION); fprintf(stderr, "usage: sshd [-46DdeGiqTtV] [-C connection_spec] [-c host_cert_file]\n" " [-E log_file] [-f config_file] [-g login_grace_time]\n" " [-h host_key_file] [-o option] [-p port] [-u len]\n" ); exit(1); } static void send_rexec_state(int fd, struct sshbuf *conf) { struct sshbuf *m = NULL, *inc = NULL; struct include_item *item = NULL; int r; debug3_f("entering fd = %d config len %zu", fd, sshbuf_len(conf)); if ((m = sshbuf_new()) == NULL || (inc = sshbuf_new()) == NULL) fatal_f("sshbuf_new failed"); /* pack includes into a string */ TAILQ_FOREACH(item, &includes, entry) { if ((r = sshbuf_put_cstring(inc, item->selector)) != 0 || (r = sshbuf_put_cstring(inc, item->filename)) != 0 || (r = sshbuf_put_stringb(inc, item->contents)) != 0) fatal_fr(r, "compose includes"); } /* * Protocol from reexec master to child: * string configuration * string included_files[] { * string selector * string filename * string contents * } */ if ((r = sshbuf_put_stringb(m, conf)) != 0 || (r = sshbuf_put_stringb(m, inc)) != 0) fatal_fr(r, "compose config"); if (ssh_msg_send(fd, 0, m) == -1) error_f("ssh_msg_send failed"); sshbuf_free(m); sshbuf_free(inc); debug3_f("done"); } static void recv_rexec_state(int fd, struct sshbuf *conf) { struct sshbuf *m, *inc; u_char *cp, ver; size_t len; int r; struct include_item *item; debug3_f("entering fd = %d", fd); if ((m = sshbuf_new()) == NULL || (inc = sshbuf_new()) == NULL) fatal_f("sshbuf_new failed"); if (ssh_msg_recv(fd, m) == -1) fatal_f("ssh_msg_recv failed"); if ((r = sshbuf_get_u8(m, &ver)) != 0) fatal_fr(r, "parse version"); if (ver != 0) fatal_f("rexec version mismatch"); if ((r = sshbuf_get_string(m, &cp, &len)) != 0 || (r = sshbuf_get_stringb(m, inc)) != 0) fatal_fr(r, "parse config"); if (conf != NULL && (r = sshbuf_put(conf, cp, len))) fatal_fr(r, "sshbuf_put"); while (sshbuf_len(inc) != 0) { item = xcalloc(1, sizeof(*item)); if ((item->contents = sshbuf_new()) == NULL) fatal_f("sshbuf_new failed"); if ((r = sshbuf_get_cstring(inc, &item->selector, NULL)) != 0 || (r = sshbuf_get_cstring(inc, &item->filename, NULL)) != 0 || (r = sshbuf_get_stringb(inc, item->contents)) != 0) fatal_fr(r, "parse includes"); TAILQ_INSERT_TAIL(&includes, item, entry); } free(cp); sshbuf_free(m); debug3_f("done"); } /* Accept a connection from inetd */ static void server_accept_inetd(int *sock_in, int *sock_out) { if (rexeced_flag) { close(REEXEC_CONFIG_PASS_FD); *sock_in = *sock_out = dup(STDIN_FILENO); } else { *sock_in = dup(STDIN_FILENO); *sock_out = dup(STDOUT_FILENO); } /* * We intentionally do not close the descriptors 0, 1, and 2 * as our code for setting the descriptors won't work if * ttyfd happens to be one of those. */ if (stdfd_devnull(1, 1, !log_stderr) == -1) error_f("stdfd_devnull failed"); debug("inetd sockets after dupping: %d, %d", *sock_in, *sock_out); } /* * Listen for TCP connections */ static void listen_on_addrs(struct listenaddr *la) { int ret, listen_sock; struct addrinfo *ai; char ntop[NI_MAXHOST], strport[NI_MAXSERV]; for (ai = la->addrs; ai; ai = ai->ai_next) { if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) continue; if (num_listen_socks >= MAX_LISTEN_SOCKS) fatal("Too many listen sockets. " "Enlarge MAX_LISTEN_SOCKS"); if ((ret = getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV)) != 0) { error("getnameinfo failed: %.100s", ssh_gai_strerror(ret)); continue; } /* Create socket for listening. */ listen_sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (listen_sock == -1) { /* kernel may not support ipv6 */ verbose("socket: %.100s", strerror(errno)); continue; } if (set_nonblock(listen_sock) == -1) { close(listen_sock); continue; } if (fcntl(listen_sock, F_SETFD, FD_CLOEXEC) == -1) { verbose("socket: CLOEXEC: %s", strerror(errno)); close(listen_sock); continue; } /* Socket options */ set_reuseaddr(listen_sock); if (la->rdomain != NULL && set_rdomain(listen_sock, la->rdomain) == -1) { close(listen_sock); continue; } /* Only communicate in IPv6 over AF_INET6 sockets. */ if (ai->ai_family == AF_INET6) sock_set_v6only(listen_sock); debug("Bind to port %s on %s.", strport, ntop); /* Bind the socket to the desired port. */ if (bind(listen_sock, ai->ai_addr, ai->ai_addrlen) == -1) { error("Bind to port %s on %s failed: %.200s.", strport, ntop, strerror(errno)); close(listen_sock); continue; } listen_socks[num_listen_socks] = listen_sock; num_listen_socks++; /* Start listening on the port. */ if (listen(listen_sock, SSH_LISTEN_BACKLOG) == -1) fatal("listen on [%s]:%s: %.100s", ntop, strport, strerror(errno)); logit("Server listening on %s port %s%s%s.", ntop, strport, la->rdomain == NULL ? "" : " rdomain ", la->rdomain == NULL ? "" : la->rdomain); } } static void server_listen(void) { u_int i; /* Initialise per-source limit tracking. */ srclimit_init(options.max_startups, options.per_source_max_startups, options.per_source_masklen_ipv4, options.per_source_masklen_ipv6); for (i = 0; i < options.num_listen_addrs; i++) { listen_on_addrs(&options.listen_addrs[i]); freeaddrinfo(options.listen_addrs[i].addrs); free(options.listen_addrs[i].rdomain); memset(&options.listen_addrs[i], 0, sizeof(options.listen_addrs[i])); } free(options.listen_addrs); options.listen_addrs = NULL; options.num_listen_addrs = 0; if (!num_listen_socks) fatal("Cannot bind any address."); } /* * The main TCP accept loop. Note that, for the non-debug case, returns * from this function are in a forked subprocess. */ static void server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s) { struct pollfd *pfd = NULL; int i, j, ret, npfd; int ostartups = -1, startups = 0, listening = 0, lameduck = 0; int startup_p[2] = { -1 , -1 }, *startup_pollfd; char c = 0; struct sockaddr_storage from; socklen_t fromlen; pid_t pid; u_char rnd[256]; sigset_t nsigset, osigset; /* pipes connected to unauthenticated child sshd processes */ startup_pipes = xcalloc(options.max_startups, sizeof(int)); startup_flags = xcalloc(options.max_startups, sizeof(int)); startup_pollfd = xcalloc(options.max_startups, sizeof(int)); for (i = 0; i < options.max_startups; i++) startup_pipes[i] = -1; /* * Prepare signal mask that we use to block signals that might set * received_sigterm or received_sighup, so that we are guaranteed * to immediately wake up the ppoll if a signal is received after * the flag is checked. */ sigemptyset(&nsigset); sigaddset(&nsigset, SIGHUP); sigaddset(&nsigset, SIGCHLD); sigaddset(&nsigset, SIGTERM); sigaddset(&nsigset, SIGQUIT); /* sized for worst-case */ pfd = xcalloc(num_listen_socks + options.max_startups, sizeof(struct pollfd)); /* * Stay listening for connections until the system crashes or * the daemon is killed with a signal. */ for (;;) { sigprocmask(SIG_BLOCK, &nsigset, &osigset); if (received_sigterm) { logit("Received signal %d; terminating.", (int) received_sigterm); close_listen_socks(); if (options.pid_file != NULL) unlink(options.pid_file); exit(received_sigterm == SIGTERM ? 0 : 255); } if (ostartups != startups) { setproctitle("%s [listener] %d of %d-%d startups", listener_proctitle, startups, options.max_startups_begin, options.max_startups); ostartups = startups; } if (received_sighup) { if (!lameduck) { debug("Received SIGHUP; waiting for children"); close_listen_socks(); lameduck = 1; } if (listening <= 0) { sigprocmask(SIG_SETMASK, &osigset, NULL); sighup_restart(); } } for (i = 0; i < num_listen_socks; i++) { pfd[i].fd = listen_socks[i]; pfd[i].events = POLLIN; } npfd = num_listen_socks; for (i = 0; i < options.max_startups; i++) { startup_pollfd[i] = -1; if (startup_pipes[i] != -1) { pfd[npfd].fd = startup_pipes[i]; pfd[npfd].events = POLLIN; startup_pollfd[i] = npfd++; } } /* Wait until a connection arrives or a child exits. */ ret = ppoll(pfd, npfd, NULL, &osigset); if (ret == -1 && errno != EINTR) { error("ppoll: %.100s", strerror(errno)); if (errno == EINVAL) cleanup_exit(1); /* can't recover */ } sigprocmask(SIG_SETMASK, &osigset, NULL); if (ret == -1) continue; for (i = 0; i < options.max_startups; i++) { if (startup_pipes[i] == -1 || startup_pollfd[i] == -1 || !(pfd[startup_pollfd[i]].revents & (POLLIN|POLLHUP))) continue; switch (read(startup_pipes[i], &c, sizeof(c))) { case -1: if (errno == EINTR || errno == EAGAIN) continue; if (errno != EPIPE) { error_f("startup pipe %d (fd=%d): " "read %s", i, startup_pipes[i], strerror(errno)); } /* FALLTHROUGH */ case 0: /* child exited or completed auth */ close(startup_pipes[i]); srclimit_done(startup_pipes[i]); startup_pipes[i] = -1; startups--; if (startup_flags[i]) listening--; break; case 1: /* child has finished preliminaries */ if (startup_flags[i]) { listening--; startup_flags[i] = 0; } break; } } for (i = 0; i < num_listen_socks; i++) { if (!(pfd[i].revents & POLLIN)) continue; fromlen = sizeof(from); *newsock = accept(listen_socks[i], (struct sockaddr *)&from, &fromlen); if (*newsock == -1) { if (errno != EINTR && errno != EWOULDBLOCK && errno != ECONNABORTED && errno != EAGAIN) error("accept: %.100s", strerror(errno)); if (errno == EMFILE || errno == ENFILE) usleep(100 * 1000); continue; } if (unset_nonblock(*newsock) == -1) { close(*newsock); continue; } if (pipe(startup_p) == -1) { error_f("pipe(startup_p): %s", strerror(errno)); close(*newsock); continue; } if (drop_connection(*newsock, startups, startup_p[0])) { close(*newsock); close(startup_p[0]); close(startup_p[1]); continue; } if (rexec_flag && socketpair(AF_UNIX, SOCK_STREAM, 0, config_s) == -1) { error("reexec socketpair: %s", strerror(errno)); close(*newsock); close(startup_p[0]); close(startup_p[1]); continue; } for (j = 0; j < options.max_startups; j++) if (startup_pipes[j] == -1) { startup_pipes[j] = startup_p[0]; startups++; startup_flags[j] = 1; break; } /* * Got connection. Fork a child to handle it, unless * we are in debugging mode. */ if (debug_flag) { /* * In debugging mode. Close the listening * socket, and start processing the * connection without forking. */ debug("Server will not fork when running in debugging mode."); close_listen_socks(); *sock_in = *newsock; *sock_out = *newsock; close(startup_p[0]); close(startup_p[1]); startup_pipe = -1; pid = getpid(); if (rexec_flag) { send_rexec_state(config_s[0], cfg); close(config_s[0]); } free(pfd); return; } /* * Normal production daemon. Fork, and have * the child process the connection. The * parent continues listening. */ platform_pre_fork(); listening++; if ((pid = fork()) == 0) { /* * Child. Close the listening and * max_startup sockets. Start using * the accepted socket. Reinitialize * logging (since our pid has changed). * We return from this function to handle * the connection. */ platform_post_fork_child(); startup_pipe = startup_p[1]; close_startup_pipes(); close_listen_socks(); *sock_in = *newsock; *sock_out = *newsock; log_init(__progname, options.log_level, options.log_facility, log_stderr); if (rexec_flag) close(config_s[0]); else { /* * Signal parent that the preliminaries * for this child are complete. For the * re-exec case, this happens after the * child has received the rexec state * from the server. */ (void)atomicio(vwrite, startup_pipe, "\0", 1); } free(pfd); return; } /* Parent. Stay in the loop. */ platform_post_fork_parent(pid); if (pid == -1) error("fork: %.100s", strerror(errno)); else debug("Forked child %ld.", (long)pid); close(startup_p[1]); if (rexec_flag) { close(config_s[1]); send_rexec_state(config_s[0], cfg); close(config_s[0]); } close(*newsock); /* * Ensure that our random state differs * from that of the child */ arc4random_stir(); arc4random_buf(rnd, sizeof(rnd)); #ifdef WITH_OPENSSL RAND_seed(rnd, sizeof(rnd)); if ((RAND_bytes((u_char *)rnd, 1)) != 1) fatal("%s: RAND_bytes failed", __func__); #endif explicit_bzero(rnd, sizeof(rnd)); } } } /* * If IP options are supported, make sure there are none (log and * return an error if any are found). Basically we are worried about * source routing; it can be used to pretend you are somebody * (ip-address) you are not. That itself may be "almost acceptable" * under certain circumstances, but rhosts authentication is useless * if source routing is accepted. Notice also that if we just dropped * source routing here, the other side could use IP spoofing to do * rest of the interaction and could still bypass security. So we * exit here if we detect any IP options. */ static void check_ip_options(struct ssh *ssh) { #ifdef IP_OPTIONS int sock_in = ssh_packet_get_connection_in(ssh); struct sockaddr_storage from; u_char opts[200]; socklen_t i, option_size = sizeof(opts), fromlen = sizeof(from); char text[sizeof(opts) * 3 + 1]; memset(&from, 0, sizeof(from)); if (getpeername(sock_in, (struct sockaddr *)&from, &fromlen) == -1) return; if (from.ss_family != AF_INET) return; /* XXX IPv6 options? */ if (getsockopt(sock_in, IPPROTO_IP, IP_OPTIONS, opts, &option_size) >= 0 && option_size != 0) { text[0] = '\0'; for (i = 0; i < option_size; i++) snprintf(text + i*3, sizeof(text) - i*3, " %2.2x", opts[i]); fatal("Connection from %.100s port %d with IP opts: %.800s", ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), text); } return; #endif /* IP_OPTIONS */ } /* Set the routing domain for this process */ static void set_process_rdomain(struct ssh *ssh, const char *name) { #if defined(HAVE_SYS_SET_PROCESS_RDOMAIN) if (name == NULL) return; /* default */ if (strcmp(name, "%D") == 0) { /* "expands" to routing domain of connection */ if ((name = ssh_packet_rdomain_in(ssh)) == NULL) return; } /* NB. We don't pass 'ssh' to sys_set_process_rdomain() */ return sys_set_process_rdomain(name); #elif defined(__OpenBSD__) int rtable, ortable = getrtable(); const char *errstr; if (name == NULL) return; /* default */ if (strcmp(name, "%D") == 0) { /* "expands" to routing domain of connection */ if ((name = ssh_packet_rdomain_in(ssh)) == NULL) return; } rtable = (int)strtonum(name, 0, 255, &errstr); if (errstr != NULL) /* Shouldn't happen */ fatal("Invalid routing domain \"%s\": %s", name, errstr); if (rtable != ortable && setrtable(rtable) != 0) fatal("Unable to set routing domain %d: %s", rtable, strerror(errno)); debug_f("set routing domain %d (was %d)", rtable, ortable); #else /* defined(__OpenBSD__) */ fatal("Unable to set routing domain: not supported in this platform"); #endif } static void accumulate_host_timing_secret(struct sshbuf *server_cfg, struct sshkey *key) { static struct ssh_digest_ctx *ctx; u_char *hash; size_t len; struct sshbuf *buf; int r; if (ctx == NULL && (ctx = ssh_digest_start(SSH_DIGEST_SHA512)) == NULL) fatal_f("ssh_digest_start"); if (key == NULL) { /* finalize */ /* add server config in case we are using agent for host keys */ if (ssh_digest_update(ctx, sshbuf_ptr(server_cfg), sshbuf_len(server_cfg)) != 0) fatal_f("ssh_digest_update"); len = ssh_digest_bytes(SSH_DIGEST_SHA512); hash = xmalloc(len); if (ssh_digest_final(ctx, hash, len) != 0) fatal_f("ssh_digest_final"); options.timing_secret = PEEK_U64(hash); freezero(hash, len); ssh_digest_free(ctx); ctx = NULL; return; } if ((buf = sshbuf_new()) == NULL) fatal_f("could not allocate buffer"); if ((r = sshkey_private_serialize(key, buf)) != 0) fatal_fr(r, "encode %s key", sshkey_ssh_name(key)); if (ssh_digest_update(ctx, sshbuf_ptr(buf), sshbuf_len(buf)) != 0) fatal_f("ssh_digest_update"); sshbuf_reset(buf); sshbuf_free(buf); } static char * prepare_proctitle(int ac, char **av) { char *ret = NULL; int i; for (i = 0; i < ac; i++) xextendf(&ret, " ", "%s", av[i]); return ret; } static void print_config(struct ssh *ssh, struct connection_info *connection_info) { /* * If no connection info was provided by -C then use * use a blank one that will cause no predicate to match. */ if (connection_info == NULL) connection_info = get_connection_info(ssh, 0, 0); connection_info->test = 1; parse_server_match_config(&options, &includes, connection_info); dump_config(&options); exit(0); } /* * Main program for the daemon. */ int main(int ac, char **av) { struct ssh *ssh = NULL; extern char *optarg; extern int optind; int r, opt, on = 1, do_dump_cfg = 0, already_daemon, remote_port; int sock_in = -1, sock_out = -1, newsock = -1; const char *remote_ip, *rdomain; char *fp, *line, *laddr, *logfile = NULL; int config_s[2] = { -1 , -1 }; u_int i, j; u_int64_t ibytes, obytes; mode_t new_umask; struct sshkey *key; struct sshkey *pubkey; int keytype; Authctxt *authctxt; struct connection_info *connection_info = NULL; sigset_t sigmask; #ifdef HAVE_SECUREWARE (void)set_auth_parameters(ac, av); #endif __progname = ssh_get_progname(av[0]); sigemptyset(&sigmask); sigprocmask(SIG_SETMASK, &sigmask, NULL); /* Save argv. Duplicate so setproctitle emulation doesn't clobber it */ saved_argc = ac; rexec_argc = ac; saved_argv = xcalloc(ac + 1, sizeof(*saved_argv)); for (i = 0; (int)i < ac; i++) saved_argv[i] = xstrdup(av[i]); saved_argv[i] = NULL; #ifndef HAVE_SETPROCTITLE /* Prepare for later setproctitle emulation */ compat_init_setproctitle(ac, av); av = saved_argv; #endif if (geteuid() == 0 && setgroups(0, NULL) == -1) debug("setgroups(): %.200s", strerror(errno)); /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ sanitise_stdfd(); /* Initialize configuration options to their default values. */ initialize_server_options(&options); /* Parse command-line arguments. */ while ((opt = getopt(ac, av, "C:E:b:c:f:g:h:k:o:p:u:46DGQRTdeiqrtV")) != -1) { switch (opt) { case '4': options.address_family = AF_INET; break; case '6': options.address_family = AF_INET6; break; case 'f': config_file_name = optarg; break; case 'c': servconf_add_hostcert("[command-line]", 0, &options, optarg); break; case 'd': if (debug_flag == 0) { debug_flag = 1; options.log_level = SYSLOG_LEVEL_DEBUG1; } else if (options.log_level < SYSLOG_LEVEL_DEBUG3) options.log_level++; break; case 'D': no_daemon_flag = 1; break; case 'G': do_dump_cfg = 1; break; case 'E': logfile = optarg; /* FALLTHROUGH */ case 'e': log_stderr = 1; break; case 'i': inetd_flag = 1; break; case 'r': rexec_flag = 0; break; case 'R': rexeced_flag = 1; inetd_flag = 1; break; case 'Q': /* ignored */ break; case 'q': options.log_level = SYSLOG_LEVEL_QUIET; break; case 'b': /* protocol 1, ignored */ break; case 'p': options.ports_from_cmdline = 1; if (options.num_ports >= MAX_PORTS) { fprintf(stderr, "too many ports.\n"); exit(1); } options.ports[options.num_ports++] = a2port(optarg); if (options.ports[options.num_ports-1] <= 0) { fprintf(stderr, "Bad port number.\n"); exit(1); } break; case 'g': if ((options.login_grace_time = convtime(optarg)) == -1) { fprintf(stderr, "Invalid login grace time.\n"); exit(1); } break; case 'k': /* protocol 1, ignored */ break; case 'h': servconf_add_hostkey("[command-line]", 0, &options, optarg, 1); break; case 't': test_flag = 1; break; case 'T': test_flag = 2; break; case 'C': connection_info = get_connection_info(ssh, 0, 0); if (parse_server_match_testspec(connection_info, optarg) == -1) exit(1); break; case 'u': utmp_len = (u_int)strtonum(optarg, 0, HOST_NAME_MAX+1+1, NULL); if (utmp_len > HOST_NAME_MAX+1) { fprintf(stderr, "Invalid utmp length.\n"); exit(1); } break; case 'o': line = xstrdup(optarg); if (process_server_config_line(&options, line, "command-line", 0, NULL, NULL, &includes) != 0) exit(1); free(line); break; case 'V': fprintf(stderr, "%s, %s\n", - SSH_VERSION, SSH_OPENSSL_VERSION); + SSH_RELEASE, SSH_OPENSSL_VERSION); exit(0); default: usage(); break; } } if (rexeced_flag || inetd_flag) rexec_flag = 0; if (!test_flag && !do_dump_cfg && rexec_flag && !path_absolute(av[0])) fatal("sshd re-exec requires execution with an absolute path"); if (rexeced_flag) closefrom(REEXEC_MIN_FREE_FD); else closefrom(REEXEC_DEVCRYPTO_RESERVED_FD); seed_rng(); /* If requested, redirect the logs to the specified logfile. */ if (logfile != NULL) log_redirect_stderr_to(logfile); /* * Force logging to stderr until we have loaded the private host * key (unless started from inetd) */ log_init(__progname, options.log_level == SYSLOG_LEVEL_NOT_SET ? SYSLOG_LEVEL_INFO : options.log_level, options.log_facility == SYSLOG_FACILITY_NOT_SET ? SYSLOG_FACILITY_AUTH : options.log_facility, log_stderr || !inetd_flag || debug_flag); /* * Unset KRB5CCNAME, otherwise the user's session may inherit it from * root's environment */ if (getenv("KRB5CCNAME") != NULL) (void) unsetenv("KRB5CCNAME"); sensitive_data.have_ssh2_key = 0; /* * If we're not doing an extended test do not silently ignore connection * test params. */ if (test_flag < 2 && connection_info != NULL) fatal("Config test connection parameter (-C) provided without " "test mode (-T)"); /* Fetch our configuration */ if ((cfg = sshbuf_new()) == NULL) fatal_f("sshbuf_new failed"); if (rexeced_flag) { setproctitle("%s", "[rexeced]"); recv_rexec_state(REEXEC_CONFIG_PASS_FD, cfg); if (!debug_flag) { startup_pipe = dup(REEXEC_STARTUP_PIPE_FD); close(REEXEC_STARTUP_PIPE_FD); /* * Signal parent that this child is at a point where * they can go away if they have a SIGHUP pending. */ (void)atomicio(vwrite, startup_pipe, "\0", 1); } } else if (strcasecmp(config_file_name, "none") != 0) load_server_config(config_file_name, cfg); parse_server_config(&options, rexeced_flag ? "rexec" : config_file_name, cfg, &includes, NULL, rexeced_flag); #ifdef WITH_OPENSSL if (options.moduli_file != NULL) dh_set_moduli_file(options.moduli_file); #endif /* Fill in default values for those options not explicitly set. */ fill_default_server_options(&options); /* Check that options are sensible */ if (options.authorized_keys_command_user == NULL && (options.authorized_keys_command != NULL && strcasecmp(options.authorized_keys_command, "none") != 0)) fatal("AuthorizedKeysCommand set without " "AuthorizedKeysCommandUser"); if (options.authorized_principals_command_user == NULL && (options.authorized_principals_command != NULL && strcasecmp(options.authorized_principals_command, "none") != 0)) fatal("AuthorizedPrincipalsCommand set without " "AuthorizedPrincipalsCommandUser"); /* * Check whether there is any path through configured auth methods. * Unfortunately it is not possible to verify this generally before * daemonisation in the presence of Match block, but this catches * and warns for trivial misconfigurations that could break login. */ if (options.num_auth_methods != 0) { for (i = 0; i < options.num_auth_methods; i++) { if (auth2_methods_valid(options.auth_methods[i], 1) == 0) break; } if (i >= options.num_auth_methods) fatal("AuthenticationMethods cannot be satisfied by " "enabled authentication methods"); } /* Check that there are no remaining arguments. */ if (optind < ac) { fprintf(stderr, "Extra argument %s.\n", av[optind]); exit(1); } debug("sshd version %s, %s", SSH_VERSION, SSH_OPENSSL_VERSION); if (do_dump_cfg) print_config(ssh, connection_info); /* Store privilege separation user for later use if required. */ privsep_chroot = use_privsep && (getuid() == 0 || geteuid() == 0); if ((privsep_pw = getpwnam(SSH_PRIVSEP_USER)) == NULL) { if (privsep_chroot || options.kerberos_authentication) fatal("Privilege separation user %s does not exist", SSH_PRIVSEP_USER); } else { privsep_pw = pwcopy(privsep_pw); freezero(privsep_pw->pw_passwd, strlen(privsep_pw->pw_passwd)); privsep_pw->pw_passwd = xstrdup("*"); } endpwent(); /* load host keys */ sensitive_data.host_keys = xcalloc(options.num_host_key_files, sizeof(struct sshkey *)); sensitive_data.host_pubkeys = xcalloc(options.num_host_key_files, sizeof(struct sshkey *)); if (options.host_key_agent) { if (strcmp(options.host_key_agent, SSH_AUTHSOCKET_ENV_NAME)) setenv(SSH_AUTHSOCKET_ENV_NAME, options.host_key_agent, 1); if ((r = ssh_get_authentication_socket(NULL)) == 0) have_agent = 1; else error_r(r, "Could not connect to agent \"%s\"", options.host_key_agent); } for (i = 0; i < options.num_host_key_files; i++) { int ll = options.host_key_file_userprovided[i] ? SYSLOG_LEVEL_ERROR : SYSLOG_LEVEL_DEBUG1; if (options.host_key_files[i] == NULL) continue; if ((r = sshkey_load_private(options.host_key_files[i], "", &key, NULL)) != 0 && r != SSH_ERR_SYSTEM_ERROR) do_log2_r(r, ll, "Unable to load host key \"%s\"", options.host_key_files[i]); if (sshkey_is_sk(key) && key->sk_flags & SSH_SK_USER_PRESENCE_REQD) { debug("host key %s requires user presence, ignoring", options.host_key_files[i]); key->sk_flags &= ~SSH_SK_USER_PRESENCE_REQD; } if (r == 0 && key != NULL && (r = sshkey_shield_private(key)) != 0) { do_log2_r(r, ll, "Unable to shield host key \"%s\"", options.host_key_files[i]); sshkey_free(key); key = NULL; } if ((r = sshkey_load_public(options.host_key_files[i], &pubkey, NULL)) != 0 && r != SSH_ERR_SYSTEM_ERROR) do_log2_r(r, ll, "Unable to load host key \"%s\"", options.host_key_files[i]); if (pubkey != NULL && key != NULL) { if (!sshkey_equal(pubkey, key)) { error("Public key for %s does not match " "private key", options.host_key_files[i]); sshkey_free(pubkey); pubkey = NULL; } } if (pubkey == NULL && key != NULL) { if ((r = sshkey_from_private(key, &pubkey)) != 0) fatal_r(r, "Could not demote key: \"%s\"", options.host_key_files[i]); } if (pubkey != NULL && (r = sshkey_check_rsa_length(pubkey, options.required_rsa_size)) != 0) { error_fr(r, "Host key %s", options.host_key_files[i]); sshkey_free(pubkey); sshkey_free(key); continue; } sensitive_data.host_keys[i] = key; sensitive_data.host_pubkeys[i] = pubkey; if (key == NULL && pubkey != NULL && have_agent) { debug("will rely on agent for hostkey %s", options.host_key_files[i]); keytype = pubkey->type; } else if (key != NULL) { keytype = key->type; accumulate_host_timing_secret(cfg, key); } else { do_log2(ll, "Unable to load host key: %s", options.host_key_files[i]); sensitive_data.host_keys[i] = NULL; sensitive_data.host_pubkeys[i] = NULL; continue; } switch (keytype) { case KEY_RSA: case KEY_DSA: case KEY_ECDSA: case KEY_ED25519: case KEY_ECDSA_SK: case KEY_ED25519_SK: case KEY_XMSS: if (have_agent || key != NULL) sensitive_data.have_ssh2_key = 1; break; } if ((fp = sshkey_fingerprint(pubkey, options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) fatal("sshkey_fingerprint failed"); debug("%s host key #%d: %s %s", key ? "private" : "agent", i, sshkey_ssh_name(pubkey), fp); free(fp); } accumulate_host_timing_secret(cfg, NULL); if (!sensitive_data.have_ssh2_key) { logit("sshd: no hostkeys available -- exiting."); exit(1); } /* * Load certificates. They are stored in an array at identical * indices to the public keys that they relate to. */ sensitive_data.host_certificates = xcalloc(options.num_host_key_files, sizeof(struct sshkey *)); for (i = 0; i < options.num_host_key_files; i++) sensitive_data.host_certificates[i] = NULL; for (i = 0; i < options.num_host_cert_files; i++) { if (options.host_cert_files[i] == NULL) continue; if ((r = sshkey_load_public(options.host_cert_files[i], &key, NULL)) != 0) { error_r(r, "Could not load host certificate \"%s\"", options.host_cert_files[i]); continue; } if (!sshkey_is_cert(key)) { error("Certificate file is not a certificate: %s", options.host_cert_files[i]); sshkey_free(key); continue; } /* Find matching private key */ for (j = 0; j < options.num_host_key_files; j++) { if (sshkey_equal_public(key, sensitive_data.host_pubkeys[j])) { sensitive_data.host_certificates[j] = key; break; } } if (j >= options.num_host_key_files) { error("No matching private key for certificate: %s", options.host_cert_files[i]); sshkey_free(key); continue; } sensitive_data.host_certificates[j] = key; debug("host certificate: #%u type %d %s", j, key->type, sshkey_type(key)); } if (privsep_chroot) { struct stat st; if ((stat(_PATH_PRIVSEP_CHROOT_DIR, &st) == -1) || (S_ISDIR(st.st_mode) == 0)) fatal("Missing privilege separation directory: %s", _PATH_PRIVSEP_CHROOT_DIR); #ifdef HAVE_CYGWIN if (check_ntsec(_PATH_PRIVSEP_CHROOT_DIR) && (st.st_uid != getuid () || (st.st_mode & (S_IWGRP|S_IWOTH)) != 0)) #else if (st.st_uid != 0 || (st.st_mode & (S_IWGRP|S_IWOTH)) != 0) #endif fatal("%s must be owned by root and not group or " "world-writable.", _PATH_PRIVSEP_CHROOT_DIR); } if (test_flag > 1) print_config(ssh, connection_info); /* Configuration looks good, so exit if in test mode. */ if (test_flag) exit(0); /* * Clear out any supplemental groups we may have inherited. This * prevents inadvertent creation of files with bad modes (in the * portable version at least, it's certainly possible for PAM * to create a file, and we can't control the code in every * module which might be used). */ if (setgroups(0, NULL) < 0) debug("setgroups() failed: %.200s", strerror(errno)); if (rexec_flag) { if (rexec_argc < 0) fatal("rexec_argc %d < 0", rexec_argc); rexec_argv = xcalloc(rexec_argc + 2, sizeof(char *)); for (i = 0; i < (u_int)rexec_argc; i++) { debug("rexec_argv[%d]='%s'", i, saved_argv[i]); rexec_argv[i] = saved_argv[i]; } rexec_argv[rexec_argc] = "-R"; rexec_argv[rexec_argc + 1] = NULL; } listener_proctitle = prepare_proctitle(ac, av); /* Ensure that umask disallows at least group and world write */ new_umask = umask(0077) | 0022; (void) umask(new_umask); /* Initialize the log (it is reinitialized below in case we forked). */ if (debug_flag && (!inetd_flag || rexeced_flag)) log_stderr = 1; log_init(__progname, options.log_level, options.log_facility, log_stderr); for (i = 0; i < options.num_log_verbose; i++) log_verbose_add(options.log_verbose[i]); /* * If not in debugging mode, not started from inetd and not already * daemonized (eg re-exec via SIGHUP), disconnect from the controlling * terminal, and fork. The original process exits. */ already_daemon = daemonized(); if (!(debug_flag || inetd_flag || no_daemon_flag || already_daemon)) { if (daemon(0, 0) == -1) fatal("daemon() failed: %.200s", strerror(errno)); disconnect_controlling_tty(); } /* Reinitialize the log (because of the fork above). */ log_init(__progname, options.log_level, options.log_facility, log_stderr); /* * Chdir to the root directory so that the current disk can be * unmounted if desired. */ if (chdir("/") == -1) error("chdir(\"/\"): %s", strerror(errno)); /* ignore SIGPIPE */ ssh_signal(SIGPIPE, SIG_IGN); /* Get a connection, either from inetd or a listening TCP socket */ if (inetd_flag) { server_accept_inetd(&sock_in, &sock_out); } else { platform_pre_listen(); server_listen(); ssh_signal(SIGHUP, sighup_handler); ssh_signal(SIGCHLD, main_sigchld_handler); ssh_signal(SIGTERM, sigterm_handler); ssh_signal(SIGQUIT, sigterm_handler); /* * Write out the pid file after the sigterm handler * is setup and the listen sockets are bound */ if (options.pid_file != NULL && !debug_flag) { FILE *f = fopen(options.pid_file, "w"); if (f == NULL) { error("Couldn't create pid file \"%s\": %s", options.pid_file, strerror(errno)); } else { fprintf(f, "%ld\n", (long) getpid()); fclose(f); } } /* Accept a connection and return in a forked child */ server_accept_loop(&sock_in, &sock_out, &newsock, config_s); } /* This is the child processing a new connection. */ setproctitle("%s", "[accepted]"); /* * Create a new session and process group since the 4.4BSD * setlogin() affects the entire process group. We don't * want the child to be able to affect the parent. */ if (!debug_flag && !inetd_flag && setsid() == -1) error("setsid: %.100s", strerror(errno)); if (rexec_flag) { debug("rexec start in %d out %d newsock %d pipe %d sock %d", sock_in, sock_out, newsock, startup_pipe, config_s[0]); if (dup2(newsock, STDIN_FILENO) == -1) debug3_f("dup2 stdin: %s", strerror(errno)); if (dup2(STDIN_FILENO, STDOUT_FILENO) == -1) debug3_f("dup2 stdout: %s", strerror(errno)); if (startup_pipe == -1) close(REEXEC_STARTUP_PIPE_FD); else if (startup_pipe != REEXEC_STARTUP_PIPE_FD) { if (dup2(startup_pipe, REEXEC_STARTUP_PIPE_FD) == -1) debug3_f("dup2 startup_p: %s", strerror(errno)); close(startup_pipe); startup_pipe = REEXEC_STARTUP_PIPE_FD; } if (dup2(config_s[1], REEXEC_CONFIG_PASS_FD) == -1) debug3_f("dup2 config_s: %s", strerror(errno)); close(config_s[1]); ssh_signal(SIGHUP, SIG_IGN); /* avoid reset to SIG_DFL */ execv(rexec_argv[0], rexec_argv); /* Reexec has failed, fall back and continue */ error("rexec of %s failed: %s", rexec_argv[0], strerror(errno)); recv_rexec_state(REEXEC_CONFIG_PASS_FD, NULL); log_init(__progname, options.log_level, options.log_facility, log_stderr); /* Clean up fds */ close(REEXEC_CONFIG_PASS_FD); newsock = sock_out = sock_in = dup(STDIN_FILENO); if (stdfd_devnull(1, 1, 0) == -1) error_f("stdfd_devnull failed"); debug("rexec cleanup in %d out %d newsock %d pipe %d sock %d", sock_in, sock_out, newsock, startup_pipe, config_s[0]); } /* Executed child processes don't need these. */ fcntl(sock_out, F_SETFD, FD_CLOEXEC); fcntl(sock_in, F_SETFD, FD_CLOEXEC); /* We will not restart on SIGHUP since it no longer makes sense. */ ssh_signal(SIGALRM, SIG_DFL); ssh_signal(SIGHUP, SIG_DFL); ssh_signal(SIGTERM, SIG_DFL); ssh_signal(SIGQUIT, SIG_DFL); ssh_signal(SIGCHLD, SIG_DFL); ssh_signal(SIGINT, SIG_DFL); /* * Register our connection. This turns encryption off because we do * not have a key. */ if ((ssh = ssh_packet_set_connection(NULL, sock_in, sock_out)) == NULL) fatal("Unable to create connection"); the_active_state = ssh; ssh_packet_set_server(ssh); check_ip_options(ssh); /* Prepare the channels layer */ channel_init_channels(ssh); channel_set_af(ssh, options.address_family); process_channel_timeouts(ssh, &options); process_permitopen(ssh, &options); /* Set SO_KEEPALIVE if requested. */ if (options.tcp_keep_alive && ssh_packet_connection_is_on_socket(ssh) && setsockopt(sock_in, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)) == -1) error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno)); if ((remote_port = ssh_remote_port(ssh)) < 0) { debug("ssh_remote_port failed"); cleanup_exit(255); } if (options.routing_domain != NULL) set_process_rdomain(ssh, options.routing_domain); /* * The rest of the code depends on the fact that * ssh_remote_ipaddr() caches the remote ip, even if * the socket goes away. */ remote_ip = ssh_remote_ipaddr(ssh); #ifdef SSH_AUDIT_EVENTS audit_connection_from(remote_ip, remote_port); #endif rdomain = ssh_packet_rdomain_in(ssh); /* Log the connection. */ laddr = get_local_ipaddr(sock_in); verbose("Connection from %s port %d on %s port %d%s%s%s", remote_ip, remote_port, laddr, ssh_local_port(ssh), rdomain == NULL ? "" : " rdomain \"", rdomain == NULL ? "" : rdomain, rdomain == NULL ? "" : "\""); free(laddr); /* * We don't want to listen forever unless the other side * successfully authenticates itself. So we set up an alarm which is * cleared after successful authentication. A limit of zero * indicates no limit. Note that we don't set the alarm in debugging * mode; it is just annoying to have the server exit just when you * are about to discover the bug. */ ssh_signal(SIGALRM, grace_alarm_handler); if (!debug_flag) alarm(options.login_grace_time); if ((r = kex_exchange_identification(ssh, -1, options.version_addendum)) != 0) sshpkt_fatal(ssh, r, "banner exchange"); ssh_packet_set_nonblocking(ssh); /* allocate authentication context */ authctxt = xcalloc(1, sizeof(*authctxt)); ssh->authctxt = authctxt; authctxt->loginmsg = loginmsg; /* XXX global for cleanup, access from other modules */ the_authctxt = authctxt; /* Set default key authentication options */ if ((auth_opts = sshauthopt_new_with_keys_defaults()) == NULL) fatal("allocation failed"); /* prepare buffer to collect messages to display to user after login */ if ((loginmsg = sshbuf_new()) == NULL) fatal_f("sshbuf_new failed"); auth_debug_reset(); if (use_privsep) { if (privsep_preauth(ssh) == 1) goto authenticated; } else if (have_agent) { if ((r = ssh_get_authentication_socket(&auth_sock)) != 0) { error_r(r, "Unable to get agent socket"); have_agent = 0; } } /* perform the key exchange */ /* authenticate user and start session */ do_ssh2_kex(ssh); do_authentication2(ssh); /* * If we use privilege separation, the unprivileged child transfers * the current keystate and exits */ if (use_privsep) { mm_send_keystate(ssh, pmonitor); ssh_packet_clear_keys(ssh); exit(0); } authenticated: /* * Cancel the alarm we set to limit the time taken for * authentication. */ alarm(0); ssh_signal(SIGALRM, SIG_DFL); authctxt->authenticated = 1; if (startup_pipe != -1) { close(startup_pipe); startup_pipe = -1; } #ifdef SSH_AUDIT_EVENTS audit_event(ssh, SSH_AUTH_SUCCESS); #endif #ifdef GSSAPI if (options.gss_authentication) { temporarily_use_uid(authctxt->pw); ssh_gssapi_storecreds(); restore_uid(); } #endif #ifdef USE_PAM if (options.use_pam) { do_pam_setcred(1); do_pam_session(ssh); } #endif /* * In privilege separation, we fork another child and prepare * file descriptor passing. */ if (use_privsep) { privsep_postauth(ssh, authctxt); /* the monitor process [priv] will not return */ } ssh_packet_set_timeout(ssh, options.client_alive_interval, options.client_alive_count_max); /* Try to send all our hostkeys to the client */ notify_hostkeys(ssh); /* Start session. */ do_authenticated(ssh, authctxt); /* The connection has been terminated. */ ssh_packet_get_bytes(ssh, &ibytes, &obytes); verbose("Transferred: sent %llu, received %llu bytes", (unsigned long long)obytes, (unsigned long long)ibytes); verbose("Closing connection to %.500s port %d", remote_ip, remote_port); #ifdef USE_PAM if (options.use_pam) finish_pam(); #endif /* USE_PAM */ #ifdef SSH_AUDIT_EVENTS PRIVSEP(audit_event(ssh, SSH_CONNECTION_CLOSE)); #endif ssh_packet_close(ssh); if (use_privsep) mm_terminate(); exit(0); } int sshd_hostkey_sign(struct ssh *ssh, struct sshkey *privkey, struct sshkey *pubkey, u_char **signature, size_t *slenp, const u_char *data, size_t dlen, const char *alg) { int r; if (use_privsep) { if (privkey) { if (mm_sshkey_sign(ssh, privkey, signature, slenp, data, dlen, alg, options.sk_provider, NULL, ssh->compat) < 0) fatal_f("privkey sign failed"); } else { if (mm_sshkey_sign(ssh, pubkey, signature, slenp, data, dlen, alg, options.sk_provider, NULL, ssh->compat) < 0) fatal_f("pubkey sign failed"); } } else { if (privkey) { if (sshkey_sign(privkey, signature, slenp, data, dlen, alg, options.sk_provider, NULL, ssh->compat) < 0) fatal_f("privkey sign failed"); } else { if ((r = ssh_agent_sign(auth_sock, pubkey, signature, slenp, data, dlen, alg, ssh->compat)) != 0) { fatal_fr(r, "agent sign failed"); } } } return 0; } /* SSH2 key exchange */ static void do_ssh2_kex(struct ssh *ssh) { char *hkalgs = NULL, *myproposal[PROPOSAL_MAX]; const char *compression = NULL; struct kex *kex; int r; if (options.rekey_limit || options.rekey_interval) ssh_packet_set_rekey_limits(ssh, options.rekey_limit, options.rekey_interval); if (options.compression == COMP_NONE) compression = "none"; hkalgs = list_hostkey_types(); kex_proposal_populate_entries(ssh, myproposal, options.kex_algorithms, options.ciphers, options.macs, compression, hkalgs); free(hkalgs); /* start key exchange */ if ((r = kex_setup(ssh, myproposal)) != 0) fatal_r(r, "kex_setup"); kex = ssh->kex; #ifdef WITH_OPENSSL kex->kex[KEX_DH_GRP1_SHA1] = kex_gen_server; kex->kex[KEX_DH_GRP14_SHA1] = kex_gen_server; kex->kex[KEX_DH_GRP14_SHA256] = kex_gen_server; kex->kex[KEX_DH_GRP16_SHA512] = kex_gen_server; kex->kex[KEX_DH_GRP18_SHA512] = kex_gen_server; kex->kex[KEX_DH_GEX_SHA1] = kexgex_server; kex->kex[KEX_DH_GEX_SHA256] = kexgex_server; # ifdef OPENSSL_HAS_ECC kex->kex[KEX_ECDH_SHA2] = kex_gen_server; # endif #endif kex->kex[KEX_C25519_SHA256] = kex_gen_server; kex->kex[KEX_KEM_SNTRUP761X25519_SHA512] = kex_gen_server; kex->load_host_public_key=&get_hostkey_public_by_type; kex->load_host_private_key=&get_hostkey_private_by_type; kex->host_key_index=&get_hostkey_index; kex->sign = sshd_hostkey_sign; ssh_dispatch_run_fatal(ssh, DISPATCH_BLOCK, &kex->done); #ifdef DEBUG_KEXDH /* send 1st encrypted/maced/compressed message */ if ((r = sshpkt_start(ssh, SSH2_MSG_IGNORE)) != 0 || (r = sshpkt_put_cstring(ssh, "markus")) != 0 || (r = sshpkt_send(ssh)) != 0 || (r = ssh_packet_write_wait(ssh)) != 0) fatal_fr(r, "send test"); #endif kex_proposal_free_entries(myproposal); debug("KEX done"); } /* server specific fatal cleanup */ void cleanup_exit(int i) { if (the_active_state != NULL && the_authctxt != NULL) { do_cleanup(the_active_state, the_authctxt); if (use_privsep && privsep_is_preauth && pmonitor != NULL && pmonitor->m_pid > 1) { debug("Killing privsep child %d", pmonitor->m_pid); if (kill(pmonitor->m_pid, SIGKILL) != 0 && errno != ESRCH) { error_f("kill(%d): %s", pmonitor->m_pid, strerror(errno)); } } } #ifdef SSH_AUDIT_EVENTS /* done after do_cleanup so it can cancel the PAM auth 'thread' */ if (the_active_state != NULL && (!use_privsep || mm_is_monitor())) audit_event(the_active_state, SSH_CONNECTION_ABANDON); #endif _exit(i); } diff --git a/sshkey.c b/sshkey.c index 727728536b34..2d3906ad8475 100644 --- a/sshkey.c +++ b/sshkey.c @@ -1,3669 +1,3670 @@ -/* $OpenBSD: sshkey.c,v 1.137 2023/07/27 22:23:05 djm Exp $ */ +/* $OpenBSD: sshkey.c,v 1.138 2023/08/21 04:36:46 djm Exp $ */ /* * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. * Copyright (c) 2008 Alexander von Gernler. All rights reserved. * Copyright (c) 2010,2011 Damien Miller. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" #include #include #ifdef WITH_OPENSSL #include #include #include #endif #include "crypto_api.h" #include #include #include +#include #include #include #include #ifdef HAVE_UTIL_H #include #endif /* HAVE_UTIL_H */ #include "ssh2.h" #include "ssherr.h" #include "misc.h" #include "sshbuf.h" #include "cipher.h" #include "digest.h" #define SSHKEY_INTERNAL #include "sshkey.h" #include "match.h" #include "ssh-sk.h" #ifdef WITH_XMSS #include "sshkey-xmss.h" #include "xmss_fast.h" #endif #include "openbsd-compat/openssl-compat.h" /* openssh private key file format */ #define MARK_BEGIN "-----BEGIN OPENSSH PRIVATE KEY-----\n" #define MARK_END "-----END OPENSSH PRIVATE KEY-----\n" #define MARK_BEGIN_LEN (sizeof(MARK_BEGIN) - 1) #define MARK_END_LEN (sizeof(MARK_END) - 1) #define KDFNAME "bcrypt" #define AUTH_MAGIC "openssh-key-v1" #define SALT_LEN 16 #define DEFAULT_CIPHERNAME "aes256-ctr" #define DEFAULT_ROUNDS 24 /* Version identification string for SSH v1 identity files. */ #define LEGACY_BEGIN "SSH PRIVATE KEY FILE FORMAT 1.1\n" /* * Constants relating to "shielding" support; protection of keys expected * to remain in memory for long durations */ #define SSHKEY_SHIELD_PREKEY_LEN (16 * 1024) #define SSHKEY_SHIELD_CIPHER "aes256-ctr" /* XXX want AES-EME* */ #define SSHKEY_SHIELD_PREKEY_HASH SSH_DIGEST_SHA512 int sshkey_private_serialize_opt(struct sshkey *key, struct sshbuf *buf, enum sshkey_serialize_rep); static int sshkey_from_blob_internal(struct sshbuf *buf, struct sshkey **keyp, int allow_cert); /* Supported key types */ extern const struct sshkey_impl sshkey_ed25519_impl; extern const struct sshkey_impl sshkey_ed25519_cert_impl; extern const struct sshkey_impl sshkey_ed25519_sk_impl; extern const struct sshkey_impl sshkey_ed25519_sk_cert_impl; #ifdef WITH_OPENSSL # ifdef OPENSSL_HAS_ECC # ifdef ENABLE_SK extern const struct sshkey_impl sshkey_ecdsa_sk_impl; extern const struct sshkey_impl sshkey_ecdsa_sk_cert_impl; extern const struct sshkey_impl sshkey_ecdsa_sk_webauthn_impl; # endif /* ENABLE_SK */ extern const struct sshkey_impl sshkey_ecdsa_nistp256_impl; extern const struct sshkey_impl sshkey_ecdsa_nistp256_cert_impl; extern const struct sshkey_impl sshkey_ecdsa_nistp384_impl; extern const struct sshkey_impl sshkey_ecdsa_nistp384_cert_impl; # ifdef OPENSSL_HAS_NISTP521 extern const struct sshkey_impl sshkey_ecdsa_nistp521_impl; extern const struct sshkey_impl sshkey_ecdsa_nistp521_cert_impl; # endif /* OPENSSL_HAS_NISTP521 */ # endif /* OPENSSL_HAS_ECC */ extern const struct sshkey_impl sshkey_rsa_impl; extern const struct sshkey_impl sshkey_rsa_cert_impl; extern const struct sshkey_impl sshkey_rsa_sha256_impl; extern const struct sshkey_impl sshkey_rsa_sha256_cert_impl; extern const struct sshkey_impl sshkey_rsa_sha512_impl; extern const struct sshkey_impl sshkey_rsa_sha512_cert_impl; extern const struct sshkey_impl sshkey_dss_impl; extern const struct sshkey_impl sshkey_dsa_cert_impl; #endif /* WITH_OPENSSL */ #ifdef WITH_XMSS extern const struct sshkey_impl sshkey_xmss_impl; extern const struct sshkey_impl sshkey_xmss_cert_impl; #endif const struct sshkey_impl * const keyimpls[] = { &sshkey_ed25519_impl, &sshkey_ed25519_cert_impl, #ifdef ENABLE_SK &sshkey_ed25519_sk_impl, &sshkey_ed25519_sk_cert_impl, #endif #ifdef WITH_OPENSSL # ifdef OPENSSL_HAS_ECC &sshkey_ecdsa_nistp256_impl, &sshkey_ecdsa_nistp256_cert_impl, &sshkey_ecdsa_nistp384_impl, &sshkey_ecdsa_nistp384_cert_impl, # ifdef OPENSSL_HAS_NISTP521 &sshkey_ecdsa_nistp521_impl, &sshkey_ecdsa_nistp521_cert_impl, # endif /* OPENSSL_HAS_NISTP521 */ # ifdef ENABLE_SK &sshkey_ecdsa_sk_impl, &sshkey_ecdsa_sk_cert_impl, &sshkey_ecdsa_sk_webauthn_impl, # endif /* ENABLE_SK */ # endif /* OPENSSL_HAS_ECC */ &sshkey_dss_impl, &sshkey_dsa_cert_impl, &sshkey_rsa_impl, &sshkey_rsa_cert_impl, &sshkey_rsa_sha256_impl, &sshkey_rsa_sha256_cert_impl, &sshkey_rsa_sha512_impl, &sshkey_rsa_sha512_cert_impl, #endif /* WITH_OPENSSL */ #ifdef WITH_XMSS &sshkey_xmss_impl, &sshkey_xmss_cert_impl, #endif NULL }; static const struct sshkey_impl * sshkey_impl_from_type(int type) { int i; for (i = 0; keyimpls[i] != NULL; i++) { if (keyimpls[i]->type == type) return keyimpls[i]; } return NULL; } static const struct sshkey_impl * sshkey_impl_from_type_nid(int type, int nid) { int i; for (i = 0; keyimpls[i] != NULL; i++) { if (keyimpls[i]->type == type && (keyimpls[i]->nid == 0 || keyimpls[i]->nid == nid)) return keyimpls[i]; } return NULL; } static const struct sshkey_impl * sshkey_impl_from_key(const struct sshkey *k) { if (k == NULL) return NULL; return sshkey_impl_from_type_nid(k->type, k->ecdsa_nid); } const char * sshkey_type(const struct sshkey *k) { const struct sshkey_impl *impl; if ((impl = sshkey_impl_from_key(k)) == NULL) return "unknown"; return impl->shortname; } static const char * sshkey_ssh_name_from_type_nid(int type, int nid) { const struct sshkey_impl *impl; if ((impl = sshkey_impl_from_type_nid(type, nid)) == NULL) return "ssh-unknown"; return impl->name; } int sshkey_type_is_cert(int type) { const struct sshkey_impl *impl; if ((impl = sshkey_impl_from_type(type)) == NULL) return 0; return impl->cert; } const char * sshkey_ssh_name(const struct sshkey *k) { return sshkey_ssh_name_from_type_nid(k->type, k->ecdsa_nid); } const char * sshkey_ssh_name_plain(const struct sshkey *k) { return sshkey_ssh_name_from_type_nid(sshkey_type_plain(k->type), k->ecdsa_nid); } int sshkey_type_from_name(const char *name) { int i; const struct sshkey_impl *impl; for (i = 0; keyimpls[i] != NULL; i++) { impl = keyimpls[i]; /* Only allow shortname matches for plain key types */ if ((impl->name != NULL && strcmp(name, impl->name) == 0) || (!impl->cert && strcasecmp(impl->shortname, name) == 0)) return impl->type; } return KEY_UNSPEC; } static int key_type_is_ecdsa_variant(int type) { switch (type) { case KEY_ECDSA: case KEY_ECDSA_CERT: case KEY_ECDSA_SK: case KEY_ECDSA_SK_CERT: return 1; } return 0; } int sshkey_ecdsa_nid_from_name(const char *name) { int i; for (i = 0; keyimpls[i] != NULL; i++) { if (!key_type_is_ecdsa_variant(keyimpls[i]->type)) continue; if (keyimpls[i]->name != NULL && strcmp(name, keyimpls[i]->name) == 0) return keyimpls[i]->nid; } return -1; } int sshkey_match_keyname_to_sigalgs(const char *keyname, const char *sigalgs) { int ktype; if (sigalgs == NULL || *sigalgs == '\0' || (ktype = sshkey_type_from_name(keyname)) == KEY_UNSPEC) return 0; else if (ktype == KEY_RSA) { return match_pattern_list("ssh-rsa", sigalgs, 0) == 1 || match_pattern_list("rsa-sha2-256", sigalgs, 0) == 1 || match_pattern_list("rsa-sha2-512", sigalgs, 0) == 1; } else if (ktype == KEY_RSA_CERT) { return match_pattern_list("ssh-rsa-cert-v01@openssh.com", sigalgs, 0) == 1 || match_pattern_list("rsa-sha2-256-cert-v01@openssh.com", sigalgs, 0) == 1 || match_pattern_list("rsa-sha2-512-cert-v01@openssh.com", sigalgs, 0) == 1; } else return match_pattern_list(keyname, sigalgs, 0) == 1; } char * sshkey_alg_list(int certs_only, int plain_only, int include_sigonly, char sep) { char *tmp, *ret = NULL; size_t i, nlen, rlen = 0; const struct sshkey_impl *impl; for (i = 0; keyimpls[i] != NULL; i++) { impl = keyimpls[i]; if (impl->name == NULL) continue; if (!include_sigonly && impl->sigonly) continue; if ((certs_only && !impl->cert) || (plain_only && impl->cert)) continue; if (ret != NULL) ret[rlen++] = sep; nlen = strlen(impl->name); if ((tmp = realloc(ret, rlen + nlen + 2)) == NULL) { free(ret); return NULL; } ret = tmp; memcpy(ret + rlen, impl->name, nlen + 1); rlen += nlen; } return ret; } int sshkey_names_valid2(const char *names, int allow_wildcard, int plain_only) { char *s, *cp, *p; const struct sshkey_impl *impl; int i, type; if (names == NULL || strcmp(names, "") == 0) return 0; if ((s = cp = strdup(names)) == NULL) return 0; for ((p = strsep(&cp, ",")); p && *p != '\0'; (p = strsep(&cp, ","))) { type = sshkey_type_from_name(p); if (type == KEY_UNSPEC) { if (allow_wildcard) { /* * Try matching key types against the string. * If any has a positive or negative match then * the component is accepted. */ impl = NULL; for (i = 0; keyimpls[i] != NULL; i++) { if (match_pattern_list( keyimpls[i]->name, p, 0) != 0) { impl = keyimpls[i]; break; } } if (impl != NULL) continue; } free(s); return 0; } else if (plain_only && sshkey_type_is_cert(type)) { free(s); return 0; } } free(s); return 1; } u_int sshkey_size(const struct sshkey *k) { const struct sshkey_impl *impl; if ((impl = sshkey_impl_from_key(k)) == NULL) return 0; if (impl->funcs->size != NULL) return impl->funcs->size(k); return impl->keybits; } static int sshkey_type_is_valid_ca(int type) { const struct sshkey_impl *impl; if ((impl = sshkey_impl_from_type(type)) == NULL) return 0; /* All non-certificate types may act as CAs */ return !impl->cert; } int sshkey_is_cert(const struct sshkey *k) { if (k == NULL) return 0; return sshkey_type_is_cert(k->type); } int sshkey_is_sk(const struct sshkey *k) { if (k == NULL) return 0; switch (sshkey_type_plain(k->type)) { case KEY_ECDSA_SK: case KEY_ED25519_SK: return 1; default: return 0; } } /* Return the cert-less equivalent to a certified key type */ int sshkey_type_plain(int type) { switch (type) { case KEY_RSA_CERT: return KEY_RSA; case KEY_DSA_CERT: return KEY_DSA; case KEY_ECDSA_CERT: return KEY_ECDSA; case KEY_ECDSA_SK_CERT: return KEY_ECDSA_SK; case KEY_ED25519_CERT: return KEY_ED25519; case KEY_ED25519_SK_CERT: return KEY_ED25519_SK; case KEY_XMSS_CERT: return KEY_XMSS; default: return type; } } /* Return the cert equivalent to a plain key type */ static int sshkey_type_certified(int type) { switch (type) { case KEY_RSA: return KEY_RSA_CERT; case KEY_DSA: return KEY_DSA_CERT; case KEY_ECDSA: return KEY_ECDSA_CERT; case KEY_ECDSA_SK: return KEY_ECDSA_SK_CERT; case KEY_ED25519: return KEY_ED25519_CERT; case KEY_ED25519_SK: return KEY_ED25519_SK_CERT; case KEY_XMSS: return KEY_XMSS_CERT; default: return -1; } } #ifdef WITH_OPENSSL /* XXX: these are really begging for a table-driven approach */ int sshkey_curve_name_to_nid(const char *name) { if (strcmp(name, "nistp256") == 0) return NID_X9_62_prime256v1; else if (strcmp(name, "nistp384") == 0) return NID_secp384r1; # ifdef OPENSSL_HAS_NISTP521 else if (strcmp(name, "nistp521") == 0) return NID_secp521r1; # endif /* OPENSSL_HAS_NISTP521 */ else return -1; } u_int sshkey_curve_nid_to_bits(int nid) { switch (nid) { case NID_X9_62_prime256v1: return 256; case NID_secp384r1: return 384; # ifdef OPENSSL_HAS_NISTP521 case NID_secp521r1: return 521; # endif /* OPENSSL_HAS_NISTP521 */ default: return 0; } } int sshkey_ecdsa_bits_to_nid(int bits) { switch (bits) { case 256: return NID_X9_62_prime256v1; case 384: return NID_secp384r1; # ifdef OPENSSL_HAS_NISTP521 case 521: return NID_secp521r1; # endif /* OPENSSL_HAS_NISTP521 */ default: return -1; } } const char * sshkey_curve_nid_to_name(int nid) { switch (nid) { case NID_X9_62_prime256v1: return "nistp256"; case NID_secp384r1: return "nistp384"; # ifdef OPENSSL_HAS_NISTP521 case NID_secp521r1: return "nistp521"; # endif /* OPENSSL_HAS_NISTP521 */ default: return NULL; } } int sshkey_ec_nid_to_hash_alg(int nid) { int kbits = sshkey_curve_nid_to_bits(nid); if (kbits <= 0) return -1; /* RFC5656 section 6.2.1 */ if (kbits <= 256) return SSH_DIGEST_SHA256; else if (kbits <= 384) return SSH_DIGEST_SHA384; else return SSH_DIGEST_SHA512; } #endif /* WITH_OPENSSL */ static void cert_free(struct sshkey_cert *cert) { u_int i; if (cert == NULL) return; sshbuf_free(cert->certblob); sshbuf_free(cert->critical); sshbuf_free(cert->extensions); free(cert->key_id); for (i = 0; i < cert->nprincipals; i++) free(cert->principals[i]); free(cert->principals); sshkey_free(cert->signature_key); free(cert->signature_type); freezero(cert, sizeof(*cert)); } static struct sshkey_cert * cert_new(void) { struct sshkey_cert *cert; if ((cert = calloc(1, sizeof(*cert))) == NULL) return NULL; if ((cert->certblob = sshbuf_new()) == NULL || (cert->critical = sshbuf_new()) == NULL || (cert->extensions = sshbuf_new()) == NULL) { cert_free(cert); return NULL; } cert->key_id = NULL; cert->principals = NULL; cert->signature_key = NULL; cert->signature_type = NULL; return cert; } struct sshkey * sshkey_new(int type) { struct sshkey *k; const struct sshkey_impl *impl = NULL; if (type != KEY_UNSPEC && (impl = sshkey_impl_from_type(type)) == NULL) return NULL; /* All non-certificate types may act as CAs */ if ((k = calloc(1, sizeof(*k))) == NULL) return NULL; k->type = type; k->ecdsa_nid = -1; if (impl != NULL && impl->funcs->alloc != NULL) { if (impl->funcs->alloc(k) != 0) { free(k); return NULL; } } if (sshkey_is_cert(k)) { if ((k->cert = cert_new()) == NULL) { sshkey_free(k); return NULL; } } return k; } /* Frees common FIDO fields */ void sshkey_sk_cleanup(struct sshkey *k) { free(k->sk_application); sshbuf_free(k->sk_key_handle); sshbuf_free(k->sk_reserved); k->sk_application = NULL; k->sk_key_handle = k->sk_reserved = NULL; } static void sshkey_free_contents(struct sshkey *k) { const struct sshkey_impl *impl; if (k == NULL) return; if ((impl = sshkey_impl_from_type(k->type)) != NULL && impl->funcs->cleanup != NULL) impl->funcs->cleanup(k); if (sshkey_is_cert(k)) cert_free(k->cert); freezero(k->shielded_private, k->shielded_len); freezero(k->shield_prekey, k->shield_prekey_len); } void sshkey_free(struct sshkey *k) { sshkey_free_contents(k); freezero(k, sizeof(*k)); } static int cert_compare(struct sshkey_cert *a, struct sshkey_cert *b) { if (a == NULL && b == NULL) return 1; if (a == NULL || b == NULL) return 0; if (sshbuf_len(a->certblob) != sshbuf_len(b->certblob)) return 0; if (timingsafe_bcmp(sshbuf_ptr(a->certblob), sshbuf_ptr(b->certblob), sshbuf_len(a->certblob)) != 0) return 0; return 1; } /* Compares FIDO-specific pubkey fields only */ int sshkey_sk_fields_equal(const struct sshkey *a, const struct sshkey *b) { if (a->sk_application == NULL || b->sk_application == NULL) return 0; if (strcmp(a->sk_application, b->sk_application) != 0) return 0; return 1; } /* * Compare public portions of key only, allowing comparisons between * certificates and plain keys too. */ int sshkey_equal_public(const struct sshkey *a, const struct sshkey *b) { const struct sshkey_impl *impl; if (a == NULL || b == NULL || sshkey_type_plain(a->type) != sshkey_type_plain(b->type)) return 0; if ((impl = sshkey_impl_from_type(a->type)) == NULL) return 0; return impl->funcs->equal(a, b); } int sshkey_equal(const struct sshkey *a, const struct sshkey *b) { if (a == NULL || b == NULL || a->type != b->type) return 0; if (sshkey_is_cert(a)) { if (!cert_compare(a->cert, b->cert)) return 0; } return sshkey_equal_public(a, b); } /* Serialise common FIDO key parts */ int sshkey_serialize_sk(const struct sshkey *key, struct sshbuf *b) { int r; if ((r = sshbuf_put_cstring(b, key->sk_application)) != 0) return r; return 0; } static int to_blob_buf(const struct sshkey *key, struct sshbuf *b, int force_plain, enum sshkey_serialize_rep opts) { int type, ret = SSH_ERR_INTERNAL_ERROR; const char *typename; const struct sshkey_impl *impl; if (key == NULL) return SSH_ERR_INVALID_ARGUMENT; type = force_plain ? sshkey_type_plain(key->type) : key->type; if (sshkey_type_is_cert(type)) { if (key->cert == NULL) return SSH_ERR_EXPECTED_CERT; if (sshbuf_len(key->cert->certblob) == 0) return SSH_ERR_KEY_LACKS_CERTBLOB; /* Use the existing blob */ if ((ret = sshbuf_putb(b, key->cert->certblob)) != 0) return ret; return 0; } if ((impl = sshkey_impl_from_type(type)) == NULL) return SSH_ERR_KEY_TYPE_UNKNOWN; typename = sshkey_ssh_name_from_type_nid(type, key->ecdsa_nid); if ((ret = sshbuf_put_cstring(b, typename)) != 0) return ret; return impl->funcs->serialize_public(key, b, opts); } int sshkey_putb(const struct sshkey *key, struct sshbuf *b) { return to_blob_buf(key, b, 0, SSHKEY_SERIALIZE_DEFAULT); } int sshkey_puts_opts(const struct sshkey *key, struct sshbuf *b, enum sshkey_serialize_rep opts) { struct sshbuf *tmp; int r; if ((tmp = sshbuf_new()) == NULL) return SSH_ERR_ALLOC_FAIL; r = to_blob_buf(key, tmp, 0, opts); if (r == 0) r = sshbuf_put_stringb(b, tmp); sshbuf_free(tmp); return r; } int sshkey_puts(const struct sshkey *key, struct sshbuf *b) { return sshkey_puts_opts(key, b, SSHKEY_SERIALIZE_DEFAULT); } int sshkey_putb_plain(const struct sshkey *key, struct sshbuf *b) { return to_blob_buf(key, b, 1, SSHKEY_SERIALIZE_DEFAULT); } static int to_blob(const struct sshkey *key, u_char **blobp, size_t *lenp, int force_plain, enum sshkey_serialize_rep opts) { int ret = SSH_ERR_INTERNAL_ERROR; size_t len; struct sshbuf *b = NULL; if (lenp != NULL) *lenp = 0; if (blobp != NULL) *blobp = NULL; if ((b = sshbuf_new()) == NULL) return SSH_ERR_ALLOC_FAIL; if ((ret = to_blob_buf(key, b, force_plain, opts)) != 0) goto out; len = sshbuf_len(b); if (lenp != NULL) *lenp = len; if (blobp != NULL) { if ((*blobp = malloc(len)) == NULL) { ret = SSH_ERR_ALLOC_FAIL; goto out; } memcpy(*blobp, sshbuf_ptr(b), len); } ret = 0; out: sshbuf_free(b); return ret; } int sshkey_to_blob(const struct sshkey *key, u_char **blobp, size_t *lenp) { return to_blob(key, blobp, lenp, 0, SSHKEY_SERIALIZE_DEFAULT); } int sshkey_plain_to_blob(const struct sshkey *key, u_char **blobp, size_t *lenp) { return to_blob(key, blobp, lenp, 1, SSHKEY_SERIALIZE_DEFAULT); } int sshkey_fingerprint_raw(const struct sshkey *k, int dgst_alg, u_char **retp, size_t *lenp) { u_char *blob = NULL, *ret = NULL; size_t blob_len = 0; int r = SSH_ERR_INTERNAL_ERROR; if (retp != NULL) *retp = NULL; if (lenp != NULL) *lenp = 0; if (ssh_digest_bytes(dgst_alg) == 0) { r = SSH_ERR_INVALID_ARGUMENT; goto out; } if ((r = to_blob(k, &blob, &blob_len, 1, SSHKEY_SERIALIZE_DEFAULT)) != 0) goto out; if ((ret = calloc(1, SSH_DIGEST_MAX_LENGTH)) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } if ((r = ssh_digest_memory(dgst_alg, blob, blob_len, ret, SSH_DIGEST_MAX_LENGTH)) != 0) goto out; /* success */ if (retp != NULL) { *retp = ret; ret = NULL; } if (lenp != NULL) *lenp = ssh_digest_bytes(dgst_alg); r = 0; out: free(ret); if (blob != NULL) freezero(blob, blob_len); return r; } static char * fingerprint_b64(const char *alg, u_char *dgst_raw, size_t dgst_raw_len) { char *ret; size_t plen = strlen(alg) + 1; size_t rlen = ((dgst_raw_len + 2) / 3) * 4 + plen + 1; if (dgst_raw_len > 65536 || (ret = calloc(1, rlen)) == NULL) return NULL; strlcpy(ret, alg, rlen); strlcat(ret, ":", rlen); if (dgst_raw_len == 0) return ret; if (b64_ntop(dgst_raw, dgst_raw_len, ret + plen, rlen - plen) == -1) { freezero(ret, rlen); return NULL; } /* Trim padding characters from end */ ret[strcspn(ret, "=")] = '\0'; return ret; } static char * fingerprint_hex(const char *alg, u_char *dgst_raw, size_t dgst_raw_len) { char *retval, hex[5]; size_t i, rlen = dgst_raw_len * 3 + strlen(alg) + 2; if (dgst_raw_len > 65536 || (retval = calloc(1, rlen)) == NULL) return NULL; strlcpy(retval, alg, rlen); strlcat(retval, ":", rlen); for (i = 0; i < dgst_raw_len; i++) { snprintf(hex, sizeof(hex), "%s%02x", i > 0 ? ":" : "", dgst_raw[i]); strlcat(retval, hex, rlen); } return retval; } static char * fingerprint_bubblebabble(u_char *dgst_raw, size_t dgst_raw_len) { char vowels[] = { 'a', 'e', 'i', 'o', 'u', 'y' }; char consonants[] = { 'b', 'c', 'd', 'f', 'g', 'h', 'k', 'l', 'm', 'n', 'p', 'r', 's', 't', 'v', 'z', 'x' }; u_int i, j = 0, rounds, seed = 1; char *retval; rounds = (dgst_raw_len / 2) + 1; if ((retval = calloc(rounds, 6)) == NULL) return NULL; retval[j++] = 'x'; for (i = 0; i < rounds; i++) { u_int idx0, idx1, idx2, idx3, idx4; if ((i + 1 < rounds) || (dgst_raw_len % 2 != 0)) { idx0 = (((((u_int)(dgst_raw[2 * i])) >> 6) & 3) + seed) % 6; idx1 = (((u_int)(dgst_raw[2 * i])) >> 2) & 15; idx2 = ((((u_int)(dgst_raw[2 * i])) & 3) + (seed / 6)) % 6; retval[j++] = vowels[idx0]; retval[j++] = consonants[idx1]; retval[j++] = vowels[idx2]; if ((i + 1) < rounds) { idx3 = (((u_int)(dgst_raw[(2 * i) + 1])) >> 4) & 15; idx4 = (((u_int)(dgst_raw[(2 * i) + 1]))) & 15; retval[j++] = consonants[idx3]; retval[j++] = '-'; retval[j++] = consonants[idx4]; seed = ((seed * 5) + ((((u_int)(dgst_raw[2 * i])) * 7) + ((u_int)(dgst_raw[(2 * i) + 1])))) % 36; } } else { idx0 = seed % 6; idx1 = 16; idx2 = seed / 6; retval[j++] = vowels[idx0]; retval[j++] = consonants[idx1]; retval[j++] = vowels[idx2]; } } retval[j++] = 'x'; retval[j++] = '\0'; return retval; } /* * Draw an ASCII-Art representing the fingerprint so human brain can * profit from its built-in pattern recognition ability. * This technique is called "random art" and can be found in some * scientific publications like this original paper: * * "Hash Visualization: a New Technique to improve Real-World Security", * Perrig A. and Song D., 1999, International Workshop on Cryptographic * Techniques and E-Commerce (CrypTEC '99) * sparrow.ece.cmu.edu/~adrian/projects/validation/validation.pdf * * The subject came up in a talk by Dan Kaminsky, too. * * If you see the picture is different, the key is different. * If the picture looks the same, you still know nothing. * * The algorithm used here is a worm crawling over a discrete plane, * leaving a trace (augmenting the field) everywhere it goes. * Movement is taken from dgst_raw 2bit-wise. Bumping into walls * makes the respective movement vector be ignored for this turn. * Graphs are not unambiguous, because circles in graphs can be * walked in either direction. */ /* * Field sizes for the random art. Have to be odd, so the starting point * can be in the exact middle of the picture, and FLDBASE should be >=8 . * Else pictures would be too dense, and drawing the frame would * fail, too, because the key type would not fit in anymore. */ #define FLDBASE 8 #define FLDSIZE_Y (FLDBASE + 1) #define FLDSIZE_X (FLDBASE * 2 + 1) static char * fingerprint_randomart(const char *alg, u_char *dgst_raw, size_t dgst_raw_len, const struct sshkey *k) { /* * Chars to be used after each other every time the worm * intersects with itself. Matter of taste. */ char *augmentation_string = " .o+=*BOX@%&#/^SE"; char *retval, *p, title[FLDSIZE_X], hash[FLDSIZE_X]; u_char field[FLDSIZE_X][FLDSIZE_Y]; size_t i, tlen, hlen; u_int b; int x, y, r; size_t len = strlen(augmentation_string) - 1; if ((retval = calloc((FLDSIZE_X + 3), (FLDSIZE_Y + 2))) == NULL) return NULL; /* initialize field */ memset(field, 0, FLDSIZE_X * FLDSIZE_Y * sizeof(char)); x = FLDSIZE_X / 2; y = FLDSIZE_Y / 2; /* process raw key */ for (i = 0; i < dgst_raw_len; i++) { int input; /* each byte conveys four 2-bit move commands */ input = dgst_raw[i]; for (b = 0; b < 4; b++) { /* evaluate 2 bit, rest is shifted later */ x += (input & 0x1) ? 1 : -1; y += (input & 0x2) ? 1 : -1; /* assure we are still in bounds */ x = MAXIMUM(x, 0); y = MAXIMUM(y, 0); x = MINIMUM(x, FLDSIZE_X - 1); y = MINIMUM(y, FLDSIZE_Y - 1); /* augment the field */ if (field[x][y] < len - 2) field[x][y]++; input = input >> 2; } } /* mark starting point and end point*/ field[FLDSIZE_X / 2][FLDSIZE_Y / 2] = len - 1; field[x][y] = len; /* assemble title */ r = snprintf(title, sizeof(title), "[%s %u]", sshkey_type(k), sshkey_size(k)); /* If [type size] won't fit, then try [type]; fits "[ED25519-CERT]" */ if (r < 0 || r > (int)sizeof(title)) r = snprintf(title, sizeof(title), "[%s]", sshkey_type(k)); tlen = (r <= 0) ? 0 : strlen(title); /* assemble hash ID. */ r = snprintf(hash, sizeof(hash), "[%s]", alg); hlen = (r <= 0) ? 0 : strlen(hash); /* output upper border */ p = retval; *p++ = '+'; for (i = 0; i < (FLDSIZE_X - tlen) / 2; i++) *p++ = '-'; memcpy(p, title, tlen); p += tlen; for (i += tlen; i < FLDSIZE_X; i++) *p++ = '-'; *p++ = '+'; *p++ = '\n'; /* output content */ for (y = 0; y < FLDSIZE_Y; y++) { *p++ = '|'; for (x = 0; x < FLDSIZE_X; x++) *p++ = augmentation_string[MINIMUM(field[x][y], len)]; *p++ = '|'; *p++ = '\n'; } /* output lower border */ *p++ = '+'; for (i = 0; i < (FLDSIZE_X - hlen) / 2; i++) *p++ = '-'; memcpy(p, hash, hlen); p += hlen; for (i += hlen; i < FLDSIZE_X; i++) *p++ = '-'; *p++ = '+'; return retval; } char * sshkey_fingerprint(const struct sshkey *k, int dgst_alg, enum sshkey_fp_rep dgst_rep) { char *retval = NULL; u_char *dgst_raw; size_t dgst_raw_len; if (sshkey_fingerprint_raw(k, dgst_alg, &dgst_raw, &dgst_raw_len) != 0) return NULL; switch (dgst_rep) { case SSH_FP_DEFAULT: if (dgst_alg == SSH_DIGEST_MD5) { retval = fingerprint_hex(ssh_digest_alg_name(dgst_alg), dgst_raw, dgst_raw_len); } else { retval = fingerprint_b64(ssh_digest_alg_name(dgst_alg), dgst_raw, dgst_raw_len); } break; case SSH_FP_HEX: retval = fingerprint_hex(ssh_digest_alg_name(dgst_alg), dgst_raw, dgst_raw_len); break; case SSH_FP_BASE64: retval = fingerprint_b64(ssh_digest_alg_name(dgst_alg), dgst_raw, dgst_raw_len); break; case SSH_FP_BUBBLEBABBLE: retval = fingerprint_bubblebabble(dgst_raw, dgst_raw_len); break; case SSH_FP_RANDOMART: retval = fingerprint_randomart(ssh_digest_alg_name(dgst_alg), dgst_raw, dgst_raw_len, k); break; default: freezero(dgst_raw, dgst_raw_len); return NULL; } freezero(dgst_raw, dgst_raw_len); return retval; } static int peek_type_nid(const char *s, size_t l, int *nid) { const struct sshkey_impl *impl; int i; for (i = 0; keyimpls[i] != NULL; i++) { impl = keyimpls[i]; if (impl->name == NULL || strlen(impl->name) != l) continue; if (memcmp(s, impl->name, l) == 0) { *nid = -1; if (key_type_is_ecdsa_variant(impl->type)) *nid = impl->nid; return impl->type; } } return KEY_UNSPEC; } /* XXX this can now be made const char * */ int sshkey_read(struct sshkey *ret, char **cpp) { struct sshkey *k; char *cp, *blobcopy; size_t space; int r, type, curve_nid = -1; struct sshbuf *blob; if (ret == NULL) return SSH_ERR_INVALID_ARGUMENT; if (ret->type != KEY_UNSPEC && sshkey_impl_from_type(ret->type) == NULL) return SSH_ERR_INVALID_ARGUMENT; /* Decode type */ cp = *cpp; space = strcspn(cp, " \t"); if (space == strlen(cp)) return SSH_ERR_INVALID_FORMAT; if ((type = peek_type_nid(cp, space, &curve_nid)) == KEY_UNSPEC) return SSH_ERR_INVALID_FORMAT; /* skip whitespace */ for (cp += space; *cp == ' ' || *cp == '\t'; cp++) ; if (*cp == '\0') return SSH_ERR_INVALID_FORMAT; if (ret->type != KEY_UNSPEC && ret->type != type) return SSH_ERR_KEY_TYPE_MISMATCH; if ((blob = sshbuf_new()) == NULL) return SSH_ERR_ALLOC_FAIL; /* find end of keyblob and decode */ space = strcspn(cp, " \t"); if ((blobcopy = strndup(cp, space)) == NULL) { sshbuf_free(blob); return SSH_ERR_ALLOC_FAIL; } if ((r = sshbuf_b64tod(blob, blobcopy)) != 0) { free(blobcopy); sshbuf_free(blob); return r; } free(blobcopy); if ((r = sshkey_fromb(blob, &k)) != 0) { sshbuf_free(blob); return r; } sshbuf_free(blob); /* skip whitespace and leave cp at start of comment */ for (cp += space; *cp == ' ' || *cp == '\t'; cp++) ; /* ensure type of blob matches type at start of line */ if (k->type != type) { sshkey_free(k); return SSH_ERR_KEY_TYPE_MISMATCH; } if (key_type_is_ecdsa_variant(type) && curve_nid != k->ecdsa_nid) { sshkey_free(k); return SSH_ERR_EC_CURVE_MISMATCH; } /* Fill in ret from parsed key */ sshkey_free_contents(ret); *ret = *k; freezero(k, sizeof(*k)); /* success */ *cpp = cp; return 0; } int sshkey_to_base64(const struct sshkey *key, char **b64p) { int r = SSH_ERR_INTERNAL_ERROR; struct sshbuf *b = NULL; char *uu = NULL; if (b64p != NULL) *b64p = NULL; if ((b = sshbuf_new()) == NULL) return SSH_ERR_ALLOC_FAIL; if ((r = sshkey_putb(key, b)) != 0) goto out; if ((uu = sshbuf_dtob64_string(b, 0)) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } /* Success */ if (b64p != NULL) { *b64p = uu; uu = NULL; } r = 0; out: sshbuf_free(b); free(uu); return r; } int sshkey_format_text(const struct sshkey *key, struct sshbuf *b) { int r = SSH_ERR_INTERNAL_ERROR; char *uu = NULL; if ((r = sshkey_to_base64(key, &uu)) != 0) goto out; if ((r = sshbuf_putf(b, "%s %s", sshkey_ssh_name(key), uu)) != 0) goto out; r = 0; out: free(uu); return r; } int sshkey_write(const struct sshkey *key, FILE *f) { struct sshbuf *b = NULL; int r = SSH_ERR_INTERNAL_ERROR; if ((b = sshbuf_new()) == NULL) return SSH_ERR_ALLOC_FAIL; if ((r = sshkey_format_text(key, b)) != 0) goto out; if (fwrite(sshbuf_ptr(b), sshbuf_len(b), 1, f) != 1) { if (feof(f)) errno = EPIPE; r = SSH_ERR_SYSTEM_ERROR; goto out; } /* Success */ r = 0; out: sshbuf_free(b); return r; } const char * sshkey_cert_type(const struct sshkey *k) { switch (k->cert->type) { case SSH2_CERT_TYPE_USER: return "user"; case SSH2_CERT_TYPE_HOST: return "host"; default: return "unknown"; } } int sshkey_check_rsa_length(const struct sshkey *k, int min_size) { #ifdef WITH_OPENSSL const BIGNUM *rsa_n; int nbits; if (k == NULL || k->rsa == NULL || (k->type != KEY_RSA && k->type != KEY_RSA_CERT)) return 0; RSA_get0_key(k->rsa, &rsa_n, NULL, NULL); nbits = BN_num_bits(rsa_n); if (nbits < SSH_RSA_MINIMUM_MODULUS_SIZE || (min_size > 0 && nbits < min_size)) return SSH_ERR_KEY_LENGTH; #endif /* WITH_OPENSSL */ return 0; } #ifdef WITH_OPENSSL # ifdef OPENSSL_HAS_ECC int sshkey_ecdsa_key_to_nid(EC_KEY *k) { EC_GROUP *eg; int nids[] = { NID_X9_62_prime256v1, NID_secp384r1, # ifdef OPENSSL_HAS_NISTP521 NID_secp521r1, # endif /* OPENSSL_HAS_NISTP521 */ -1 }; int nid; u_int i; const EC_GROUP *g = EC_KEY_get0_group(k); /* * The group may be stored in a ASN.1 encoded private key in one of two * ways: as a "named group", which is reconstituted by ASN.1 object ID * or explicit group parameters encoded into the key blob. Only the * "named group" case sets the group NID for us, but we can figure * it out for the other case by comparing against all the groups that * are supported. */ if ((nid = EC_GROUP_get_curve_name(g)) > 0) return nid; for (i = 0; nids[i] != -1; i++) { if ((eg = EC_GROUP_new_by_curve_name(nids[i])) == NULL) return -1; if (EC_GROUP_cmp(g, eg, NULL) == 0) break; EC_GROUP_free(eg); } if (nids[i] != -1) { /* Use the group with the NID attached */ EC_GROUP_set_asn1_flag(eg, OPENSSL_EC_NAMED_CURVE); if (EC_KEY_set_group(k, eg) != 1) { EC_GROUP_free(eg); return -1; } } return nids[i]; } # endif /* OPENSSL_HAS_ECC */ #endif /* WITH_OPENSSL */ int sshkey_generate(int type, u_int bits, struct sshkey **keyp) { struct sshkey *k; int ret = SSH_ERR_INTERNAL_ERROR; const struct sshkey_impl *impl; if (keyp == NULL || sshkey_type_is_cert(type)) return SSH_ERR_INVALID_ARGUMENT; *keyp = NULL; if ((impl = sshkey_impl_from_type(type)) == NULL) return SSH_ERR_KEY_TYPE_UNKNOWN; if (impl->funcs->generate == NULL) return SSH_ERR_FEATURE_UNSUPPORTED; if ((k = sshkey_new(KEY_UNSPEC)) == NULL) return SSH_ERR_ALLOC_FAIL; k->type = type; if ((ret = impl->funcs->generate(k, bits)) != 0) { sshkey_free(k); return ret; } /* success */ *keyp = k; return 0; } int sshkey_cert_copy(const struct sshkey *from_key, struct sshkey *to_key) { u_int i; const struct sshkey_cert *from; struct sshkey_cert *to; int r = SSH_ERR_INTERNAL_ERROR; if (to_key == NULL || (from = from_key->cert) == NULL) return SSH_ERR_INVALID_ARGUMENT; if ((to = cert_new()) == NULL) return SSH_ERR_ALLOC_FAIL; if ((r = sshbuf_putb(to->certblob, from->certblob)) != 0 || (r = sshbuf_putb(to->critical, from->critical)) != 0 || (r = sshbuf_putb(to->extensions, from->extensions)) != 0) goto out; to->serial = from->serial; to->type = from->type; if (from->key_id == NULL) to->key_id = NULL; else if ((to->key_id = strdup(from->key_id)) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } to->valid_after = from->valid_after; to->valid_before = from->valid_before; if (from->signature_key == NULL) to->signature_key = NULL; else if ((r = sshkey_from_private(from->signature_key, &to->signature_key)) != 0) goto out; if (from->signature_type != NULL && (to->signature_type = strdup(from->signature_type)) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } if (from->nprincipals > SSHKEY_CERT_MAX_PRINCIPALS) { r = SSH_ERR_INVALID_ARGUMENT; goto out; } if (from->nprincipals > 0) { if ((to->principals = calloc(from->nprincipals, sizeof(*to->principals))) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } for (i = 0; i < from->nprincipals; i++) { to->principals[i] = strdup(from->principals[i]); if (to->principals[i] == NULL) { to->nprincipals = i; r = SSH_ERR_ALLOC_FAIL; goto out; } } } to->nprincipals = from->nprincipals; /* success */ cert_free(to_key->cert); to_key->cert = to; to = NULL; r = 0; out: cert_free(to); return r; } int sshkey_copy_public_sk(const struct sshkey *from, struct sshkey *to) { /* Append security-key application string */ if ((to->sk_application = strdup(from->sk_application)) == NULL) return SSH_ERR_ALLOC_FAIL; return 0; } int sshkey_from_private(const struct sshkey *k, struct sshkey **pkp) { struct sshkey *n = NULL; int r = SSH_ERR_INTERNAL_ERROR; const struct sshkey_impl *impl; *pkp = NULL; if ((impl = sshkey_impl_from_key(k)) == NULL) return SSH_ERR_KEY_TYPE_UNKNOWN; if ((n = sshkey_new(k->type)) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } if ((r = impl->funcs->copy_public(k, n)) != 0) goto out; if (sshkey_is_cert(k) && (r = sshkey_cert_copy(k, n)) != 0) goto out; /* success */ *pkp = n; n = NULL; r = 0; out: sshkey_free(n); return r; } int sshkey_is_shielded(struct sshkey *k) { return k != NULL && k->shielded_private != NULL; } int sshkey_shield_private(struct sshkey *k) { struct sshbuf *prvbuf = NULL; u_char *prekey = NULL, *enc = NULL, keyiv[SSH_DIGEST_MAX_LENGTH]; struct sshcipher_ctx *cctx = NULL; const struct sshcipher *cipher; size_t i, enclen = 0; struct sshkey *kswap = NULL, tmp; int r = SSH_ERR_INTERNAL_ERROR; #ifdef DEBUG_PK fprintf(stderr, "%s: entering for %s\n", __func__, sshkey_ssh_name(k)); #endif if ((cipher = cipher_by_name(SSHKEY_SHIELD_CIPHER)) == NULL) { r = SSH_ERR_INVALID_ARGUMENT; goto out; } if (cipher_keylen(cipher) + cipher_ivlen(cipher) > ssh_digest_bytes(SSHKEY_SHIELD_PREKEY_HASH)) { r = SSH_ERR_INTERNAL_ERROR; goto out; } /* Prepare a random pre-key, and from it an ephemeral key */ if ((prekey = malloc(SSHKEY_SHIELD_PREKEY_LEN)) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } arc4random_buf(prekey, SSHKEY_SHIELD_PREKEY_LEN); if ((r = ssh_digest_memory(SSHKEY_SHIELD_PREKEY_HASH, prekey, SSHKEY_SHIELD_PREKEY_LEN, keyiv, SSH_DIGEST_MAX_LENGTH)) != 0) goto out; #ifdef DEBUG_PK fprintf(stderr, "%s: key+iv\n", __func__); sshbuf_dump_data(keyiv, ssh_digest_bytes(SSHKEY_SHIELD_PREKEY_HASH), stderr); #endif if ((r = cipher_init(&cctx, cipher, keyiv, cipher_keylen(cipher), keyiv + cipher_keylen(cipher), cipher_ivlen(cipher), 1)) != 0) goto out; /* Serialise and encrypt the private key using the ephemeral key */ if ((prvbuf = sshbuf_new()) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } if (sshkey_is_shielded(k) && (r = sshkey_unshield_private(k)) != 0) goto out; if ((r = sshkey_private_serialize_opt(k, prvbuf, SSHKEY_SERIALIZE_SHIELD)) != 0) goto out; /* pad to cipher blocksize */ i = 0; while (sshbuf_len(prvbuf) % cipher_blocksize(cipher)) { if ((r = sshbuf_put_u8(prvbuf, ++i & 0xff)) != 0) goto out; } #ifdef DEBUG_PK fprintf(stderr, "%s: serialised\n", __func__); sshbuf_dump(prvbuf, stderr); #endif /* encrypt */ enclen = sshbuf_len(prvbuf); if ((enc = malloc(enclen)) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } if ((r = cipher_crypt(cctx, 0, enc, sshbuf_ptr(prvbuf), sshbuf_len(prvbuf), 0, 0)) != 0) goto out; #ifdef DEBUG_PK fprintf(stderr, "%s: encrypted\n", __func__); sshbuf_dump_data(enc, enclen, stderr); #endif /* Make a scrubbed, public-only copy of our private key argument */ if ((r = sshkey_from_private(k, &kswap)) != 0) goto out; /* Swap the private key out (it will be destroyed below) */ tmp = *kswap; *kswap = *k; *k = tmp; /* Insert the shielded key into our argument */ k->shielded_private = enc; k->shielded_len = enclen; k->shield_prekey = prekey; k->shield_prekey_len = SSHKEY_SHIELD_PREKEY_LEN; enc = prekey = NULL; /* transferred */ enclen = 0; /* preserve key fields that are required for correct operation */ k->sk_flags = kswap->sk_flags; /* success */ r = 0; out: /* XXX behaviour on error - invalidate original private key? */ cipher_free(cctx); explicit_bzero(keyiv, sizeof(keyiv)); explicit_bzero(&tmp, sizeof(tmp)); freezero(enc, enclen); freezero(prekey, SSHKEY_SHIELD_PREKEY_LEN); sshkey_free(kswap); sshbuf_free(prvbuf); return r; } /* Check deterministic padding after private key */ static int private2_check_padding(struct sshbuf *decrypted) { u_char pad; size_t i; int r; i = 0; while (sshbuf_len(decrypted)) { if ((r = sshbuf_get_u8(decrypted, &pad)) != 0) goto out; if (pad != (++i & 0xff)) { r = SSH_ERR_INVALID_FORMAT; goto out; } } /* success */ r = 0; out: explicit_bzero(&pad, sizeof(pad)); explicit_bzero(&i, sizeof(i)); return r; } int sshkey_unshield_private(struct sshkey *k) { struct sshbuf *prvbuf = NULL; u_char *cp, keyiv[SSH_DIGEST_MAX_LENGTH]; struct sshcipher_ctx *cctx = NULL; const struct sshcipher *cipher; struct sshkey *kswap = NULL, tmp; int r = SSH_ERR_INTERNAL_ERROR; #ifdef DEBUG_PK fprintf(stderr, "%s: entering for %s\n", __func__, sshkey_ssh_name(k)); #endif if (!sshkey_is_shielded(k)) return 0; /* nothing to do */ if ((cipher = cipher_by_name(SSHKEY_SHIELD_CIPHER)) == NULL) { r = SSH_ERR_INVALID_ARGUMENT; goto out; } if (cipher_keylen(cipher) + cipher_ivlen(cipher) > ssh_digest_bytes(SSHKEY_SHIELD_PREKEY_HASH)) { r = SSH_ERR_INTERNAL_ERROR; goto out; } /* check size of shielded key blob */ if (k->shielded_len < cipher_blocksize(cipher) || (k->shielded_len % cipher_blocksize(cipher)) != 0) { r = SSH_ERR_INVALID_FORMAT; goto out; } /* Calculate the ephemeral key from the prekey */ if ((r = ssh_digest_memory(SSHKEY_SHIELD_PREKEY_HASH, k->shield_prekey, k->shield_prekey_len, keyiv, SSH_DIGEST_MAX_LENGTH)) != 0) goto out; if ((r = cipher_init(&cctx, cipher, keyiv, cipher_keylen(cipher), keyiv + cipher_keylen(cipher), cipher_ivlen(cipher), 0)) != 0) goto out; #ifdef DEBUG_PK fprintf(stderr, "%s: key+iv\n", __func__); sshbuf_dump_data(keyiv, ssh_digest_bytes(SSHKEY_SHIELD_PREKEY_HASH), stderr); #endif /* Decrypt and parse the shielded private key using the ephemeral key */ if ((prvbuf = sshbuf_new()) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } if ((r = sshbuf_reserve(prvbuf, k->shielded_len, &cp)) != 0) goto out; /* decrypt */ #ifdef DEBUG_PK fprintf(stderr, "%s: encrypted\n", __func__); sshbuf_dump_data(k->shielded_private, k->shielded_len, stderr); #endif if ((r = cipher_crypt(cctx, 0, cp, k->shielded_private, k->shielded_len, 0, 0)) != 0) goto out; #ifdef DEBUG_PK fprintf(stderr, "%s: serialised\n", __func__); sshbuf_dump(prvbuf, stderr); #endif /* Parse private key */ if ((r = sshkey_private_deserialize(prvbuf, &kswap)) != 0) goto out; if ((r = private2_check_padding(prvbuf)) != 0) goto out; /* Swap the parsed key back into place */ tmp = *kswap; *kswap = *k; *k = tmp; /* success */ r = 0; out: cipher_free(cctx); explicit_bzero(keyiv, sizeof(keyiv)); explicit_bzero(&tmp, sizeof(tmp)); sshkey_free(kswap); sshbuf_free(prvbuf); return r; } static int cert_parse(struct sshbuf *b, struct sshkey *key, struct sshbuf *certbuf) { struct sshbuf *principals = NULL, *crit = NULL; struct sshbuf *exts = NULL, *ca = NULL; u_char *sig = NULL; size_t signed_len = 0, slen = 0, kidlen = 0; int ret = SSH_ERR_INTERNAL_ERROR; /* Copy the entire key blob for verification and later serialisation */ if ((ret = sshbuf_putb(key->cert->certblob, certbuf)) != 0) return ret; /* Parse body of certificate up to signature */ if ((ret = sshbuf_get_u64(b, &key->cert->serial)) != 0 || (ret = sshbuf_get_u32(b, &key->cert->type)) != 0 || (ret = sshbuf_get_cstring(b, &key->cert->key_id, &kidlen)) != 0 || (ret = sshbuf_froms(b, &principals)) != 0 || (ret = sshbuf_get_u64(b, &key->cert->valid_after)) != 0 || (ret = sshbuf_get_u64(b, &key->cert->valid_before)) != 0 || (ret = sshbuf_froms(b, &crit)) != 0 || (ret = sshbuf_froms(b, &exts)) != 0 || (ret = sshbuf_get_string_direct(b, NULL, NULL)) != 0 || (ret = sshbuf_froms(b, &ca)) != 0) { /* XXX debug print error for ret */ ret = SSH_ERR_INVALID_FORMAT; goto out; } /* Signature is left in the buffer so we can calculate this length */ signed_len = sshbuf_len(key->cert->certblob) - sshbuf_len(b); if ((ret = sshbuf_get_string(b, &sig, &slen)) != 0) { ret = SSH_ERR_INVALID_FORMAT; goto out; } if (key->cert->type != SSH2_CERT_TYPE_USER && key->cert->type != SSH2_CERT_TYPE_HOST) { ret = SSH_ERR_KEY_CERT_UNKNOWN_TYPE; goto out; } /* Parse principals section */ while (sshbuf_len(principals) > 0) { char *principal = NULL; char **oprincipals = NULL; if (key->cert->nprincipals >= SSHKEY_CERT_MAX_PRINCIPALS) { ret = SSH_ERR_INVALID_FORMAT; goto out; } if ((ret = sshbuf_get_cstring(principals, &principal, NULL)) != 0) { ret = SSH_ERR_INVALID_FORMAT; goto out; } oprincipals = key->cert->principals; key->cert->principals = recallocarray(key->cert->principals, key->cert->nprincipals, key->cert->nprincipals + 1, sizeof(*key->cert->principals)); if (key->cert->principals == NULL) { free(principal); key->cert->principals = oprincipals; ret = SSH_ERR_ALLOC_FAIL; goto out; } key->cert->principals[key->cert->nprincipals++] = principal; } /* * Stash a copies of the critical options and extensions sections * for later use. */ if ((ret = sshbuf_putb(key->cert->critical, crit)) != 0 || (exts != NULL && (ret = sshbuf_putb(key->cert->extensions, exts)) != 0)) goto out; /* * Validate critical options and extensions sections format. */ while (sshbuf_len(crit) != 0) { if ((ret = sshbuf_get_string_direct(crit, NULL, NULL)) != 0 || (ret = sshbuf_get_string_direct(crit, NULL, NULL)) != 0) { sshbuf_reset(key->cert->critical); ret = SSH_ERR_INVALID_FORMAT; goto out; } } while (exts != NULL && sshbuf_len(exts) != 0) { if ((ret = sshbuf_get_string_direct(exts, NULL, NULL)) != 0 || (ret = sshbuf_get_string_direct(exts, NULL, NULL)) != 0) { sshbuf_reset(key->cert->extensions); ret = SSH_ERR_INVALID_FORMAT; goto out; } } /* Parse CA key and check signature */ if (sshkey_from_blob_internal(ca, &key->cert->signature_key, 0) != 0) { ret = SSH_ERR_KEY_CERT_INVALID_SIGN_KEY; goto out; } if (!sshkey_type_is_valid_ca(key->cert->signature_key->type)) { ret = SSH_ERR_KEY_CERT_INVALID_SIGN_KEY; goto out; } if ((ret = sshkey_verify(key->cert->signature_key, sig, slen, sshbuf_ptr(key->cert->certblob), signed_len, NULL, 0, NULL)) != 0) goto out; if ((ret = sshkey_get_sigtype(sig, slen, &key->cert->signature_type)) != 0) goto out; /* Success */ ret = 0; out: sshbuf_free(ca); sshbuf_free(crit); sshbuf_free(exts); sshbuf_free(principals); free(sig); return ret; } int sshkey_deserialize_sk(struct sshbuf *b, struct sshkey *key) { /* Parse additional security-key application string */ if (sshbuf_get_cstring(b, &key->sk_application, NULL) != 0) return SSH_ERR_INVALID_FORMAT; return 0; } static int sshkey_from_blob_internal(struct sshbuf *b, struct sshkey **keyp, int allow_cert) { int type, ret = SSH_ERR_INTERNAL_ERROR; char *ktype = NULL; struct sshkey *key = NULL; struct sshbuf *copy; const struct sshkey_impl *impl; #ifdef DEBUG_PK /* XXX */ sshbuf_dump(b, stderr); #endif if (keyp != NULL) *keyp = NULL; if ((copy = sshbuf_fromb(b)) == NULL) { ret = SSH_ERR_ALLOC_FAIL; goto out; } if (sshbuf_get_cstring(b, &ktype, NULL) != 0) { ret = SSH_ERR_INVALID_FORMAT; goto out; } type = sshkey_type_from_name(ktype); if (!allow_cert && sshkey_type_is_cert(type)) { ret = SSH_ERR_KEY_CERT_INVALID_SIGN_KEY; goto out; } if ((impl = sshkey_impl_from_type(type)) == NULL) { ret = SSH_ERR_KEY_TYPE_UNKNOWN; goto out; } if ((key = sshkey_new(type)) == NULL) { ret = SSH_ERR_ALLOC_FAIL; goto out; } if (sshkey_type_is_cert(type)) { /* Skip nonce that preceeds all certificates */ if (sshbuf_get_string_direct(b, NULL, NULL) != 0) { ret = SSH_ERR_INVALID_FORMAT; goto out; } } if ((ret = impl->funcs->deserialize_public(ktype, b, key)) != 0) goto out; /* Parse certificate potion */ if (sshkey_is_cert(key) && (ret = cert_parse(b, key, copy)) != 0) goto out; if (key != NULL && sshbuf_len(b) != 0) { ret = SSH_ERR_INVALID_FORMAT; goto out; } ret = 0; if (keyp != NULL) { *keyp = key; key = NULL; } out: sshbuf_free(copy); sshkey_free(key); free(ktype); return ret; } int sshkey_from_blob(const u_char *blob, size_t blen, struct sshkey **keyp) { struct sshbuf *b; int r; if ((b = sshbuf_from(blob, blen)) == NULL) return SSH_ERR_ALLOC_FAIL; r = sshkey_from_blob_internal(b, keyp, 1); sshbuf_free(b); return r; } int sshkey_fromb(struct sshbuf *b, struct sshkey **keyp) { return sshkey_from_blob_internal(b, keyp, 1); } int sshkey_froms(struct sshbuf *buf, struct sshkey **keyp) { struct sshbuf *b; int r; if ((r = sshbuf_froms(buf, &b)) != 0) return r; r = sshkey_from_blob_internal(b, keyp, 1); sshbuf_free(b); return r; } int sshkey_get_sigtype(const u_char *sig, size_t siglen, char **sigtypep) { int r; struct sshbuf *b = NULL; char *sigtype = NULL; if (sigtypep != NULL) *sigtypep = NULL; if ((b = sshbuf_from(sig, siglen)) == NULL) return SSH_ERR_ALLOC_FAIL; if ((r = sshbuf_get_cstring(b, &sigtype, NULL)) != 0) goto out; /* success */ if (sigtypep != NULL) { *sigtypep = sigtype; sigtype = NULL; } r = 0; out: free(sigtype); sshbuf_free(b); return r; } /* * * Checks whether a certificate's signature type is allowed. * Returns 0 (success) if the certificate signature type appears in the * "allowed" pattern-list, or the key is not a certificate to begin with. * Otherwise returns a ssherr.h code. */ int sshkey_check_cert_sigtype(const struct sshkey *key, const char *allowed) { if (key == NULL || allowed == NULL) return SSH_ERR_INVALID_ARGUMENT; if (!sshkey_type_is_cert(key->type)) return 0; if (key->cert == NULL || key->cert->signature_type == NULL) return SSH_ERR_INVALID_ARGUMENT; if (match_pattern_list(key->cert->signature_type, allowed, 0) != 1) return SSH_ERR_SIGN_ALG_UNSUPPORTED; return 0; } /* * Returns the expected signature algorithm for a given public key algorithm. */ const char * sshkey_sigalg_by_name(const char *name) { const struct sshkey_impl *impl; int i; for (i = 0; keyimpls[i] != NULL; i++) { impl = keyimpls[i]; if (strcmp(impl->name, name) != 0) continue; if (impl->sigalg != NULL) return impl->sigalg; if (!impl->cert) return impl->name; return sshkey_ssh_name_from_type_nid( sshkey_type_plain(impl->type), impl->nid); } return NULL; } /* * Verifies that the signature algorithm appearing inside the signature blob * matches that which was requested. */ int sshkey_check_sigtype(const u_char *sig, size_t siglen, const char *requested_alg) { const char *expected_alg; char *sigtype = NULL; int r; if (requested_alg == NULL) return 0; if ((expected_alg = sshkey_sigalg_by_name(requested_alg)) == NULL) return SSH_ERR_INVALID_ARGUMENT; if ((r = sshkey_get_sigtype(sig, siglen, &sigtype)) != 0) return r; r = strcmp(expected_alg, sigtype) == 0; free(sigtype); return r ? 0 : SSH_ERR_SIGN_ALG_UNSUPPORTED; } int sshkey_sign(struct sshkey *key, u_char **sigp, size_t *lenp, const u_char *data, size_t datalen, const char *alg, const char *sk_provider, const char *sk_pin, u_int compat) { int was_shielded = sshkey_is_shielded(key); int r2, r = SSH_ERR_INTERNAL_ERROR; const struct sshkey_impl *impl; if (sigp != NULL) *sigp = NULL; if (lenp != NULL) *lenp = 0; if (datalen > SSH_KEY_MAX_SIGN_DATA_SIZE) return SSH_ERR_INVALID_ARGUMENT; if ((impl = sshkey_impl_from_key(key)) == NULL) return SSH_ERR_KEY_TYPE_UNKNOWN; if ((r = sshkey_unshield_private(key)) != 0) return r; if (sshkey_is_sk(key)) { r = sshsk_sign(sk_provider, key, sigp, lenp, data, datalen, compat, sk_pin); } else { if (impl->funcs->sign == NULL) r = SSH_ERR_SIGN_ALG_UNSUPPORTED; else { r = impl->funcs->sign(key, sigp, lenp, data, datalen, alg, sk_provider, sk_pin, compat); } } if (was_shielded && (r2 = sshkey_shield_private(key)) != 0) return r2; return r; } /* * ssh_key_verify returns 0 for a correct signature and < 0 on error. * If "alg" specified, then the signature must use that algorithm. */ int sshkey_verify(const struct sshkey *key, const u_char *sig, size_t siglen, const u_char *data, size_t dlen, const char *alg, u_int compat, struct sshkey_sig_details **detailsp) { const struct sshkey_impl *impl; if (detailsp != NULL) *detailsp = NULL; if (siglen == 0 || dlen > SSH_KEY_MAX_SIGN_DATA_SIZE) return SSH_ERR_INVALID_ARGUMENT; if ((impl = sshkey_impl_from_key(key)) == NULL) return SSH_ERR_KEY_TYPE_UNKNOWN; return impl->funcs->verify(key, sig, siglen, data, dlen, alg, compat, detailsp); } /* Convert a plain key to their _CERT equivalent */ int sshkey_to_certified(struct sshkey *k) { int newtype; if ((newtype = sshkey_type_certified(k->type)) == -1) return SSH_ERR_INVALID_ARGUMENT; if ((k->cert = cert_new()) == NULL) return SSH_ERR_ALLOC_FAIL; k->type = newtype; return 0; } /* Convert a certificate to its raw key equivalent */ int sshkey_drop_cert(struct sshkey *k) { if (!sshkey_type_is_cert(k->type)) return SSH_ERR_KEY_TYPE_UNKNOWN; cert_free(k->cert); k->cert = NULL; k->type = sshkey_type_plain(k->type); return 0; } /* Sign a certified key, (re-)generating the signed certblob. */ int sshkey_certify_custom(struct sshkey *k, struct sshkey *ca, const char *alg, const char *sk_provider, const char *sk_pin, sshkey_certify_signer *signer, void *signer_ctx) { const struct sshkey_impl *impl; struct sshbuf *principals = NULL; u_char *ca_blob = NULL, *sig_blob = NULL, nonce[32]; size_t i, ca_len, sig_len; int ret = SSH_ERR_INTERNAL_ERROR; struct sshbuf *cert = NULL; char *sigtype = NULL; if (k == NULL || k->cert == NULL || k->cert->certblob == NULL || ca == NULL) return SSH_ERR_INVALID_ARGUMENT; if (!sshkey_is_cert(k)) return SSH_ERR_KEY_TYPE_UNKNOWN; if (!sshkey_type_is_valid_ca(ca->type)) return SSH_ERR_KEY_CERT_INVALID_SIGN_KEY; if ((impl = sshkey_impl_from_key(k)) == NULL) return SSH_ERR_INTERNAL_ERROR; /* * If no alg specified as argument but a signature_type was set, * then prefer that. If both were specified, then they must match. */ if (alg == NULL) alg = k->cert->signature_type; else if (k->cert->signature_type != NULL && strcmp(alg, k->cert->signature_type) != 0) return SSH_ERR_INVALID_ARGUMENT; /* * If no signing algorithm or signature_type was specified and we're * using a RSA key, then default to a good signature algorithm. */ if (alg == NULL && ca->type == KEY_RSA) alg = "rsa-sha2-512"; if ((ret = sshkey_to_blob(ca, &ca_blob, &ca_len)) != 0) return SSH_ERR_KEY_CERT_INVALID_SIGN_KEY; cert = k->cert->certblob; /* for readability */ sshbuf_reset(cert); if ((ret = sshbuf_put_cstring(cert, sshkey_ssh_name(k))) != 0) goto out; /* -v01 certs put nonce first */ arc4random_buf(&nonce, sizeof(nonce)); if ((ret = sshbuf_put_string(cert, nonce, sizeof(nonce))) != 0) goto out; /* Public key next */ if ((ret = impl->funcs->serialize_public(k, cert, SSHKEY_SERIALIZE_DEFAULT)) != 0) goto out; /* Then remaining cert fields */ if ((ret = sshbuf_put_u64(cert, k->cert->serial)) != 0 || (ret = sshbuf_put_u32(cert, k->cert->type)) != 0 || (ret = sshbuf_put_cstring(cert, k->cert->key_id)) != 0) goto out; if ((principals = sshbuf_new()) == NULL) { ret = SSH_ERR_ALLOC_FAIL; goto out; } for (i = 0; i < k->cert->nprincipals; i++) { if ((ret = sshbuf_put_cstring(principals, k->cert->principals[i])) != 0) goto out; } if ((ret = sshbuf_put_stringb(cert, principals)) != 0 || (ret = sshbuf_put_u64(cert, k->cert->valid_after)) != 0 || (ret = sshbuf_put_u64(cert, k->cert->valid_before)) != 0 || (ret = sshbuf_put_stringb(cert, k->cert->critical)) != 0 || (ret = sshbuf_put_stringb(cert, k->cert->extensions)) != 0 || (ret = sshbuf_put_string(cert, NULL, 0)) != 0 || /* Reserved */ (ret = sshbuf_put_string(cert, ca_blob, ca_len)) != 0) goto out; /* Sign the whole mess */ if ((ret = signer(ca, &sig_blob, &sig_len, sshbuf_ptr(cert), sshbuf_len(cert), alg, sk_provider, sk_pin, 0, signer_ctx)) != 0) goto out; /* Check and update signature_type against what was actually used */ if ((ret = sshkey_get_sigtype(sig_blob, sig_len, &sigtype)) != 0) goto out; if (alg != NULL && strcmp(alg, sigtype) != 0) { ret = SSH_ERR_SIGN_ALG_UNSUPPORTED; goto out; } if (k->cert->signature_type == NULL) { k->cert->signature_type = sigtype; sigtype = NULL; } /* Append signature and we are done */ if ((ret = sshbuf_put_string(cert, sig_blob, sig_len)) != 0) goto out; ret = 0; out: if (ret != 0) sshbuf_reset(cert); free(sig_blob); free(ca_blob); free(sigtype); sshbuf_free(principals); return ret; } static int default_key_sign(struct sshkey *key, u_char **sigp, size_t *lenp, const u_char *data, size_t datalen, const char *alg, const char *sk_provider, const char *sk_pin, u_int compat, void *ctx) { if (ctx != NULL) return SSH_ERR_INVALID_ARGUMENT; return sshkey_sign(key, sigp, lenp, data, datalen, alg, sk_provider, sk_pin, compat); } int sshkey_certify(struct sshkey *k, struct sshkey *ca, const char *alg, const char *sk_provider, const char *sk_pin) { return sshkey_certify_custom(k, ca, alg, sk_provider, sk_pin, default_key_sign, NULL); } int sshkey_cert_check_authority(const struct sshkey *k, int want_host, int require_principal, int wildcard_pattern, uint64_t verify_time, const char *name, const char **reason) { u_int i, principal_matches; if (reason == NULL) return SSH_ERR_INVALID_ARGUMENT; if (!sshkey_is_cert(k)) { *reason = "Key is not a certificate"; return SSH_ERR_KEY_CERT_INVALID; } if (want_host) { if (k->cert->type != SSH2_CERT_TYPE_HOST) { *reason = "Certificate invalid: not a host certificate"; return SSH_ERR_KEY_CERT_INVALID; } } else { if (k->cert->type != SSH2_CERT_TYPE_USER) { *reason = "Certificate invalid: not a user certificate"; return SSH_ERR_KEY_CERT_INVALID; } } if (verify_time < k->cert->valid_after) { *reason = "Certificate invalid: not yet valid"; return SSH_ERR_KEY_CERT_INVALID; } if (verify_time >= k->cert->valid_before) { *reason = "Certificate invalid: expired"; return SSH_ERR_KEY_CERT_INVALID; } if (k->cert->nprincipals == 0) { if (require_principal) { *reason = "Certificate lacks principal list"; return SSH_ERR_KEY_CERT_INVALID; } } else if (name != NULL) { principal_matches = 0; for (i = 0; i < k->cert->nprincipals; i++) { if (wildcard_pattern) { if (match_pattern(k->cert->principals[i], name)) { principal_matches = 1; break; } } else if (strcmp(name, k->cert->principals[i]) == 0) { principal_matches = 1; break; } } if (!principal_matches) { *reason = "Certificate invalid: name is not a listed " "principal"; return SSH_ERR_KEY_CERT_INVALID; } } return 0; } int sshkey_cert_check_authority_now(const struct sshkey *k, int want_host, int require_principal, int wildcard_pattern, const char *name, const char **reason) { time_t now; if ((now = time(NULL)) < 0) { /* yikes - system clock before epoch! */ *reason = "Certificate invalid: not yet valid"; return SSH_ERR_KEY_CERT_INVALID; } return sshkey_cert_check_authority(k, want_host, require_principal, wildcard_pattern, (uint64_t)now, name, reason); } int sshkey_cert_check_host(const struct sshkey *key, const char *host, int wildcard_principals, const char *ca_sign_algorithms, const char **reason) { int r; if ((r = sshkey_cert_check_authority_now(key, 1, 0, wildcard_principals, host, reason)) != 0) return r; if (sshbuf_len(key->cert->critical) != 0) { *reason = "Certificate contains unsupported critical options"; return SSH_ERR_KEY_CERT_INVALID; } if (ca_sign_algorithms != NULL && (r = sshkey_check_cert_sigtype(key, ca_sign_algorithms)) != 0) { *reason = "Certificate signed with disallowed algorithm"; return SSH_ERR_KEY_CERT_INVALID; } return 0; } size_t sshkey_format_cert_validity(const struct sshkey_cert *cert, char *s, size_t l) { char from[32], to[32], ret[128]; *from = *to = '\0'; if (cert->valid_after == 0 && cert->valid_before == 0xffffffffffffffffULL) return strlcpy(s, "forever", l); if (cert->valid_after != 0) format_absolute_time(cert->valid_after, from, sizeof(from)); if (cert->valid_before != 0xffffffffffffffffULL) format_absolute_time(cert->valid_before, to, sizeof(to)); if (cert->valid_after == 0) snprintf(ret, sizeof(ret), "before %s", to); else if (cert->valid_before == 0xffffffffffffffffULL) snprintf(ret, sizeof(ret), "after %s", from); else snprintf(ret, sizeof(ret), "from %s to %s", from, to); return strlcpy(s, ret, l); } /* Common serialization for FIDO private keys */ int sshkey_serialize_private_sk(const struct sshkey *key, struct sshbuf *b) { int r; if ((r = sshbuf_put_cstring(b, key->sk_application)) != 0 || (r = sshbuf_put_u8(b, key->sk_flags)) != 0 || (r = sshbuf_put_stringb(b, key->sk_key_handle)) != 0 || (r = sshbuf_put_stringb(b, key->sk_reserved)) != 0) return r; return 0; } int sshkey_private_serialize_opt(struct sshkey *key, struct sshbuf *buf, enum sshkey_serialize_rep opts) { int r = SSH_ERR_INTERNAL_ERROR; int was_shielded = sshkey_is_shielded(key); struct sshbuf *b = NULL; const struct sshkey_impl *impl; if ((impl = sshkey_impl_from_key(key)) == NULL) return SSH_ERR_INTERNAL_ERROR; if ((r = sshkey_unshield_private(key)) != 0) return r; if ((b = sshbuf_new()) == NULL) return SSH_ERR_ALLOC_FAIL; if ((r = sshbuf_put_cstring(b, sshkey_ssh_name(key))) != 0) goto out; if (sshkey_is_cert(key)) { if (key->cert == NULL || sshbuf_len(key->cert->certblob) == 0) { r = SSH_ERR_INVALID_ARGUMENT; goto out; } if ((r = sshbuf_put_stringb(b, key->cert->certblob)) != 0) goto out; } if ((r = impl->funcs->serialize_private(key, b, opts)) != 0) goto out; /* * success (but we still need to append the output to buf after * possibly re-shielding the private key) */ r = 0; out: if (was_shielded) r = sshkey_shield_private(key); if (r == 0) r = sshbuf_putb(buf, b); sshbuf_free(b); return r; } int sshkey_private_serialize(struct sshkey *key, struct sshbuf *b) { return sshkey_private_serialize_opt(key, b, SSHKEY_SERIALIZE_DEFAULT); } /* Shared deserialization of FIDO private key components */ int sshkey_private_deserialize_sk(struct sshbuf *buf, struct sshkey *k) { int r; if ((k->sk_key_handle = sshbuf_new()) == NULL || (k->sk_reserved = sshbuf_new()) == NULL) return SSH_ERR_ALLOC_FAIL; if ((r = sshbuf_get_cstring(buf, &k->sk_application, NULL)) != 0 || (r = sshbuf_get_u8(buf, &k->sk_flags)) != 0 || (r = sshbuf_get_stringb(buf, k->sk_key_handle)) != 0 || (r = sshbuf_get_stringb(buf, k->sk_reserved)) != 0) return r; return 0; } int sshkey_private_deserialize(struct sshbuf *buf, struct sshkey **kp) { const struct sshkey_impl *impl; char *tname = NULL; char *expect_sk_application = NULL; u_char *expect_ed25519_pk = NULL; struct sshkey *k = NULL; int type, r = SSH_ERR_INTERNAL_ERROR; if (kp != NULL) *kp = NULL; if ((r = sshbuf_get_cstring(buf, &tname, NULL)) != 0) goto out; type = sshkey_type_from_name(tname); if (sshkey_type_is_cert(type)) { /* * Certificate key private keys begin with the certificate * itself. Make sure this matches the type of the enclosing * private key. */ if ((r = sshkey_froms(buf, &k)) != 0) goto out; if (k->type != type) { r = SSH_ERR_KEY_CERT_MISMATCH; goto out; } /* For ECDSA keys, the group must match too */ if (k->type == KEY_ECDSA && k->ecdsa_nid != sshkey_ecdsa_nid_from_name(tname)) { r = SSH_ERR_KEY_CERT_MISMATCH; goto out; } /* * Several fields are redundant between certificate and * private key body, we require these to match. */ expect_sk_application = k->sk_application; expect_ed25519_pk = k->ed25519_pk; k->sk_application = NULL; k->ed25519_pk = NULL; /* XXX xmss too or refactor */ } else { if ((k = sshkey_new(type)) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } } if ((impl = sshkey_impl_from_type(type)) == NULL) { r = SSH_ERR_INTERNAL_ERROR; goto out; } if ((r = impl->funcs->deserialize_private(tname, buf, k)) != 0) goto out; /* XXX xmss too or refactor */ if ((expect_sk_application != NULL && (k->sk_application == NULL || strcmp(expect_sk_application, k->sk_application) != 0)) || (expect_ed25519_pk != NULL && (k->ed25519_pk == NULL || memcmp(expect_ed25519_pk, k->ed25519_pk, ED25519_PK_SZ) != 0))) { r = SSH_ERR_KEY_CERT_MISMATCH; goto out; } /* success */ r = 0; if (kp != NULL) { *kp = k; k = NULL; } out: free(tname); sshkey_free(k); free(expect_sk_application); free(expect_ed25519_pk); return r; } #if defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC) int sshkey_ec_validate_public(const EC_GROUP *group, const EC_POINT *public) { EC_POINT *nq = NULL; BIGNUM *order = NULL, *x = NULL, *y = NULL, *tmp = NULL; int ret = SSH_ERR_KEY_INVALID_EC_VALUE; /* * NB. This assumes OpenSSL has already verified that the public * point lies on the curve. This is done by EC_POINT_oct2point() * implicitly calling EC_POINT_is_on_curve(). If this code is ever * reachable with public points not unmarshalled using * EC_POINT_oct2point then the caller will need to explicitly check. */ /* * We shouldn't ever hit this case because bignum_get_ecpoint() * refuses to load GF2m points. */ if (EC_METHOD_get_field_type(EC_GROUP_method_of(group)) != NID_X9_62_prime_field) goto out; /* Q != infinity */ if (EC_POINT_is_at_infinity(group, public)) goto out; if ((x = BN_new()) == NULL || (y = BN_new()) == NULL || (order = BN_new()) == NULL || (tmp = BN_new()) == NULL) { ret = SSH_ERR_ALLOC_FAIL; goto out; } /* log2(x) > log2(order)/2, log2(y) > log2(order)/2 */ if (EC_GROUP_get_order(group, order, NULL) != 1 || EC_POINT_get_affine_coordinates_GFp(group, public, x, y, NULL) != 1) { ret = SSH_ERR_LIBCRYPTO_ERROR; goto out; } if (BN_num_bits(x) <= BN_num_bits(order) / 2 || BN_num_bits(y) <= BN_num_bits(order) / 2) goto out; /* nQ == infinity (n == order of subgroup) */ if ((nq = EC_POINT_new(group)) == NULL) { ret = SSH_ERR_ALLOC_FAIL; goto out; } if (EC_POINT_mul(group, nq, NULL, public, order, NULL) != 1) { ret = SSH_ERR_LIBCRYPTO_ERROR; goto out; } if (EC_POINT_is_at_infinity(group, nq) != 1) goto out; /* x < order - 1, y < order - 1 */ if (!BN_sub(tmp, order, BN_value_one())) { ret = SSH_ERR_LIBCRYPTO_ERROR; goto out; } if (BN_cmp(x, tmp) >= 0 || BN_cmp(y, tmp) >= 0) goto out; ret = 0; out: BN_clear_free(x); BN_clear_free(y); BN_clear_free(order); BN_clear_free(tmp); EC_POINT_free(nq); return ret; } int sshkey_ec_validate_private(const EC_KEY *key) { BIGNUM *order = NULL, *tmp = NULL; int ret = SSH_ERR_KEY_INVALID_EC_VALUE; if ((order = BN_new()) == NULL || (tmp = BN_new()) == NULL) { ret = SSH_ERR_ALLOC_FAIL; goto out; } /* log2(private) > log2(order)/2 */ if (EC_GROUP_get_order(EC_KEY_get0_group(key), order, NULL) != 1) { ret = SSH_ERR_LIBCRYPTO_ERROR; goto out; } if (BN_num_bits(EC_KEY_get0_private_key(key)) <= BN_num_bits(order) / 2) goto out; /* private < order - 1 */ if (!BN_sub(tmp, order, BN_value_one())) { ret = SSH_ERR_LIBCRYPTO_ERROR; goto out; } if (BN_cmp(EC_KEY_get0_private_key(key), tmp) >= 0) goto out; ret = 0; out: BN_clear_free(order); BN_clear_free(tmp); return ret; } void sshkey_dump_ec_point(const EC_GROUP *group, const EC_POINT *point) { BIGNUM *x = NULL, *y = NULL; if (point == NULL) { fputs("point=(NULL)\n", stderr); return; } if ((x = BN_new()) == NULL || (y = BN_new()) == NULL) { fprintf(stderr, "%s: BN_new failed\n", __func__); goto out; } if (EC_METHOD_get_field_type(EC_GROUP_method_of(group)) != NID_X9_62_prime_field) { fprintf(stderr, "%s: group is not a prime field\n", __func__); goto out; } if (EC_POINT_get_affine_coordinates_GFp(group, point, x, y, NULL) != 1) { fprintf(stderr, "%s: EC_POINT_get_affine_coordinates_GFp\n", __func__); goto out; } fputs("x=", stderr); BN_print_fp(stderr, x); fputs("\ny=", stderr); BN_print_fp(stderr, y); fputs("\n", stderr); out: BN_clear_free(x); BN_clear_free(y); } void sshkey_dump_ec_key(const EC_KEY *key) { const BIGNUM *exponent; sshkey_dump_ec_point(EC_KEY_get0_group(key), EC_KEY_get0_public_key(key)); fputs("exponent=", stderr); if ((exponent = EC_KEY_get0_private_key(key)) == NULL) fputs("(NULL)", stderr); else BN_print_fp(stderr, EC_KEY_get0_private_key(key)); fputs("\n", stderr); } #endif /* WITH_OPENSSL && OPENSSL_HAS_ECC */ static int sshkey_private_to_blob2(struct sshkey *prv, struct sshbuf *blob, const char *passphrase, const char *comment, const char *ciphername, int rounds) { u_char *cp, *key = NULL, *pubkeyblob = NULL; u_char salt[SALT_LEN]; size_t i, pubkeylen, keylen, ivlen, blocksize, authlen; u_int check; int r = SSH_ERR_INTERNAL_ERROR; struct sshcipher_ctx *ciphercontext = NULL; const struct sshcipher *cipher; const char *kdfname = KDFNAME; struct sshbuf *encoded = NULL, *encrypted = NULL, *kdf = NULL; if (rounds <= 0) rounds = DEFAULT_ROUNDS; if (passphrase == NULL || !strlen(passphrase)) { ciphername = "none"; kdfname = "none"; } else if (ciphername == NULL) ciphername = DEFAULT_CIPHERNAME; if ((cipher = cipher_by_name(ciphername)) == NULL) { r = SSH_ERR_INVALID_ARGUMENT; goto out; } if ((kdf = sshbuf_new()) == NULL || (encoded = sshbuf_new()) == NULL || (encrypted = sshbuf_new()) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } blocksize = cipher_blocksize(cipher); keylen = cipher_keylen(cipher); ivlen = cipher_ivlen(cipher); authlen = cipher_authlen(cipher); if ((key = calloc(1, keylen + ivlen)) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } if (strcmp(kdfname, "bcrypt") == 0) { arc4random_buf(salt, SALT_LEN); if (bcrypt_pbkdf(passphrase, strlen(passphrase), salt, SALT_LEN, key, keylen + ivlen, rounds) < 0) { r = SSH_ERR_INVALID_ARGUMENT; goto out; } if ((r = sshbuf_put_string(kdf, salt, SALT_LEN)) != 0 || (r = sshbuf_put_u32(kdf, rounds)) != 0) goto out; } else if (strcmp(kdfname, "none") != 0) { /* Unsupported KDF type */ r = SSH_ERR_KEY_UNKNOWN_CIPHER; goto out; } if ((r = cipher_init(&ciphercontext, cipher, key, keylen, key + keylen, ivlen, 1)) != 0) goto out; if ((r = sshbuf_put(encoded, AUTH_MAGIC, sizeof(AUTH_MAGIC))) != 0 || (r = sshbuf_put_cstring(encoded, ciphername)) != 0 || (r = sshbuf_put_cstring(encoded, kdfname)) != 0 || (r = sshbuf_put_stringb(encoded, kdf)) != 0 || (r = sshbuf_put_u32(encoded, 1)) != 0 || /* number of keys */ (r = sshkey_to_blob(prv, &pubkeyblob, &pubkeylen)) != 0 || (r = sshbuf_put_string(encoded, pubkeyblob, pubkeylen)) != 0) goto out; /* set up the buffer that will be encrypted */ /* Random check bytes */ check = arc4random(); if ((r = sshbuf_put_u32(encrypted, check)) != 0 || (r = sshbuf_put_u32(encrypted, check)) != 0) goto out; /* append private key and comment*/ if ((r = sshkey_private_serialize_opt(prv, encrypted, SSHKEY_SERIALIZE_FULL)) != 0 || (r = sshbuf_put_cstring(encrypted, comment)) != 0) goto out; /* padding */ i = 0; while (sshbuf_len(encrypted) % blocksize) { if ((r = sshbuf_put_u8(encrypted, ++i & 0xff)) != 0) goto out; } /* length in destination buffer */ if ((r = sshbuf_put_u32(encoded, sshbuf_len(encrypted))) != 0) goto out; /* encrypt */ if ((r = sshbuf_reserve(encoded, sshbuf_len(encrypted) + authlen, &cp)) != 0) goto out; if ((r = cipher_crypt(ciphercontext, 0, cp, sshbuf_ptr(encrypted), sshbuf_len(encrypted), 0, authlen)) != 0) goto out; sshbuf_reset(blob); /* assemble uuencoded key */ if ((r = sshbuf_put(blob, MARK_BEGIN, MARK_BEGIN_LEN)) != 0 || (r = sshbuf_dtob64(encoded, blob, 1)) != 0 || (r = sshbuf_put(blob, MARK_END, MARK_END_LEN)) != 0) goto out; /* success */ r = 0; out: sshbuf_free(kdf); sshbuf_free(encoded); sshbuf_free(encrypted); cipher_free(ciphercontext); explicit_bzero(salt, sizeof(salt)); if (key != NULL) freezero(key, keylen + ivlen); if (pubkeyblob != NULL) freezero(pubkeyblob, pubkeylen); return r; } static int private2_uudecode(struct sshbuf *blob, struct sshbuf **decodedp) { const u_char *cp; size_t encoded_len; int r; u_char last; struct sshbuf *encoded = NULL, *decoded = NULL; if (blob == NULL || decodedp == NULL) return SSH_ERR_INVALID_ARGUMENT; *decodedp = NULL; if ((encoded = sshbuf_new()) == NULL || (decoded = sshbuf_new()) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } /* check preamble */ cp = sshbuf_ptr(blob); encoded_len = sshbuf_len(blob); if (encoded_len < (MARK_BEGIN_LEN + MARK_END_LEN) || memcmp(cp, MARK_BEGIN, MARK_BEGIN_LEN) != 0) { r = SSH_ERR_INVALID_FORMAT; goto out; } cp += MARK_BEGIN_LEN; encoded_len -= MARK_BEGIN_LEN; /* Look for end marker, removing whitespace as we go */ while (encoded_len > 0) { if (*cp != '\n' && *cp != '\r') { if ((r = sshbuf_put_u8(encoded, *cp)) != 0) goto out; } last = *cp; encoded_len--; cp++; if (last == '\n') { if (encoded_len >= MARK_END_LEN && memcmp(cp, MARK_END, MARK_END_LEN) == 0) { /* \0 terminate */ if ((r = sshbuf_put_u8(encoded, 0)) != 0) goto out; break; } } } if (encoded_len == 0) { r = SSH_ERR_INVALID_FORMAT; goto out; } /* decode base64 */ if ((r = sshbuf_b64tod(decoded, (char *)sshbuf_ptr(encoded))) != 0) goto out; /* check magic */ if (sshbuf_len(decoded) < sizeof(AUTH_MAGIC) || memcmp(sshbuf_ptr(decoded), AUTH_MAGIC, sizeof(AUTH_MAGIC))) { r = SSH_ERR_INVALID_FORMAT; goto out; } /* success */ *decodedp = decoded; decoded = NULL; r = 0; out: sshbuf_free(encoded); sshbuf_free(decoded); return r; } static int private2_decrypt(struct sshbuf *decoded, const char *passphrase, struct sshbuf **decryptedp, struct sshkey **pubkeyp) { char *ciphername = NULL, *kdfname = NULL; const struct sshcipher *cipher = NULL; int r = SSH_ERR_INTERNAL_ERROR; size_t keylen = 0, ivlen = 0, authlen = 0, slen = 0; struct sshbuf *kdf = NULL, *decrypted = NULL; struct sshcipher_ctx *ciphercontext = NULL; struct sshkey *pubkey = NULL; u_char *key = NULL, *salt = NULL, *dp; u_int blocksize, rounds, nkeys, encrypted_len, check1, check2; if (decoded == NULL || decryptedp == NULL || pubkeyp == NULL) return SSH_ERR_INVALID_ARGUMENT; *decryptedp = NULL; *pubkeyp = NULL; if ((decrypted = sshbuf_new()) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } /* parse public portion of key */ if ((r = sshbuf_consume(decoded, sizeof(AUTH_MAGIC))) != 0 || (r = sshbuf_get_cstring(decoded, &ciphername, NULL)) != 0 || (r = sshbuf_get_cstring(decoded, &kdfname, NULL)) != 0 || (r = sshbuf_froms(decoded, &kdf)) != 0 || (r = sshbuf_get_u32(decoded, &nkeys)) != 0) goto out; if (nkeys != 1) { /* XXX only one key supported at present */ r = SSH_ERR_INVALID_FORMAT; goto out; } if ((r = sshkey_froms(decoded, &pubkey)) != 0 || (r = sshbuf_get_u32(decoded, &encrypted_len)) != 0) goto out; if ((cipher = cipher_by_name(ciphername)) == NULL) { r = SSH_ERR_KEY_UNKNOWN_CIPHER; goto out; } if (strcmp(kdfname, "none") != 0 && strcmp(kdfname, "bcrypt") != 0) { r = SSH_ERR_KEY_UNKNOWN_CIPHER; goto out; } if (strcmp(kdfname, "none") == 0 && strcmp(ciphername, "none") != 0) { r = SSH_ERR_INVALID_FORMAT; goto out; } if ((passphrase == NULL || strlen(passphrase) == 0) && strcmp(kdfname, "none") != 0) { /* passphrase required */ r = SSH_ERR_KEY_WRONG_PASSPHRASE; goto out; } /* check size of encrypted key blob */ blocksize = cipher_blocksize(cipher); if (encrypted_len < blocksize || (encrypted_len % blocksize) != 0) { r = SSH_ERR_INVALID_FORMAT; goto out; } /* setup key */ keylen = cipher_keylen(cipher); ivlen = cipher_ivlen(cipher); authlen = cipher_authlen(cipher); if ((key = calloc(1, keylen + ivlen)) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } if (strcmp(kdfname, "bcrypt") == 0) { if ((r = sshbuf_get_string(kdf, &salt, &slen)) != 0 || (r = sshbuf_get_u32(kdf, &rounds)) != 0) goto out; if (bcrypt_pbkdf(passphrase, strlen(passphrase), salt, slen, key, keylen + ivlen, rounds) < 0) { r = SSH_ERR_INVALID_FORMAT; goto out; } } /* check that an appropriate amount of auth data is present */ if (sshbuf_len(decoded) < authlen || sshbuf_len(decoded) - authlen < encrypted_len) { r = SSH_ERR_INVALID_FORMAT; goto out; } /* decrypt private portion of key */ if ((r = sshbuf_reserve(decrypted, encrypted_len, &dp)) != 0 || (r = cipher_init(&ciphercontext, cipher, key, keylen, key + keylen, ivlen, 0)) != 0) goto out; if ((r = cipher_crypt(ciphercontext, 0, dp, sshbuf_ptr(decoded), encrypted_len, 0, authlen)) != 0) { /* an integrity error here indicates an incorrect passphrase */ if (r == SSH_ERR_MAC_INVALID) r = SSH_ERR_KEY_WRONG_PASSPHRASE; goto out; } if ((r = sshbuf_consume(decoded, encrypted_len + authlen)) != 0) goto out; /* there should be no trailing data */ if (sshbuf_len(decoded) != 0) { r = SSH_ERR_INVALID_FORMAT; goto out; } /* check check bytes */ if ((r = sshbuf_get_u32(decrypted, &check1)) != 0 || (r = sshbuf_get_u32(decrypted, &check2)) != 0) goto out; if (check1 != check2) { r = SSH_ERR_KEY_WRONG_PASSPHRASE; goto out; } /* success */ *decryptedp = decrypted; decrypted = NULL; *pubkeyp = pubkey; pubkey = NULL; r = 0; out: cipher_free(ciphercontext); free(ciphername); free(kdfname); sshkey_free(pubkey); if (salt != NULL) { explicit_bzero(salt, slen); free(salt); } if (key != NULL) { explicit_bzero(key, keylen + ivlen); free(key); } sshbuf_free(kdf); sshbuf_free(decrypted); return r; } static int sshkey_parse_private2(struct sshbuf *blob, int type, const char *passphrase, struct sshkey **keyp, char **commentp) { char *comment = NULL; int r = SSH_ERR_INTERNAL_ERROR; struct sshbuf *decoded = NULL, *decrypted = NULL; struct sshkey *k = NULL, *pubkey = NULL; if (keyp != NULL) *keyp = NULL; if (commentp != NULL) *commentp = NULL; /* Undo base64 encoding and decrypt the private section */ if ((r = private2_uudecode(blob, &decoded)) != 0 || (r = private2_decrypt(decoded, passphrase, &decrypted, &pubkey)) != 0) goto out; if (type != KEY_UNSPEC && sshkey_type_plain(type) != sshkey_type_plain(pubkey->type)) { r = SSH_ERR_KEY_TYPE_MISMATCH; goto out; } /* Load the private key and comment */ if ((r = sshkey_private_deserialize(decrypted, &k)) != 0 || (r = sshbuf_get_cstring(decrypted, &comment, NULL)) != 0) goto out; /* Check deterministic padding after private section */ if ((r = private2_check_padding(decrypted)) != 0) goto out; /* Check that the public key in the envelope matches the private key */ if (!sshkey_equal(pubkey, k)) { r = SSH_ERR_INVALID_FORMAT; goto out; } /* success */ r = 0; if (keyp != NULL) { *keyp = k; k = NULL; } if (commentp != NULL) { *commentp = comment; comment = NULL; } out: free(comment); sshbuf_free(decoded); sshbuf_free(decrypted); sshkey_free(k); sshkey_free(pubkey); return r; } static int sshkey_parse_private2_pubkey(struct sshbuf *blob, int type, struct sshkey **keyp) { int r = SSH_ERR_INTERNAL_ERROR; struct sshbuf *decoded = NULL; struct sshkey *pubkey = NULL; u_int nkeys = 0; if (keyp != NULL) *keyp = NULL; if ((r = private2_uudecode(blob, &decoded)) != 0) goto out; /* parse public key from unencrypted envelope */ if ((r = sshbuf_consume(decoded, sizeof(AUTH_MAGIC))) != 0 || (r = sshbuf_skip_string(decoded)) != 0 || /* cipher */ (r = sshbuf_skip_string(decoded)) != 0 || /* KDF alg */ (r = sshbuf_skip_string(decoded)) != 0 || /* KDF hint */ (r = sshbuf_get_u32(decoded, &nkeys)) != 0) goto out; if (nkeys != 1) { /* XXX only one key supported at present */ r = SSH_ERR_INVALID_FORMAT; goto out; } /* Parse the public key */ if ((r = sshkey_froms(decoded, &pubkey)) != 0) goto out; if (type != KEY_UNSPEC && sshkey_type_plain(type) != sshkey_type_plain(pubkey->type)) { r = SSH_ERR_KEY_TYPE_MISMATCH; goto out; } /* success */ r = 0; if (keyp != NULL) { *keyp = pubkey; pubkey = NULL; } out: sshbuf_free(decoded); sshkey_free(pubkey); return r; } #ifdef WITH_OPENSSL /* convert SSH v2 key to PEM or PKCS#8 format */ static int sshkey_private_to_blob_pem_pkcs8(struct sshkey *key, struct sshbuf *buf, int format, const char *_passphrase, const char *comment) { int was_shielded = sshkey_is_shielded(key); int success, r; int blen, len = strlen(_passphrase); u_char *passphrase = (len > 0) ? (u_char *)_passphrase : NULL; const EVP_CIPHER *cipher = (len > 0) ? EVP_aes_128_cbc() : NULL; char *bptr; BIO *bio = NULL; struct sshbuf *blob; EVP_PKEY *pkey = NULL; if (len > 0 && len <= 4) return SSH_ERR_PASSPHRASE_TOO_SHORT; if ((blob = sshbuf_new()) == NULL) return SSH_ERR_ALLOC_FAIL; if ((bio = BIO_new(BIO_s_mem())) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } if (format == SSHKEY_PRIVATE_PKCS8 && (pkey = EVP_PKEY_new()) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } if ((r = sshkey_unshield_private(key)) != 0) goto out; switch (key->type) { case KEY_DSA: if (format == SSHKEY_PRIVATE_PEM) { success = PEM_write_bio_DSAPrivateKey(bio, key->dsa, cipher, passphrase, len, NULL, NULL); } else { success = EVP_PKEY_set1_DSA(pkey, key->dsa); } break; #ifdef OPENSSL_HAS_ECC case KEY_ECDSA: if (format == SSHKEY_PRIVATE_PEM) { success = PEM_write_bio_ECPrivateKey(bio, key->ecdsa, cipher, passphrase, len, NULL, NULL); } else { success = EVP_PKEY_set1_EC_KEY(pkey, key->ecdsa); } break; #endif case KEY_RSA: if (format == SSHKEY_PRIVATE_PEM) { success = PEM_write_bio_RSAPrivateKey(bio, key->rsa, cipher, passphrase, len, NULL, NULL); } else { success = EVP_PKEY_set1_RSA(pkey, key->rsa); } break; default: success = 0; break; } if (success == 0) { r = SSH_ERR_LIBCRYPTO_ERROR; goto out; } if (format == SSHKEY_PRIVATE_PKCS8) { if ((success = PEM_write_bio_PrivateKey(bio, pkey, cipher, passphrase, len, NULL, NULL)) == 0) { r = SSH_ERR_LIBCRYPTO_ERROR; goto out; } } if ((blen = BIO_get_mem_data(bio, &bptr)) <= 0) { r = SSH_ERR_INTERNAL_ERROR; goto out; } if ((r = sshbuf_put(blob, bptr, blen)) != 0) goto out; r = 0; out: if (was_shielded) r = sshkey_shield_private(key); if (r == 0) r = sshbuf_putb(buf, blob); EVP_PKEY_free(pkey); sshbuf_free(blob); BIO_free(bio); return r; } #endif /* WITH_OPENSSL */ /* Serialise "key" to buffer "blob" */ int sshkey_private_to_fileblob(struct sshkey *key, struct sshbuf *blob, const char *passphrase, const char *comment, int format, const char *openssh_format_cipher, int openssh_format_rounds) { switch (key->type) { #ifdef WITH_OPENSSL case KEY_DSA: case KEY_ECDSA: case KEY_RSA: break; /* see below */ #endif /* WITH_OPENSSL */ case KEY_ED25519: case KEY_ED25519_SK: #ifdef WITH_XMSS case KEY_XMSS: #endif /* WITH_XMSS */ #ifdef WITH_OPENSSL case KEY_ECDSA_SK: #endif /* WITH_OPENSSL */ return sshkey_private_to_blob2(key, blob, passphrase, comment, openssh_format_cipher, openssh_format_rounds); default: return SSH_ERR_KEY_TYPE_UNKNOWN; } #ifdef WITH_OPENSSL switch (format) { case SSHKEY_PRIVATE_OPENSSH: return sshkey_private_to_blob2(key, blob, passphrase, comment, openssh_format_cipher, openssh_format_rounds); case SSHKEY_PRIVATE_PEM: case SSHKEY_PRIVATE_PKCS8: return sshkey_private_to_blob_pem_pkcs8(key, blob, format, passphrase, comment); default: return SSH_ERR_INVALID_ARGUMENT; } #endif /* WITH_OPENSSL */ } #ifdef WITH_OPENSSL static int translate_libcrypto_error(unsigned long pem_err) { int pem_reason = ERR_GET_REASON(pem_err); switch (ERR_GET_LIB(pem_err)) { case ERR_LIB_PEM: switch (pem_reason) { case PEM_R_BAD_PASSWORD_READ: #ifdef PEM_R_PROBLEMS_GETTING_PASSWORD case PEM_R_PROBLEMS_GETTING_PASSWORD: #endif #ifdef PEM_R_BAD_DECRYPT case PEM_R_BAD_DECRYPT: #endif return SSH_ERR_KEY_WRONG_PASSPHRASE; default: return SSH_ERR_INVALID_FORMAT; } case ERR_LIB_EVP: switch (pem_reason) { #ifdef EVP_R_BAD_DECRYPT case EVP_R_BAD_DECRYPT: return SSH_ERR_KEY_WRONG_PASSPHRASE; #endif #ifdef EVP_R_BN_DECODE_ERROR case EVP_R_BN_DECODE_ERROR: #endif case EVP_R_DECODE_ERROR: #ifdef EVP_R_PRIVATE_KEY_DECODE_ERROR case EVP_R_PRIVATE_KEY_DECODE_ERROR: #endif return SSH_ERR_INVALID_FORMAT; default: return SSH_ERR_LIBCRYPTO_ERROR; } case ERR_LIB_ASN1: return SSH_ERR_INVALID_FORMAT; } return SSH_ERR_LIBCRYPTO_ERROR; } static void clear_libcrypto_errors(void) { while (ERR_get_error() != 0) ; } /* * Translate OpenSSL error codes to determine whether * passphrase is required/incorrect. */ static int convert_libcrypto_error(void) { /* * Some password errors are reported at the beginning * of the error queue. */ if (translate_libcrypto_error(ERR_peek_error()) == SSH_ERR_KEY_WRONG_PASSPHRASE) return SSH_ERR_KEY_WRONG_PASSPHRASE; return translate_libcrypto_error(ERR_peek_last_error()); } static int pem_passphrase_cb(char *buf, int size, int rwflag, void *u) { char *p = (char *)u; size_t len; if (p == NULL || (len = strlen(p)) == 0) return -1; if (size < 0 || len > (size_t)size) return -1; memcpy(buf, p, len); return (int)len; } static int sshkey_parse_private_pem_fileblob(struct sshbuf *blob, int type, const char *passphrase, struct sshkey **keyp) { EVP_PKEY *pk = NULL; struct sshkey *prv = NULL; BIO *bio = NULL; int r; if (keyp != NULL) *keyp = NULL; if ((bio = BIO_new(BIO_s_mem())) == NULL || sshbuf_len(blob) > INT_MAX) return SSH_ERR_ALLOC_FAIL; if (BIO_write(bio, sshbuf_ptr(blob), sshbuf_len(blob)) != (int)sshbuf_len(blob)) { r = SSH_ERR_ALLOC_FAIL; goto out; } clear_libcrypto_errors(); if ((pk = PEM_read_bio_PrivateKey(bio, NULL, pem_passphrase_cb, (char *)passphrase)) == NULL) { /* * libcrypto may return various ASN.1 errors when attempting * to parse a key with an incorrect passphrase. * Treat all format errors as "incorrect passphrase" if a * passphrase was supplied. */ if (passphrase != NULL && *passphrase != '\0') r = SSH_ERR_KEY_WRONG_PASSPHRASE; else r = convert_libcrypto_error(); goto out; } if (EVP_PKEY_base_id(pk) == EVP_PKEY_RSA && (type == KEY_UNSPEC || type == KEY_RSA)) { if ((prv = sshkey_new(KEY_UNSPEC)) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } prv->rsa = EVP_PKEY_get1_RSA(pk); prv->type = KEY_RSA; #ifdef DEBUG_PK RSA_print_fp(stderr, prv->rsa, 8); #endif if (RSA_blinding_on(prv->rsa, NULL) != 1) { r = SSH_ERR_LIBCRYPTO_ERROR; goto out; } if ((r = sshkey_check_rsa_length(prv, 0)) != 0) goto out; } else if (EVP_PKEY_base_id(pk) == EVP_PKEY_DSA && (type == KEY_UNSPEC || type == KEY_DSA)) { if ((prv = sshkey_new(KEY_UNSPEC)) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } prv->dsa = EVP_PKEY_get1_DSA(pk); prv->type = KEY_DSA; #ifdef DEBUG_PK DSA_print_fp(stderr, prv->dsa, 8); #endif #ifdef OPENSSL_HAS_ECC } else if (EVP_PKEY_base_id(pk) == EVP_PKEY_EC && (type == KEY_UNSPEC || type == KEY_ECDSA)) { if ((prv = sshkey_new(KEY_UNSPEC)) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } prv->ecdsa = EVP_PKEY_get1_EC_KEY(pk); prv->type = KEY_ECDSA; prv->ecdsa_nid = sshkey_ecdsa_key_to_nid(prv->ecdsa); if (prv->ecdsa_nid == -1 || sshkey_curve_nid_to_name(prv->ecdsa_nid) == NULL || sshkey_ec_validate_public(EC_KEY_get0_group(prv->ecdsa), EC_KEY_get0_public_key(prv->ecdsa)) != 0 || sshkey_ec_validate_private(prv->ecdsa) != 0) { r = SSH_ERR_INVALID_FORMAT; goto out; } # ifdef DEBUG_PK if (prv != NULL && prv->ecdsa != NULL) sshkey_dump_ec_key(prv->ecdsa); # endif #endif /* OPENSSL_HAS_ECC */ } else { r = SSH_ERR_INVALID_FORMAT; goto out; } r = 0; if (keyp != NULL) { *keyp = prv; prv = NULL; } out: BIO_free(bio); EVP_PKEY_free(pk); sshkey_free(prv); return r; } #endif /* WITH_OPENSSL */ int sshkey_parse_private_fileblob_type(struct sshbuf *blob, int type, const char *passphrase, struct sshkey **keyp, char **commentp) { int r = SSH_ERR_INTERNAL_ERROR; if (keyp != NULL) *keyp = NULL; if (commentp != NULL) *commentp = NULL; switch (type) { case KEY_ED25519: case KEY_XMSS: /* No fallback for new-format-only keys */ return sshkey_parse_private2(blob, type, passphrase, keyp, commentp); default: r = sshkey_parse_private2(blob, type, passphrase, keyp, commentp); /* Only fallback to PEM parser if a format error occurred. */ if (r != SSH_ERR_INVALID_FORMAT) return r; #ifdef WITH_OPENSSL return sshkey_parse_private_pem_fileblob(blob, type, passphrase, keyp); #else return SSH_ERR_INVALID_FORMAT; #endif /* WITH_OPENSSL */ } } int sshkey_parse_private_fileblob(struct sshbuf *buffer, const char *passphrase, struct sshkey **keyp, char **commentp) { if (keyp != NULL) *keyp = NULL; if (commentp != NULL) *commentp = NULL; return sshkey_parse_private_fileblob_type(buffer, KEY_UNSPEC, passphrase, keyp, commentp); } void sshkey_sig_details_free(struct sshkey_sig_details *details) { freezero(details, sizeof(*details)); } int sshkey_parse_pubkey_from_private_fileblob_type(struct sshbuf *blob, int type, struct sshkey **pubkeyp) { int r = SSH_ERR_INTERNAL_ERROR; if (pubkeyp != NULL) *pubkeyp = NULL; /* only new-format private keys bundle a public key inside */ if ((r = sshkey_parse_private2_pubkey(blob, type, pubkeyp)) != 0) return r; return 0; } #ifdef WITH_XMSS /* * serialize the key with the current state and forward the state * maxsign times. */ int sshkey_private_serialize_maxsign(struct sshkey *k, struct sshbuf *b, u_int32_t maxsign, int printerror) { int r, rupdate; if (maxsign == 0 || sshkey_type_plain(k->type) != KEY_XMSS) return sshkey_private_serialize_opt(k, b, SSHKEY_SERIALIZE_DEFAULT); if ((r = sshkey_xmss_get_state(k, printerror)) != 0 || (r = sshkey_private_serialize_opt(k, b, SSHKEY_SERIALIZE_STATE)) != 0 || (r = sshkey_xmss_forward_state(k, maxsign)) != 0) goto out; r = 0; out: if ((rupdate = sshkey_xmss_update_state(k, printerror)) != 0) { if (r == 0) r = rupdate; } return r; } u_int32_t sshkey_signatures_left(const struct sshkey *k) { if (sshkey_type_plain(k->type) == KEY_XMSS) return sshkey_xmss_signatures_left(k); return 0; } int sshkey_enable_maxsign(struct sshkey *k, u_int32_t maxsign) { if (sshkey_type_plain(k->type) != KEY_XMSS) return SSH_ERR_INVALID_ARGUMENT; return sshkey_xmss_enable_maxsign(k, maxsign); } int sshkey_set_filename(struct sshkey *k, const char *filename) { if (k == NULL) return SSH_ERR_INVALID_ARGUMENT; if (sshkey_type_plain(k->type) != KEY_XMSS) return 0; if (filename == NULL) return SSH_ERR_INVALID_ARGUMENT; if ((k->xmss_filename = strdup(filename)) == NULL) return SSH_ERR_ALLOC_FAIL; return 0; } #else int sshkey_private_serialize_maxsign(struct sshkey *k, struct sshbuf *b, u_int32_t maxsign, int printerror) { return sshkey_private_serialize_opt(k, b, SSHKEY_SERIALIZE_DEFAULT); } u_int32_t sshkey_signatures_left(const struct sshkey *k) { return 0; } int sshkey_enable_maxsign(struct sshkey *k, u_int32_t maxsign) { return SSH_ERR_INVALID_ARGUMENT; } int sshkey_set_filename(struct sshkey *k, const char *filename) { if (k == NULL) return SSH_ERR_INVALID_ARGUMENT; return 0; } #endif /* WITH_XMSS */ diff --git a/sshsig.c b/sshsig.c index 854d67322409..d219db90e9a3 100644 --- a/sshsig.c +++ b/sshsig.c @@ -1,1147 +1,1158 @@ -/* $OpenBSD: sshsig.c,v 1.32 2023/04/06 03:56:02 djm Exp $ */ +/* $OpenBSD: sshsig.c,v 1.33 2023/09/06 23:18:15 djm Exp $ */ /* * Copyright (c) 2019 Google LLC * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "includes.h" #include #include #include #include #include #include #include "authfd.h" #include "authfile.h" #include "log.h" #include "misc.h" #include "sshbuf.h" #include "sshsig.h" #include "ssherr.h" #include "sshkey.h" #include "match.h" #include "digest.h" #define SIG_VERSION 0x01 #define MAGIC_PREAMBLE "SSHSIG" #define MAGIC_PREAMBLE_LEN (sizeof(MAGIC_PREAMBLE) - 1) -#define BEGIN_SIGNATURE "-----BEGIN SSH SIGNATURE-----\n" +#define BEGIN_SIGNATURE "-----BEGIN SSH SIGNATURE-----" #define END_SIGNATURE "-----END SSH SIGNATURE-----" #define RSA_SIGN_ALG "rsa-sha2-512" /* XXX maybe make configurable */ #define RSA_SIGN_ALLOWED "rsa-sha2-512,rsa-sha2-256" #define HASHALG_DEFAULT "sha512" /* XXX maybe make configurable */ #define HASHALG_ALLOWED "sha256,sha512" int sshsig_armor(const struct sshbuf *blob, struct sshbuf **out) { struct sshbuf *buf = NULL; int r = SSH_ERR_INTERNAL_ERROR; *out = NULL; if ((buf = sshbuf_new()) == NULL) { error_f("sshbuf_new failed"); r = SSH_ERR_ALLOC_FAIL; goto out; } - if ((r = sshbuf_put(buf, BEGIN_SIGNATURE, - sizeof(BEGIN_SIGNATURE)-1)) != 0) { + if ((r = sshbuf_putf(buf, "%s\n", BEGIN_SIGNATURE)) != 0) { error_fr(r, "sshbuf_putf"); goto out; } if ((r = sshbuf_dtob64(blob, buf, 1)) != 0) { error_fr(r, "base64 encode signature"); goto out; } if ((r = sshbuf_put(buf, END_SIGNATURE, sizeof(END_SIGNATURE)-1)) != 0 || (r = sshbuf_put_u8(buf, '\n')) != 0) { error_fr(r, "sshbuf_put"); goto out; } /* success */ *out = buf; buf = NULL; /* transferred */ r = 0; out: sshbuf_free(buf); return r; } int sshsig_dearmor(struct sshbuf *sig, struct sshbuf **out) { int r; size_t eoffset = 0; struct sshbuf *buf = NULL; struct sshbuf *sbuf = NULL; char *b64 = NULL; if ((sbuf = sshbuf_fromb(sig)) == NULL) { error_f("sshbuf_fromb failed"); return SSH_ERR_ALLOC_FAIL; } + /* Expect and consume preamble + lf/crlf */ if ((r = sshbuf_cmp(sbuf, 0, BEGIN_SIGNATURE, sizeof(BEGIN_SIGNATURE)-1)) != 0) { error("Couldn't parse signature: missing header"); goto done; } - if ((r = sshbuf_consume(sbuf, sizeof(BEGIN_SIGNATURE)-1)) != 0) { error_fr(r, "consume"); goto done; } - + if ((r = sshbuf_cmp(sbuf, 0, "\r\n", 2)) == 0) + eoffset = 2; + else if ((r = sshbuf_cmp(sbuf, 0, "\n", 1)) == 0) + eoffset = 1; + else { + r = SSH_ERR_INVALID_FORMAT; + error_f("no header eol"); + goto done; + } + if ((r = sshbuf_consume(sbuf, eoffset)) != 0) { + error_fr(r, "consume eol"); + goto done; + } + /* Find and consume lf + suffix (any prior cr would be ignored) */ if ((r = sshbuf_find(sbuf, 0, "\n" END_SIGNATURE, - sizeof("\n" END_SIGNATURE)-1, &eoffset)) != 0) { + sizeof(END_SIGNATURE), &eoffset)) != 0) { error("Couldn't parse signature: missing footer"); goto done; } - if ((r = sshbuf_consume_end(sbuf, sshbuf_len(sbuf)-eoffset)) != 0) { error_fr(r, "consume"); goto done; } if ((b64 = sshbuf_dup_string(sbuf)) == NULL) { error_f("sshbuf_dup_string failed"); r = SSH_ERR_ALLOC_FAIL; goto done; } if ((buf = sshbuf_new()) == NULL) { error_f("sshbuf_new() failed"); r = SSH_ERR_ALLOC_FAIL; goto done; } if ((r = sshbuf_b64tod(buf, b64)) != 0) { error_fr(r, "decode base64"); goto done; } /* success */ *out = buf; r = 0; buf = NULL; /* transferred */ done: sshbuf_free(buf); sshbuf_free(sbuf); free(b64); return r; } static int sshsig_wrap_sign(struct sshkey *key, const char *hashalg, const char *sk_provider, const char *sk_pin, const struct sshbuf *h_message, const char *sig_namespace, struct sshbuf **out, sshsig_signer *signer, void *signer_ctx) { int r; size_t slen = 0; u_char *sig = NULL; struct sshbuf *blob = NULL; struct sshbuf *tosign = NULL; const char *sign_alg = NULL; if ((tosign = sshbuf_new()) == NULL || (blob = sshbuf_new()) == NULL) { error_f("sshbuf_new failed"); r = SSH_ERR_ALLOC_FAIL; goto done; } if ((r = sshbuf_put(tosign, MAGIC_PREAMBLE, MAGIC_PREAMBLE_LEN)) != 0 || (r = sshbuf_put_cstring(tosign, sig_namespace)) != 0 || (r = sshbuf_put_string(tosign, NULL, 0)) != 0 || /* reserved */ (r = sshbuf_put_cstring(tosign, hashalg)) != 0 || (r = sshbuf_put_stringb(tosign, h_message)) != 0) { error_fr(r, "assemble message to sign"); goto done; } /* If using RSA keys then default to a good signature algorithm */ if (sshkey_type_plain(key->type) == KEY_RSA) sign_alg = RSA_SIGN_ALG; if (signer != NULL) { if ((r = signer(key, &sig, &slen, sshbuf_ptr(tosign), sshbuf_len(tosign), sign_alg, sk_provider, sk_pin, 0, signer_ctx)) != 0) { error_r(r, "Couldn't sign message (signer)"); goto done; } } else { if ((r = sshkey_sign(key, &sig, &slen, sshbuf_ptr(tosign), sshbuf_len(tosign), sign_alg, sk_provider, sk_pin, 0)) != 0) { error_r(r, "Couldn't sign message"); goto done; } } if ((r = sshbuf_put(blob, MAGIC_PREAMBLE, MAGIC_PREAMBLE_LEN)) != 0 || (r = sshbuf_put_u32(blob, SIG_VERSION)) != 0 || (r = sshkey_puts(key, blob)) != 0 || (r = sshbuf_put_cstring(blob, sig_namespace)) != 0 || (r = sshbuf_put_string(blob, NULL, 0)) != 0 || /* reserved */ (r = sshbuf_put_cstring(blob, hashalg)) != 0 || (r = sshbuf_put_string(blob, sig, slen)) != 0) { error_fr(r, "assemble signature object"); goto done; } if (out != NULL) { *out = blob; blob = NULL; } r = 0; done: free(sig); sshbuf_free(blob); sshbuf_free(tosign); return r; } /* Check preamble and version. */ static int sshsig_parse_preamble(struct sshbuf *buf) { int r = SSH_ERR_INTERNAL_ERROR; uint32_t sversion; if ((r = sshbuf_cmp(buf, 0, MAGIC_PREAMBLE, MAGIC_PREAMBLE_LEN)) != 0 || (r = sshbuf_consume(buf, (sizeof(MAGIC_PREAMBLE)-1))) != 0 || (r = sshbuf_get_u32(buf, &sversion)) != 0) { error("Couldn't verify signature: invalid format"); return r; } if (sversion > SIG_VERSION) { error("Signature version %lu is larger than supported " "version %u", (unsigned long)sversion, SIG_VERSION); return SSH_ERR_INVALID_FORMAT; } return 0; } static int sshsig_check_hashalg(const char *hashalg) { if (hashalg == NULL || match_pattern_list(hashalg, HASHALG_ALLOWED, 0) == 1) return 0; error_f("unsupported hash algorithm \"%.100s\"", hashalg); return SSH_ERR_SIGN_ALG_UNSUPPORTED; } static int sshsig_peek_hashalg(struct sshbuf *signature, char **hashalgp) { struct sshbuf *buf = NULL; char *hashalg = NULL; int r = SSH_ERR_INTERNAL_ERROR; if (hashalgp != NULL) *hashalgp = NULL; if ((buf = sshbuf_fromb(signature)) == NULL) return SSH_ERR_ALLOC_FAIL; if ((r = sshsig_parse_preamble(buf)) != 0) goto done; if ((r = sshbuf_get_string_direct(buf, NULL, NULL)) != 0 || (r = sshbuf_get_string_direct(buf, NULL, NULL)) != 0 || (r = sshbuf_get_string(buf, NULL, NULL)) != 0 || (r = sshbuf_get_cstring(buf, &hashalg, NULL)) != 0 || (r = sshbuf_get_string_direct(buf, NULL, NULL)) != 0) { error_fr(r, "parse signature object"); goto done; } /* success */ r = 0; *hashalgp = hashalg; hashalg = NULL; done: free(hashalg); sshbuf_free(buf); return r; } static int sshsig_wrap_verify(struct sshbuf *signature, const char *hashalg, const struct sshbuf *h_message, const char *expect_namespace, struct sshkey **sign_keyp, struct sshkey_sig_details **sig_details) { int r = SSH_ERR_INTERNAL_ERROR; struct sshbuf *buf = NULL, *toverify = NULL; struct sshkey *key = NULL; const u_char *sig; char *got_namespace = NULL, *sigtype = NULL, *sig_hashalg = NULL; size_t siglen; debug_f("verify message length %zu", sshbuf_len(h_message)); if (sig_details != NULL) *sig_details = NULL; if (sign_keyp != NULL) *sign_keyp = NULL; if ((toverify = sshbuf_new()) == NULL) { error_f("sshbuf_new failed"); r = SSH_ERR_ALLOC_FAIL; goto done; } if ((r = sshbuf_put(toverify, MAGIC_PREAMBLE, MAGIC_PREAMBLE_LEN)) != 0 || (r = sshbuf_put_cstring(toverify, expect_namespace)) != 0 || (r = sshbuf_put_string(toverify, NULL, 0)) != 0 || /* reserved */ (r = sshbuf_put_cstring(toverify, hashalg)) != 0 || (r = sshbuf_put_stringb(toverify, h_message)) != 0) { error_fr(r, "assemble message to verify"); goto done; } if ((r = sshsig_parse_preamble(signature)) != 0) goto done; if ((r = sshkey_froms(signature, &key)) != 0 || (r = sshbuf_get_cstring(signature, &got_namespace, NULL)) != 0 || (r = sshbuf_get_string(signature, NULL, NULL)) != 0 || (r = sshbuf_get_cstring(signature, &sig_hashalg, NULL)) != 0 || (r = sshbuf_get_string_direct(signature, &sig, &siglen)) != 0) { error_fr(r, "parse signature object"); goto done; } if (sshbuf_len(signature) != 0) { error("Signature contains trailing data"); r = SSH_ERR_INVALID_FORMAT; goto done; } if (strcmp(expect_namespace, got_namespace) != 0) { error("Couldn't verify signature: namespace does not match"); debug_f("expected namespace \"%s\" received \"%s\"", expect_namespace, got_namespace); r = SSH_ERR_SIGNATURE_INVALID; goto done; } if (strcmp(hashalg, sig_hashalg) != 0) { error("Couldn't verify signature: hash algorithm mismatch"); debug_f("expected algorithm \"%s\" received \"%s\"", hashalg, sig_hashalg); r = SSH_ERR_SIGNATURE_INVALID; goto done; } /* Ensure that RSA keys use an acceptable signature algorithm */ if (sshkey_type_plain(key->type) == KEY_RSA) { if ((r = sshkey_get_sigtype(sig, siglen, &sigtype)) != 0) { error_r(r, "Couldn't verify signature: unable to get " "signature type"); goto done; } if (match_pattern_list(sigtype, RSA_SIGN_ALLOWED, 0) != 1) { error("Couldn't verify signature: unsupported RSA " "signature algorithm %s", sigtype); r = SSH_ERR_SIGN_ALG_UNSUPPORTED; goto done; } } if ((r = sshkey_verify(key, sig, siglen, sshbuf_ptr(toverify), sshbuf_len(toverify), NULL, 0, sig_details)) != 0) { error_r(r, "Signature verification failed"); goto done; } /* success */ r = 0; if (sign_keyp != NULL) { *sign_keyp = key; key = NULL; /* transferred */ } done: free(got_namespace); free(sigtype); free(sig_hashalg); sshbuf_free(buf); sshbuf_free(toverify); sshkey_free(key); return r; } static int hash_buffer(const struct sshbuf *m, const char *hashalg, struct sshbuf **bp) { char *hex, hash[SSH_DIGEST_MAX_LENGTH]; int alg, r = SSH_ERR_INTERNAL_ERROR; struct sshbuf *b = NULL; *bp = NULL; memset(hash, 0, sizeof(hash)); if ((r = sshsig_check_hashalg(hashalg)) != 0) return r; if ((alg = ssh_digest_alg_by_name(hashalg)) == -1) { error_f("can't look up hash algorithm %s", hashalg); return SSH_ERR_INTERNAL_ERROR; } if ((r = ssh_digest_buffer(alg, m, hash, sizeof(hash))) != 0) { error_fr(r, "ssh_digest_buffer"); return r; } if ((hex = tohex(hash, ssh_digest_bytes(alg))) != NULL) { debug3_f("final hash: %s", hex); freezero(hex, strlen(hex)); } if ((b = sshbuf_new()) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } if ((r = sshbuf_put(b, hash, ssh_digest_bytes(alg))) != 0) { error_fr(r, "sshbuf_put"); goto out; } *bp = b; b = NULL; /* transferred */ /* success */ r = 0; out: sshbuf_free(b); explicit_bzero(hash, sizeof(hash)); return r; } int sshsig_signb(struct sshkey *key, const char *hashalg, const char *sk_provider, const char *sk_pin, const struct sshbuf *message, const char *sig_namespace, struct sshbuf **out, sshsig_signer *signer, void *signer_ctx) { struct sshbuf *b = NULL; int r = SSH_ERR_INTERNAL_ERROR; if (hashalg == NULL) hashalg = HASHALG_DEFAULT; if (out != NULL) *out = NULL; if ((r = hash_buffer(message, hashalg, &b)) != 0) { error_fr(r, "hash buffer"); goto out; } if ((r = sshsig_wrap_sign(key, hashalg, sk_provider, sk_pin, b, sig_namespace, out, signer, signer_ctx)) != 0) goto out; /* success */ r = 0; out: sshbuf_free(b); return r; } int sshsig_verifyb(struct sshbuf *signature, const struct sshbuf *message, const char *expect_namespace, struct sshkey **sign_keyp, struct sshkey_sig_details **sig_details) { struct sshbuf *b = NULL; int r = SSH_ERR_INTERNAL_ERROR; char *hashalg = NULL; if (sig_details != NULL) *sig_details = NULL; if (sign_keyp != NULL) *sign_keyp = NULL; if ((r = sshsig_peek_hashalg(signature, &hashalg)) != 0) return r; debug_f("signature made with hash \"%s\"", hashalg); if ((r = hash_buffer(message, hashalg, &b)) != 0) { error_fr(r, "hash buffer"); goto out; } if ((r = sshsig_wrap_verify(signature, hashalg, b, expect_namespace, sign_keyp, sig_details)) != 0) goto out; /* success */ r = 0; out: sshbuf_free(b); free(hashalg); return r; } static int hash_file(int fd, const char *hashalg, struct sshbuf **bp) { char *hex, rbuf[8192], hash[SSH_DIGEST_MAX_LENGTH]; ssize_t n, total = 0; struct ssh_digest_ctx *ctx = NULL; int alg, oerrno, r = SSH_ERR_INTERNAL_ERROR; struct sshbuf *b = NULL; *bp = NULL; memset(hash, 0, sizeof(hash)); if ((r = sshsig_check_hashalg(hashalg)) != 0) return r; if ((alg = ssh_digest_alg_by_name(hashalg)) == -1) { error_f("can't look up hash algorithm %s", hashalg); return SSH_ERR_INTERNAL_ERROR; } if ((ctx = ssh_digest_start(alg)) == NULL) { error_f("ssh_digest_start failed"); return SSH_ERR_INTERNAL_ERROR; } for (;;) { if ((n = read(fd, rbuf, sizeof(rbuf))) == -1) { if (errno == EINTR || errno == EAGAIN) continue; oerrno = errno; error_f("read: %s", strerror(errno)); errno = oerrno; r = SSH_ERR_SYSTEM_ERROR; goto out; } else if (n == 0) { debug2_f("hashed %zu bytes", total); break; /* EOF */ } total += (size_t)n; if ((r = ssh_digest_update(ctx, rbuf, (size_t)n)) != 0) { error_fr(r, "ssh_digest_update"); goto out; } } if ((r = ssh_digest_final(ctx, hash, sizeof(hash))) != 0) { error_fr(r, "ssh_digest_final"); goto out; } if ((hex = tohex(hash, ssh_digest_bytes(alg))) != NULL) { debug3_f("final hash: %s", hex); freezero(hex, strlen(hex)); } if ((b = sshbuf_new()) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } if ((r = sshbuf_put(b, hash, ssh_digest_bytes(alg))) != 0) { error_fr(r, "sshbuf_put"); goto out; } *bp = b; b = NULL; /* transferred */ /* success */ r = 0; out: oerrno = errno; sshbuf_free(b); ssh_digest_free(ctx); explicit_bzero(hash, sizeof(hash)); errno = oerrno; return r; } int sshsig_sign_fd(struct sshkey *key, const char *hashalg, const char *sk_provider, const char *sk_pin, int fd, const char *sig_namespace, struct sshbuf **out, sshsig_signer *signer, void *signer_ctx) { struct sshbuf *b = NULL; int r = SSH_ERR_INTERNAL_ERROR; if (hashalg == NULL) hashalg = HASHALG_DEFAULT; if (out != NULL) *out = NULL; if ((r = hash_file(fd, hashalg, &b)) != 0) { error_fr(r, "hash_file"); return r; } if ((r = sshsig_wrap_sign(key, hashalg, sk_provider, sk_pin, b, sig_namespace, out, signer, signer_ctx)) != 0) goto out; /* success */ r = 0; out: sshbuf_free(b); return r; } int sshsig_verify_fd(struct sshbuf *signature, int fd, const char *expect_namespace, struct sshkey **sign_keyp, struct sshkey_sig_details **sig_details) { struct sshbuf *b = NULL; int r = SSH_ERR_INTERNAL_ERROR; char *hashalg = NULL; if (sig_details != NULL) *sig_details = NULL; if (sign_keyp != NULL) *sign_keyp = NULL; if ((r = sshsig_peek_hashalg(signature, &hashalg)) != 0) return r; debug_f("signature made with hash \"%s\"", hashalg); if ((r = hash_file(fd, hashalg, &b)) != 0) { error_fr(r, "hash_file"); goto out; } if ((r = sshsig_wrap_verify(signature, hashalg, b, expect_namespace, sign_keyp, sig_details)) != 0) goto out; /* success */ r = 0; out: sshbuf_free(b); free(hashalg); return r; } struct sshsigopt { int ca; char *namespaces; uint64_t valid_after, valid_before; }; struct sshsigopt * sshsigopt_parse(const char *opts, const char *path, u_long linenum, const char **errstrp) { struct sshsigopt *ret; int r; char *opt; const char *errstr = NULL; if ((ret = calloc(1, sizeof(*ret))) == NULL) return NULL; if (opts == NULL || *opts == '\0') return ret; /* Empty options yields empty options :) */ while (*opts && *opts != ' ' && *opts != '\t') { /* flag options */ if ((r = opt_flag("cert-authority", 0, &opts)) != -1) { ret->ca = 1; } else if (opt_match(&opts, "namespaces")) { if (ret->namespaces != NULL) { errstr = "multiple \"namespaces\" clauses"; goto fail; } ret->namespaces = opt_dequote(&opts, &errstr); if (ret->namespaces == NULL) goto fail; } else if (opt_match(&opts, "valid-after")) { if (ret->valid_after != 0) { errstr = "multiple \"valid-after\" clauses"; goto fail; } if ((opt = opt_dequote(&opts, &errstr)) == NULL) goto fail; if (parse_absolute_time(opt, &ret->valid_after) != 0 || ret->valid_after == 0) { free(opt); errstr = "invalid \"valid-after\" time"; goto fail; } free(opt); } else if (opt_match(&opts, "valid-before")) { if (ret->valid_before != 0) { errstr = "multiple \"valid-before\" clauses"; goto fail; } if ((opt = opt_dequote(&opts, &errstr)) == NULL) goto fail; if (parse_absolute_time(opt, &ret->valid_before) != 0 || ret->valid_before == 0) { free(opt); errstr = "invalid \"valid-before\" time"; goto fail; } free(opt); } /* * Skip the comma, and move to the next option * (or break out if there are no more). */ if (*opts == '\0' || *opts == ' ' || *opts == '\t') break; /* End of options. */ /* Anything other than a comma is an unknown option */ if (*opts != ',') { errstr = "unknown key option"; goto fail; } opts++; if (*opts == '\0') { errstr = "unexpected end-of-options"; goto fail; } } /* final consistency check */ if (ret->valid_after != 0 && ret->valid_before != 0 && ret->valid_before <= ret->valid_after) { errstr = "\"valid-before\" time is before \"valid-after\""; goto fail; } /* success */ return ret; fail: if (errstrp != NULL) *errstrp = errstr; sshsigopt_free(ret); return NULL; } void sshsigopt_free(struct sshsigopt *opts) { if (opts == NULL) return; free(opts->namespaces); free(opts); } static int parse_principals_key_and_options(const char *path, u_long linenum, char *line, const char *required_principal, char **principalsp, struct sshkey **keyp, struct sshsigopt **sigoptsp) { char *opts = NULL, *tmp, *cp, *principals = NULL; const char *reason = NULL; struct sshsigopt *sigopts = NULL; struct sshkey *key = NULL; int r = SSH_ERR_INTERNAL_ERROR; if (principalsp != NULL) *principalsp = NULL; if (sigoptsp != NULL) *sigoptsp = NULL; if (keyp != NULL) *keyp = NULL; cp = line; cp = cp + strspn(cp, " \t"); /* skip leading whitespace */ if (*cp == '#' || *cp == '\0') return SSH_ERR_KEY_NOT_FOUND; /* blank or all-comment line */ /* format: identity[,identity...] [option[,option...]] key */ if ((tmp = strdelimw(&cp)) == NULL || cp == NULL) { error("%s:%lu: invalid line", path, linenum); r = SSH_ERR_INVALID_FORMAT; goto out; } if ((principals = strdup(tmp)) == NULL) { error_f("strdup failed"); r = SSH_ERR_ALLOC_FAIL; goto out; } /* * Bail out early if we're looking for a particular principal and this * line does not list it. */ if (required_principal != NULL) { if (match_pattern_list(required_principal, principals, 0) != 1) { /* principal didn't match */ r = SSH_ERR_KEY_NOT_FOUND; goto out; } debug_f("%s:%lu: matched principal \"%s\"", path, linenum, required_principal); } if ((key = sshkey_new(KEY_UNSPEC)) == NULL) { error_f("sshkey_new failed"); r = SSH_ERR_ALLOC_FAIL; goto out; } if (sshkey_read(key, &cp) != 0) { /* no key? Check for options */ opts = cp; if (sshkey_advance_past_options(&cp) != 0) { error("%s:%lu: invalid options", path, linenum); r = SSH_ERR_INVALID_FORMAT; goto out; } if (cp == NULL || *cp == '\0') { error("%s:%lu: missing key", path, linenum); r = SSH_ERR_INVALID_FORMAT; goto out; } *cp++ = '\0'; skip_space(&cp); if (sshkey_read(key, &cp) != 0) { error("%s:%lu: invalid key", path, linenum); r = SSH_ERR_INVALID_FORMAT; goto out; } } debug3("%s:%lu: options %s", path, linenum, opts == NULL ? "" : opts); if ((sigopts = sshsigopt_parse(opts, path, linenum, &reason)) == NULL) { error("%s:%lu: bad options: %s", path, linenum, reason); r = SSH_ERR_INVALID_FORMAT; goto out; } /* success */ if (principalsp != NULL) { *principalsp = principals; principals = NULL; /* transferred */ } if (sigoptsp != NULL) { *sigoptsp = sigopts; sigopts = NULL; /* transferred */ } if (keyp != NULL) { *keyp = key; key = NULL; /* transferred */ } r = 0; out: free(principals); sshsigopt_free(sigopts); sshkey_free(key); return r; } static int cert_filter_principals(const char *path, u_long linenum, char **principalsp, const struct sshkey *cert, uint64_t verify_time) { char *cp, *oprincipals, *principals; const char *reason; struct sshbuf *nprincipals; int r = SSH_ERR_INTERNAL_ERROR, success = 0; u_int i; oprincipals = principals = *principalsp; *principalsp = NULL; if ((nprincipals = sshbuf_new()) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } while ((cp = strsep(&principals, ",")) != NULL && *cp != '\0') { /* Check certificate validity */ if ((r = sshkey_cert_check_authority(cert, 0, 1, 0, verify_time, NULL, &reason)) != 0) { debug("%s:%lu: principal \"%s\" not authorized: %s", path, linenum, cp, reason); continue; } /* Return all matching principal names from the cert */ for (i = 0; i < cert->cert->nprincipals; i++) { if (match_pattern(cert->cert->principals[i], cp)) { if ((r = sshbuf_putf(nprincipals, "%s%s", sshbuf_len(nprincipals) != 0 ? "," : "", cert->cert->principals[i])) != 0) { error_f("buffer error"); goto out; } } } } if (sshbuf_len(nprincipals) == 0) { error("%s:%lu: no valid principals found", path, linenum); r = SSH_ERR_KEY_CERT_INVALID; goto out; } if ((principals = sshbuf_dup_string(nprincipals)) == NULL) { error_f("buffer error"); goto out; } /* success */ success = 1; *principalsp = principals; out: sshbuf_free(nprincipals); free(oprincipals); return success ? 0 : r; } static int check_allowed_keys_line(const char *path, u_long linenum, char *line, const struct sshkey *sign_key, const char *principal, const char *sig_namespace, uint64_t verify_time, char **principalsp) { struct sshkey *found_key = NULL; char *principals = NULL; int r, success = 0; const char *reason = NULL; struct sshsigopt *sigopts = NULL; char tvalid[64], tverify[64]; if (principalsp != NULL) *principalsp = NULL; /* Parse the line */ if ((r = parse_principals_key_and_options(path, linenum, line, principal, &principals, &found_key, &sigopts)) != 0) { /* error already logged */ goto done; } if (!sigopts->ca && sshkey_equal(found_key, sign_key)) { /* Exact match of key */ debug("%s:%lu: matched key", path, linenum); } else if (sigopts->ca && sshkey_is_cert(sign_key) && sshkey_equal_public(sign_key->cert->signature_key, found_key)) { if (principal) { /* Match certificate CA key with specified principal */ if ((r = sshkey_cert_check_authority(sign_key, 0, 1, 0, verify_time, principal, &reason)) != 0) { error("%s:%lu: certificate not authorized: %s", path, linenum, reason); goto done; } debug("%s:%lu: matched certificate CA key", path, linenum); } else { /* No principal specified - find all matching ones */ if ((r = cert_filter_principals(path, linenum, &principals, sign_key, verify_time)) != 0) { /* error already displayed */ debug_r(r, "%s:%lu: cert_filter_principals", path, linenum); goto done; } debug("%s:%lu: matched certificate CA key", path, linenum); } } else { /* Didn't match key */ goto done; } /* Check whether options preclude the use of this key */ if (sigopts->namespaces != NULL && sig_namespace != NULL && match_pattern_list(sig_namespace, sigopts->namespaces, 0) != 1) { error("%s:%lu: key is not permitted for use in signature " "namespace \"%s\"", path, linenum, sig_namespace); goto done; } /* check key time validity */ format_absolute_time((uint64_t)verify_time, tverify, sizeof(tverify)); if (sigopts->valid_after != 0 && (uint64_t)verify_time < sigopts->valid_after) { format_absolute_time(sigopts->valid_after, tvalid, sizeof(tvalid)); error("%s:%lu: key is not yet valid: " "verify time %s < valid-after %s", path, linenum, tverify, tvalid); goto done; } if (sigopts->valid_before != 0 && (uint64_t)verify_time > sigopts->valid_before) { format_absolute_time(sigopts->valid_before, tvalid, sizeof(tvalid)); error("%s:%lu: key has expired: " "verify time %s > valid-before %s", path, linenum, tverify, tvalid); goto done; } success = 1; done: if (success && principalsp != NULL) { *principalsp = principals; principals = NULL; /* transferred */ } free(principals); sshkey_free(found_key); sshsigopt_free(sigopts); return success ? 0 : SSH_ERR_KEY_NOT_FOUND; } int sshsig_check_allowed_keys(const char *path, const struct sshkey *sign_key, const char *principal, const char *sig_namespace, uint64_t verify_time) { FILE *f = NULL; char *line = NULL; size_t linesize = 0; u_long linenum = 0; int r = SSH_ERR_KEY_NOT_FOUND, oerrno; /* Check key and principal against file */ if ((f = fopen(path, "r")) == NULL) { oerrno = errno; error("Unable to open allowed keys file \"%s\": %s", path, strerror(errno)); errno = oerrno; return SSH_ERR_SYSTEM_ERROR; } while (getline(&line, &linesize, f) != -1) { linenum++; r = check_allowed_keys_line(path, linenum, line, sign_key, principal, sig_namespace, verify_time, NULL); free(line); line = NULL; linesize = 0; if (r == SSH_ERR_KEY_NOT_FOUND) continue; else if (r == 0) { /* success */ fclose(f); return 0; } else break; } /* Either we hit an error parsing or we simply didn't find the key */ fclose(f); free(line); return r; } int sshsig_find_principals(const char *path, const struct sshkey *sign_key, uint64_t verify_time, char **principals) { FILE *f = NULL; char *line = NULL; size_t linesize = 0; u_long linenum = 0; int r = SSH_ERR_KEY_NOT_FOUND, oerrno; if ((f = fopen(path, "r")) == NULL) { oerrno = errno; error("Unable to open allowed keys file \"%s\": %s", path, strerror(errno)); errno = oerrno; return SSH_ERR_SYSTEM_ERROR; } while (getline(&line, &linesize, f) != -1) { linenum++; r = check_allowed_keys_line(path, linenum, line, sign_key, NULL, NULL, verify_time, principals); free(line); line = NULL; linesize = 0; if (r == SSH_ERR_KEY_NOT_FOUND) continue; else if (r == 0) { /* success */ fclose(f); return 0; } else break; } free(line); /* Either we hit an error parsing or we simply didn't find the key */ if (ferror(f) != 0) { oerrno = errno; fclose(f); error("Unable to read allowed keys file \"%s\": %s", path, strerror(errno)); errno = oerrno; return SSH_ERR_SYSTEM_ERROR; } fclose(f); return r; } int sshsig_match_principals(const char *path, const char *principal, char ***principalsp, size_t *nprincipalsp) { FILE *f = NULL; char *found, *line = NULL, **principals = NULL, **tmp; size_t i, nprincipals = 0, linesize = 0; u_long linenum = 0; int oerrno = 0, r, ret = 0; if (principalsp != NULL) *principalsp = NULL; if (nprincipalsp != NULL) *nprincipalsp = 0; /* Check key and principal against file */ if ((f = fopen(path, "r")) == NULL) { oerrno = errno; error("Unable to open allowed keys file \"%s\": %s", path, strerror(errno)); errno = oerrno; return SSH_ERR_SYSTEM_ERROR; } while (getline(&line, &linesize, f) != -1) { linenum++; /* Parse the line */ if ((r = parse_principals_key_and_options(path, linenum, line, principal, &found, NULL, NULL)) != 0) { if (r == SSH_ERR_KEY_NOT_FOUND) continue; ret = r; oerrno = errno; break; /* unexpected error */ } if ((tmp = recallocarray(principals, nprincipals, nprincipals + 1, sizeof(*principals))) == NULL) { ret = SSH_ERR_ALLOC_FAIL; free(found); break; } principals = tmp; principals[nprincipals++] = found; /* transferred */ free(line); line = NULL; linesize = 0; } fclose(f); if (ret == 0) { if (nprincipals == 0) ret = SSH_ERR_KEY_NOT_FOUND; if (principalsp != NULL) { *principalsp = principals; principals = NULL; /* transferred */ } if (nprincipalsp != 0) { *nprincipalsp = nprincipals; nprincipals = 0; } } for (i = 0; i < nprincipals; i++) free(principals[i]); free(principals); errno = oerrno; return ret; } int sshsig_get_pubkey(struct sshbuf *signature, struct sshkey **pubkey) { struct sshkey *pk = NULL; int r = SSH_ERR_SIGNATURE_INVALID; if (pubkey == NULL) return SSH_ERR_INTERNAL_ERROR; if ((r = sshsig_parse_preamble(signature)) != 0) return r; if ((r = sshkey_froms(signature, &pk)) != 0) return r; *pubkey = pk; pk = NULL; return 0; } diff --git a/version.h b/version.h index e5b1e719d4d6..1363d4706812 100644 --- a/version.h +++ b/version.h @@ -1,6 +1,6 @@ -/* $OpenBSD: version.h,v 1.98 2023/08/10 01:01:07 djm Exp $ */ +/* $OpenBSD: version.h,v 1.99 2023/10/04 04:04:09 djm Exp $ */ -#define SSH_VERSION "OpenSSH_9.4" +#define SSH_VERSION "OpenSSH_9.5" #define SSH_PORTABLE "p1" #define SSH_RELEASE SSH_VERSION SSH_PORTABLE