diff --git a/.gitignore b/.gitignore index 149e338..7f20fba 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ /*.tar.bz2 /*.tar.gz /*.tar.gz.asc -/DJM-GPG-KEY.gpg +/*.gpg diff --git a/gsi-openssh.spec b/gsi-openssh.spec index c76558b..60183ef 100644 --- a/gsi-openssh.spec +++ b/gsi-openssh.spec @@ -30,8 +30,8 @@ # Do we want LDAP support %global ldap 1 -%global openssh_ver 7.8p1 -%global openssh_rel 1 +%global openssh_ver 8.0p1 +%global openssh_rel 16 Summary: An implementation of the SSH protocol with GSI authentication Name: gsi-openssh @@ -56,11 +56,6 @@ Source99: README.sshd-and-gsisshd #https://bugzilla.mindrot.org/show_bug.cgi?id=2581 Patch100: openssh-6.7p1-coverity.patch -#https://bugzilla.mindrot.org/show_bug.cgi?id=1894 -#https://bugzilla.redhat.com/show_bug.cgi?id=735889 -#Patch102: openssh-5.8p1-getaddrinfo.patch -# OpenSSL 1.1.0 compatibility -Patch104: openssh-7.3p1-openssl-1.1.0.patch #https://bugzilla.mindrot.org/show_bug.cgi?id=1402 # https://bugzilla.redhat.com/show_bug.cgi?id=1171248 @@ -98,8 +93,6 @@ Patch702: openssh-5.1p1-askpass-progress.patch Patch703: openssh-4.3p2-askpass-grab-info.patch #https://bugzilla.mindrot.org/show_bug.cgi?id=1635 (WONTFIX) Patch707: openssh-7.7p1-redhat.patch -#https://bugzilla.mindrot.org/show_bug.cgi?id=1640 (WONTFIX) -Patch709: openssh-6.2p1-vendor.patch # warn users for unsupported UsePAM=no (#757545) Patch711: openssh-7.8p1-UsePAM-warning.patch # make aes-ctr ciphers use EVP engines such as AES-NI from OpenSSL @@ -109,26 +102,20 @@ Patch713: openssh-6.6p1-ctr-cavstest.patch # add SSH KDF CAVS test driver Patch714: openssh-6.7p1-kdf-cavs.patch -#http://www.sxw.org.uk/computing/patches/openssh.html -#changed cache storage type - #848228 -Patch800: openssh-7.8p1-gsskex.patch +# GSSAPI Key Exchange (RFC 4462 + draft-ietf-curdle-gss-keyex-sha2-08) +# from https://github.com/openssh-gsskex/openssh-gsskex/tree/fedora/master +Patch800: openssh-8.0p1-gssapi-keyex.patch #http://www.mail-archive.com/kerberos@mit.edu/msg17591.html Patch801: openssh-6.6p1-force_krb.patch # add new option GSSAPIEnablek5users and disable using ~/.k5users by default (#1169843) # CVE-2014-9278 Patch802: openssh-6.6p1-GSSAPIEnablek5users.patch -# Documentation about GSSAPI -# from https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=765655 -Patch803: openssh-7.1p1-gssapi-documentation.patch # Improve ccache handling in openssh (#991186, #1199363, #1566494) # https://bugzilla.mindrot.org/show_bug.cgi?id=2775 Patch804: openssh-7.7p1-gssapi-new-unique.patch # Respect k5login_directory option in krk5.conf (#1328243) Patch805: openssh-7.2p2-k5login_directory.patch -# Support SHA2 in GSS key exchanges from draft-ssorce-gss-keyex-sha2-02 -Patch807: openssh-7.5p1-gssapi-kex-with-ec.patch -Patch900: openssh-6.1p1-gssapi-canohost.patch #https://bugzilla.mindrot.org/show_bug.cgi?id=1780 Patch901: openssh-6.6p1-kuserok.patch # Use tty allocation for a remote scp (#985650) @@ -139,16 +126,12 @@ Patch916: openssh-6.6.1p1-selinux-contexts.patch Patch918: openssh-6.6.1p1-log-in-chroot.patch # scp file into non-existing directory (#1142223) Patch919: openssh-6.6.1p1-scp-non-existing-directory.patch -# Config parser shouldn't accept ip/port syntax (#1130733) -Patch920: openssh-7.8p1-ip-port-config-parser.patch # apply upstream patch and make sshd -T more consistent (#1187521) Patch922: openssh-6.8p1-sshdT-output.patch # Add sftp option to force mode of created files (#1191055) Patch926: openssh-6.7p1-sftp-force-permission.patch # Restore compatible default (#89216) Patch929: openssh-6.9p1-permit-root-login.patch -# Add GSSAPIKexAlgorithms option for server and client application -Patch932: openssh-7.0p1-gssKexAlgorithms.patch # make s390 use /dev/ crypto devices -- ignore closefrom Patch939: openssh-7.2p2-s390-closefrom.patch # Move MAX_DISPLAYS to a configuration option (#1341302) @@ -160,15 +143,110 @@ Patch949: openssh-7.6p1-cleanup-selinux.patch # Sandbox adjustments for s390 and audit Patch950: openssh-7.5p1-sandbox.patch # PKCS#11 URIs (upstream #2817, 2nd iteration) -Patch951: openssh-7.6p1-pkcs11-uri.patch -# PKCS#11 ECDSA keys (upstream #2474, 8th iteration) -Patch952: openssh-7.6p1-pkcs11-ecdsa.patch +Patch951: openssh-8.0p1-pkcs11-uri.patch # Unbreak scp between two IPv6 hosts (#1620333) Patch953: openssh-7.8p1-scp-ipv6.patch +# ssh-copy-id is unmaintained: Aggreagete patches +# - do not return 0 if the write fails (full disk) +# - shellcheck reports (upstream #2902) +Patch958: openssh-7.9p1-ssh-copy-id.patch +# Verify the SCP vulnerabilities are fixed in the package testsuite +# https://bugzilla.mindrot.org/show_bug.cgi?id=3007 +Patch961: openssh-8.0p1-scp-tests.patch +# Mention crypto-policies in manual pages (#1668325) +Patch962: openssh-8.0p1-crypto-policies.patch +# Use OpenSSL high-level API to produce and verify signatures (#1707485) +Patch963: openssh-8.0p1-openssl-evp.patch +# Use OpenSSL KDF (#1631761) +Patch964: openssh-8.0p1-openssl-kdf.patch +# Use new OpenSSL for PEM export to avoid MD5 dependency (#1712436) +Patch965: openssh-8.0p1-openssl-pem.patch +# Seed from dev/random if requested (#1785655) +Patch966: openssh-8.0p1-entropy.patch +# Unbreak ssh-keyscan RSA keys without SHA1 (#1744108) +Patch967: openssh-8.0p1-keyscan-rsa-sha2.patch +# Detect proxyJump loops in configuration files (#1804099) +Patch968: openssh-8.0p1-proxyjump-loops.patch +# ssh-keygen should default to SHA2-based signature algorithm (#1790610) +Patch969: openssh-8.0p1-keygen-sha2.patch +# RDomain is not suported on non-OpenBSD (#1807686) +# https://bugzilla.mindrot.org/show_bug.cgi?id=3126 +Patch970: openssh-8.0p1-rdomain.patch +# Do not fail X11 forwarding if IPv6 is disabled (#1662189) +# https://bugzilla.mindrot.org/show_bug.cgi?id=2143 +Patch971: openssh-8.0p1-x11-without-ipv6.patch +# Client window fix (#1913041) +Patch972: openssh-8.0p1-channel-limits.patch +# SFTP sort upon the modification time (#1909988) +# https://bugzilla.mindrot.org/show_bug.cgi?id=3248 +Patch973: openssh-8.0p1-sftp-timespeccmp.patch +# ssh-keygen printing fingerprint issue with Windows keys (#1901518) +Patch974: openssh-8.0p1-keygen-strip-doseol.patch +# sshd provides PAM an incorrect error code (#1879503) +Patch975: openssh-8.0p1-preserve-pam-errors.patch +# ssh incorrectly restores the blocking mode on standard output (#1942901) +Patch976: openssh-8.0p1-restore-nonblock.patch +# CVE 2020-14145 +Patch977: openssh-8.0p1-cve-2020-14145.patch +# sshd -T requires -C when "Match" is used in sshd_config (#1836277) +Patch978: openssh-8.0p1-sshd_config.patch +# CVE-2021-41617 +Patch980: openssh-8.7p1-upstream-cve-2021-41617.patch +# support sshd Include directive +# upstream commits: +# c2bd7f74b0e0f3a3ee9d19ac549e6ba89013abaf~1..677d0ece67634262b3b96c3cd6410b19f3a603b7 +# 8bdc3bb7cf4c82c3344cfcb82495a43406e87e83 +# 47adfdc07f4f8ea0064a1495500244de08d311ed~1..7af1e92cd289b7eaa9a683e9a6f2fddd98f37a01 +# supplementary commit 612b1dd1ec91ffb1e01f58cca0c6eb1d47bf4423 +Patch981: openssh-8.0p1-sshd_include.patch +# Port upstream ClientAliveCountMax behaviour +# upstream commit: +# 69334996ae203c51c70bf01d414c918a44618f8e +Patch982: openssh-8.0p1-client_alive_count_max.patch +# add a local implementation of BSD realpath() for sftp-server +# use ahead of OpenBSD's realpath changing to match POSIX +# upstream commits: +# 569b650f93b561c09c655f83f128e1dfffe74101 +# 53a6ebf1445a857f5e487b18ee5e5830a9575149 +# 5428b0d239f6b516c81d1dd15aa9fe9e60af75d4 +Patch983: openssh-8.0p1-sftp-realpath.patch +# include caveat for crypto-policy in sshd manpage (#2044354) +Patch984: openssh-8.0p1-crypto-policy-doc.patch +# minimize the use of SHA1 as a proof of possession for RSA key (#2093897) +# upstream commits: +# 291721bc7c840d113a49518f3fca70e86248b8e8 +# 0fa33683223c76289470a954404047bc762be84c +# f8df0413f0a057b6a3d3dd7bd8bc7c5d80911d3a +Patch985: openssh-8.7p1-minimize-sha1-use.patch +# Upstream ff89b1bed80721295555bd083b173247a9c0484e +Patch986: openssh-9.1p1-sshbanner.patch +# Upstream 25e3bccbaa63d27b9d5e09c123f1eb28594d2bd6 +Patch987: openssh-8.0p1-ipv6-process.patch +# Upstream 4332b4fe49360679647a8705bc08f4e81323f6b4 +Patch988: openssh-8.0p1-avoidkillall.patch +# Upstream 89b54900ac61986760452f132bbe3fb7249cfdac +Patch989: openssh-8.0p1-bigsshdconfig.patch +# upsream commit +# b23fe83f06ee7e721033769cfa03ae840476d280 +Patch1015: openssh-9.3p1-upstream-cve-2023-38408.patch +#upstream commit 01dbf3d46651b7d6ddf5e45d233839bbfffaeaec +Patch1017: openssh-9.4p2-limit-delay.patch +#upstream commit 1edb00c58f8a6875fad6a497aa2bacf37f9e6cd5 +Patch1018: openssh-9.6p1-CVE-2023-48795.patch +#upstream commit 7ef3787c84b6b524501211b11a26c742f829af1a +Patch1019: openssh-9.6p1-CVE-2023-51385.patch +# SCP kill switch +Patch1020: openssh-8.7p1-scp-kill-switch.patch +#upstream commit 96faa0de6c673a2ce84736eba37fc9fb723d9e5c +Patch1021: openssh-8.0p1-upstream-ignore-SIGPIPE.patch # This is the patch that adds GSI support # Based on hpn_isshd-gsi.7.5p1b.patch from Globus upstream -Patch98: openssh-7.8p1-gsissh.patch +Patch98: openssh-8.0p1-gsissh.patch + +# This is the HPN patch +# Based on https://github.com/rapier1/hpn-ssh/ tag: hpn-8_0_P1 (hpn14v19) +Patch99: openssh-8.0p1-hpn-14.19.patch License: BSD Group: Applications/Internet @@ -182,7 +260,6 @@ BuildRequires: autoconf, automake, perl-interpreter, perl-generators, zlib-devel BuildRequires: audit-libs-devel >= 2.0.5 BuildRequires: util-linux, groff BuildRequires: pam-devel -BuildRequires: fipscheck-devel >= 1.3.0 BuildRequires: openssl-devel >= 0.9.8j BuildRequires: systemd-devel BuildRequires: gcc @@ -197,7 +274,6 @@ BuildRequires: krb5-devel BuildRequires: globus-gss-assist-devel >= 8 BuildRequires: globus-gssapi-gsi-devel >= 12.12 BuildRequires: globus-common-devel >= 14 -BuildRequires: globus-usage-devel >= 3 %endif %if %{libedit} @@ -221,7 +297,6 @@ Provides: gsissh-clients = %{version}-%{release} Obsoletes: gsissh-clients < 5.8p2-2 Group: Applications/Internet Requires: %{name} = %{version}-%{release} -Requires: fipscheck-lib%{_isa} >= 1.3.0 Requires: crypto-policies >= 20180306-1 %package server @@ -232,7 +307,6 @@ Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} Requires(pre): /usr/sbin/useradd Requires: pam >= 1.0.1-3 -Requires: fipscheck-lib%{_isa} >= 1.3.0 Requires: crypto-policies >= 20180306-1 %{?systemd_requires} @@ -271,8 +345,6 @@ This version of OpenSSH has been modified to support GSI authentication. gpgv2 --quiet --keyring %{SOURCE3} %{SOURCE1} %{SOURCE0} %setup -q -n openssh-%{version} -# investigate %%patch102 -p1 -b .getaddrinfo - %patch400 -p1 -b .role-mls %patch404 -p1 -b .privsep-selinux @@ -290,7 +362,6 @@ gpgv2 --quiet --keyring %{SOURCE3} %{SOURCE1} %{SOURCE0} %patch702 -p1 -b .progress %patch703 -p1 -b .grab-info %patch707 -p1 -b .redhat -%patch709 -p1 -b .vendor %patch711 -p1 -b .log-usepam-no %patch712 -p1 -b .evp-ctr %patch713 -p1 -b .ctr-cavs @@ -298,44 +369,74 @@ gpgv2 --quiet --keyring %{SOURCE3} %{SOURCE1} %{SOURCE0} %patch800 -p1 -b .gsskex %patch801 -p1 -b .force_krb -%patch803 -p1 -b .gss-docs %patch804 -p1 -b .ccache_name %patch805 -p1 -b .k5login -%patch900 -p1 -b .canohost %patch901 -p1 -b .kuserok %patch906 -p1 -b .fromto-remote %patch916 -p1 -b .contexts %patch918 -p1 -b .log-in-chroot %patch919 -p1 -b .scp -%patch920 -p1 -b .config %patch802 -p1 -b .GSSAPIEnablek5users %patch922 -p1 -b .sshdt %patch926 -p1 -b .sftp-force-mode %patch929 -p1 -b .root-login -%patch932 -p1 -b .gsskexalg %patch939 -p1 -b .s390-dev %patch944 -p1 -b .x11max %patch948 -p1 -b .systemd -%patch807 -p1 -b .gsskex-ec %patch949 -p1 -b .refactor %patch950 -p1 -b .sandbox %patch951 -p1 -b .pkcs11-uri -%patch952 -p1 -b .pkcs11-ecdsa %patch953 -p1 -b .scp-ipv6 +%patch958 -p1 -b .ssh-copy-id +%patch961 -p1 -b .scp-tests +%patch962 -p1 -b .crypto-policies +%patch963 -p1 -b .openssl-evp +%patch964 -p1 -b .openssl-kdf +%patch965 -p1 -b .openssl-pem +%patch966 -p1 -b .entropy +%patch967 -p1 -b .keyscan +%patch968 -p1 -b .proxyjump-loops +%patch969 -p1 -b .keygen-sha2 +%patch970 -p1 -b .rdomain +%patch971 -p1 -b .x11-ipv6 +%patch972 -p1 -b .channel-limits +%patch973 -p1 -b .sftp-timespeccmp +%patch974 -p1 -b .keygen-strip-doseol +%patch975 -p1 -b .preserve-pam-errors +%patch976 -p1 -b .restore-nonblock +%patch977 -p1 -b .cve-2020-14145 +%patch978 -p1 -b .sshd_config +%patch980 -p1 -b .cve-2021-41617 +%patch981 -p1 -b .sshdinclude +%patch982 -p1 -b .client_alive_count_max +%patch983 -p1 -b .sftp-realpath +%patch984 -p1 -b .crypto-policy-doc +%patch985 -p1 -b .minimize-sha1-use +%patch986 -p1 -b .banner +%patch987 -p1 -b .sftp_ipv6 +%patch988 -p1 -b .killall +%patch989 -p1 -b .bigsshdconfig %patch200 -p1 -b .audit %patch201 -p1 -b .audit-race %patch700 -p1 -b .fips %patch100 -p1 -b .coverity -%patch104 -p1 -b .openssl + +%patch1015 -p1 -b .cve-2023-38408 +%patch1017 -p1 -b .limitdelay +%patch1018 -p1 -b .cve-2023-48795 +%patch1019 -p1 -b .cve-2023-51385 +%patch1020 -p1 -b .scp-kill-switch +%patch1021 -p1 -b .ignore-SIGPIPE %patch98 -p1 -b .gsi +%patch99 -p1 -b .hpn sed 's/sshd.pid/gsisshd.pid/' -i pathnames.h sed 's!$(piddir)/sshd.pid!$(piddir)/gsisshd.pid!' -i Makefile.in -sed 's!/etc/sysconfig/sshd!/etc/sysconfig/gsisshd!' -i sshd_config +sed 's!/etc/pam.d/ssh!/etc/pam.d/gsisshd!' -i sshd_config cp -p %{SOURCE99} . @@ -375,10 +476,9 @@ fi --sysconfdir=%{_sysconfdir}/gsissh \ --libexecdir=%{_libexecdir}/gsissh \ --datadir=%{_datadir}/gsissh \ - --with-default-path=/usr/local/bin:/usr/bin \ - --with-superuser-path=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin \ - --with-privsep-path=%{_var}/empty/gsisshd \ - --enable-vendor-patchlevel="FC-%{openssh_ver}-%{openssh_rel}" \ + --with-default-path=%{_libexecdir}/gsissh/bin:/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin \ + --with-superuser-path=%{_libexecdir}/gsissh/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin \ + --with-privsep-path=%{_var}/empty/sshd \ --disable-strip \ --without-zlib-version-check \ --with-ssl-engine \ @@ -391,6 +491,7 @@ fi --with-ldap \ %endif --with-pam \ + --with-pam-service=gsisshd \ %if %{WITH_SELINUX} --with-selinux --with-audit=linux \ --with-sandbox=seccomp_filter \ @@ -414,27 +515,18 @@ fi make SSH_PROGRAM=%{_bindir}/gsissh \ ASKPASS_PROGRAM=%{_libexecdir}/openssh/ssh-askpass -# Add generation of HMAC checksums of the final stripped binaries -%global __spec_install_post \ - %%{?__debug_package:%%{__debug_install_post}} \ - %%{__arch_install_post} \ - %%{__os_install_post} \ - fipshmac -d $RPM_BUILD_ROOT%{_libdir}/fipscheck $RPM_BUILD_ROOT%{_bindir}/gsissh $RPM_BUILD_ROOT%{_sbindir}/gsisshd \ -%{nil} - %install rm -rf $RPM_BUILD_ROOT mkdir -p -m755 $RPM_BUILD_ROOT%{_sysconfdir}/gsissh mkdir -p -m755 $RPM_BUILD_ROOT%{_sysconfdir}/gsissh/ssh_config.d mkdir -p -m755 $RPM_BUILD_ROOT%{_libexecdir}/gsissh -mkdir -p -m755 $RPM_BUILD_ROOT%{_var}/empty/gsisshd +mkdir -p -m755 $RPM_BUILD_ROOT%{_var}/empty/sshd make install DESTDIR=$RPM_BUILD_ROOT rm -f $RPM_BUILD_ROOT%{_sysconfdir}/gsissh/ldap.conf install -d $RPM_BUILD_ROOT/etc/pam.d/ install -d $RPM_BUILD_ROOT/etc/sysconfig/ install -d $RPM_BUILD_ROOT%{_libexecdir}/gsissh -install -d $RPM_BUILD_ROOT%{_libdir}/fipscheck install -m644 %{SOURCE2} $RPM_BUILD_ROOT/etc/pam.d/gsisshd install -m644 %{SOURCE7} $RPM_BUILD_ROOT/etc/sysconfig/gsisshd install -m644 ssh_config_redhat $RPM_BUILD_ROOT/etc/gsissh/ssh_config.d/05-redhat.conf @@ -470,6 +562,11 @@ for f in $RPM_BUILD_ROOT%{_bindir}/* \ mv $f `dirname $f`/gsi`basename $f` done +# Add scp symlink in gsisshd's path +mkdir $RPM_BUILD_ROOT%{_libexecdir}/gsissh/bin +ln -nrs $RPM_BUILD_ROOT%{_bindir}/gsiscp \ + $RPM_BUILD_ROOT%{_libexecdir}/gsissh/bin/scp + perl -pi -e "s|$RPM_BUILD_ROOT||g" $RPM_BUILD_ROOT%{_mandir}/man*/* %pre @@ -491,19 +588,18 @@ getent passwd sshd >/dev/null || \ %systemd_postun_with_restart gsisshd.service %files -%license LICENCE LICENSE.globus_usage -%doc CREDITS ChangeLog INSTALL OVERVIEW PROTOCOL* README README.platform README.privsep README.tun README.dns README.sshd-and-gsisshd TODO +%license LICENCE +%doc CREDITS ChangeLog INSTALL OVERVIEW PROTOCOL* README HPN-README README.platform README.privsep README.tun README.dns README.sshd-and-gsisshd TODO %attr(0755,root,root) %dir %{_sysconfdir}/gsissh %attr(0644,root,root) %config(noreplace) %{_sysconfdir}/gsissh/moduli %attr(0755,root,root) %{_bindir}/gsissh-keygen %attr(0644,root,root) %{_mandir}/man1/gsissh-keygen.1* %attr(0755,root,root) %dir %{_libexecdir}/gsissh -%attr(2755,root,ssh_keys) %{_libexecdir}/gsissh/ssh-keysign +%attr(2555,root,ssh_keys) %{_libexecdir}/gsissh/ssh-keysign %attr(0644,root,root) %{_mandir}/man8/gsissh-keysign.8* %files clients %attr(0755,root,root) %{_bindir}/gsissh -%attr(0644,root,root) %{_libdir}/fipscheck/gsissh.hmac %attr(0644,root,root) %{_mandir}/man1/gsissh.1* %attr(0755,root,root) %{_bindir}/gsiscp %attr(0644,root,root) %{_mandir}/man1/gsiscp.1* @@ -513,11 +609,12 @@ getent passwd sshd >/dev/null || \ %attr(0644,root,root) %{_mandir}/man5/gsissh_config.5* %attr(0755,root,root) %{_bindir}/gsisftp %attr(0644,root,root) %{_mandir}/man1/gsisftp.1* +%attr(0755,root,root) %dir %{_libexecdir}/gsissh/bin +%{_libexecdir}/gsissh/bin/scp %files server -%dir %attr(0711,root,root) %{_var}/empty/gsisshd +%dir %attr(0711,root,root) %{_var}/empty/sshd %attr(0755,root,root) %{_sbindir}/gsisshd -%attr(0644,root,root) %{_libdir}/fipscheck/gsisshd.hmac %attr(0755,root,root) %{_libexecdir}/gsissh/sftp-server %attr(0755,root,root) %{_libexecdir}/gsissh/sshd-keygen %attr(0644,root,root) %{_mandir}/man5/gsisshd_config.5* @@ -535,6 +632,70 @@ getent passwd sshd >/dev/null || \ %attr(0644,root,root) %{_tmpfilesdir}/gsissh.conf %changelog +* Wed Oct 02 2024 Mattias Ellert - 8.0p1-16 +- Based on openssh-8.0p1-25.el8_10 + +* Fri Jul 12 2024 Mattias Ellert - 8.0p1-15 +- Add scp symlink in gsisshd's path + +* Tue Jul 09 2024 Mattias Ellert - 8.0p1-14 +- Based on openssh-8.0p1-24.el8 +- Drop patch openssh-8.0p1-sshbuf-readonly.patch (now included in + openssh-8.0p1-gssapi-keyex.patch) + +* Fri Aug 11 2023 Mattias Ellert - 8.0p1-13 +- Based on openssh-8.0p1-19.el8_8 + +* Sat Mar 11 2023 Mattias Ellert - 8.0p1-12 +- Based on openssh-8.0p1-17.el8_7 + +* Wed Oct 05 2022 Mattias Ellert - 8.0p1-11 +- Based on openssh-8.0p1-16.el8 + +* Tue Aug 09 2022 Mattias Ellert - 8.0p1-10 +- Based on openssh-8.0p1-13.el8 + +* Wed Nov 24 2021 Mattias Ellert - 8.0p1-9 +- Based on openssh-8.0p1-10.el8 + +* Sat May 22 2021 Mattias Ellert - 8.0p1-8 +- Based on openssh-8.0p1-6.el8_4.2 + +* Tue Mar 16 2021 Mattias Ellert - 8.0p1-7 +- Fix issue with read-only ssh buffer during gssapi key exchange +- Clean up HPN patch + +* Mon Nov 30 2020 Mattias Ellert - 8.0p1-6 +- Based on openssh-8.0p1-5.el8 + +* Fri Oct 23 2020 Frank Scheiner - 8.0p1-5 +- Update HPN patch with additional documentation and configuration and a + version number fix + +* Fri Sep 25 2020 Frank Scheiner - 8.0p1-4 +- Add HPN patch + +* Mon May 04 2020 Mattias Ellert - 8.0p1-3 +- Add missing buffer initialization in gsissh patch + +* Tue Apr 14 2020 Mattias Ellert - 8.0p1-2 +- Based on openssh-8.0p1-4.el8 + +* Wed Jan 08 2020 Mattias Ellert - 8.0p1-1 +- Based on openssh-8.0p1-3.el8 + +* Mon Aug 19 2019 Mattias Ellert - 7.8p1-5 +- Based on openssh-7.8p1-4.el8 + +* Wed Feb 27 2019 Mattias Ellert - 7.8p1-4 +- Remove usage statistics collection support + +* Fri Feb 08 2019 Mattias Ellert - 7.8p1-3 +- CVE-2019-7639 + +* Tue Jan 15 2019 Mattias Ellert - 7.8p1-2 +- Based on openssh-7.8p1-4.fc28 + * Tue Oct 23 2018 Mattias Ellert - 7.8p1-1 - Based on openssh-7.8p1-3.fc28 diff --git a/gsisshd.sysconfig b/gsisshd.sysconfig index 2981eee..a668d82 100644 --- a/gsisshd.sysconfig +++ b/gsisshd.sysconfig @@ -6,6 +6,12 @@ # of DSA key or systemctl mask gsisshd-keygen@rsa.service to disable RSA key # creation. +# Do not change this option unless you have hardware random +# generator and you REALLY know what you are doing + +SSH_USE_STRONG_RNG=0 +# SSH_USE_STRONG_RNG=1 + # System-wide crypto policy: # To opt-out, uncomment the following line # CRYPTO_POLICY= diff --git a/openssh-5.8p1-getaddrinfo.patch b/openssh-5.8p1-getaddrinfo.patch deleted file mode 100644 index 76deaef..0000000 --- a/openssh-5.8p1-getaddrinfo.patch +++ /dev/null @@ -1,24 +0,0 @@ -diff -up openssh-5.6p1/channels.c.getaddrinfo openssh-5.6p1/channels.c ---- openssh-5.6p1/channels.c.getaddrinfo 2012-02-14 16:12:54.427852524 +0100 -+++ openssh-5.6p1/channels.c 2012-02-14 16:13:22.818928690 +0100 -@@ -3275,6 +3275,9 @@ x11_create_display_inet(int x11_display_ - memset(&hints, 0, sizeof(hints)); - hints.ai_family = IPv4or6; - hints.ai_flags = x11_use_localhost ? 0: AI_PASSIVE; -+#ifdef AI_ADDRCONFIG -+ hints.ai_flags |= AI_ADDRCONFIG; -+#endif - hints.ai_socktype = SOCK_STREAM; - snprintf(strport, sizeof strport, "%d", port); - if ((gaierr = getaddrinfo(NULL, strport, &hints, &aitop)) != 0) { -diff -up openssh-5.6p1/sshconnect.c.getaddrinfo openssh-5.6p1/sshconnect.c ---- openssh-5.6p1/sshconnect.c.getaddrinfo 2012-02-14 16:09:25.057964291 +0100 -+++ openssh-5.6p1/sshconnect.c 2012-02-14 16:09:25.106047007 +0100 -@@ -343,6 +343,7 @@ ssh_connect(const char *host, struct soc - memset(&hints, 0, sizeof(hints)); - hints.ai_family = family; - hints.ai_socktype = SOCK_STREAM; -+ hints.ai_flags = AI_V4MAPPED | AI_ADDRCONFIG; - snprintf(strport, sizeof strport, "%u", port); - if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) - fatal("%s: Could not resolve hostname %.100s: %s", __progname, diff --git a/openssh-6.1p1-gssapi-canohost.patch b/openssh-6.1p1-gssapi-canohost.patch deleted file mode 100644 index 3e6c9cc..0000000 --- a/openssh-6.1p1-gssapi-canohost.patch +++ /dev/null @@ -1,21 +0,0 @@ -diff -up openssh-6.1p1/sshconnect2.c.canohost openssh-6.1p1/sshconnect2.c ---- openssh-6.1p1/sshconnect2.c.canohost 2012-10-30 10:52:59.593301692 +0100 -+++ openssh-6.1p1/sshconnect2.c 2012-10-30 11:01:12.870301632 +0100 -@@ -699,12 +699,15 @@ userauth_gssapi(Authctxt *authctxt) - static u_int mech = 0; - OM_uint32 min; - int r, ok = 0; -- const char *gss_host; -+ const char *gss_host = NULL; - - if (options.gss_server_identity) - gss_host = options.gss_server_identity; -- else if (options.gss_trust_dns) -+ else if (options.gss_trust_dns) { - gss_host = get_canonical_hostname(active_state, 1); -+ if (strcmp(gss_host, "UNKNOWN") == 0) -+ gss_host = authctxt->host; -+ } - else - gss_host = authctxt->host; - diff --git a/openssh-6.2p1-vendor.patch b/openssh-6.2p1-vendor.patch deleted file mode 100644 index 1af5e9d..0000000 --- a/openssh-6.2p1-vendor.patch +++ /dev/null @@ -1,142 +0,0 @@ -diff -up openssh-7.4p1/configure.ac.vendor openssh-7.4p1/configure.ac ---- openssh-7.4p1/configure.ac.vendor 2016-12-23 13:34:51.681253844 +0100 -+++ openssh-7.4p1/configure.ac 2016-12-23 13:34:51.694253847 +0100 -@@ -4930,6 +4930,12 @@ AC_ARG_WITH([lastlog], - fi - ] - ) -+AC_ARG_ENABLE(vendor-patchlevel, -+ [ --enable-vendor-patchlevel=TAG specify a vendor patch level], -+ [AC_DEFINE_UNQUOTED(SSH_VENDOR_PATCHLEVEL,[SSH_RELEASE "-" "$enableval"],[Define to your vendor patch level, if it has been modified from the upstream source release.]) -+ SSH_VENDOR_PATCHLEVEL="$enableval"], -+ [AC_DEFINE(SSH_VENDOR_PATCHLEVEL,SSH_RELEASE,[Define to your vendor patch level, if it has been modified from the upstream source release.]) -+ SSH_VENDOR_PATCHLEVEL=none]) - - dnl lastlog, [uw]tmpx? detection - dnl NOTE: set the paths in the platform section to avoid the -@@ -5194,6 +5200,7 @@ echo " Translate v4 in v6 hack - echo " BSD Auth support: $BSD_AUTH_MSG" - echo " Random number source: $RAND_MSG" - echo " Privsep sandbox style: $SANDBOX_STYLE" -+echo " Vendor patch level: $SSH_VENDOR_PATCHLEVEL" - - echo "" - -diff -up openssh-7.4p1/servconf.c.vendor openssh-7.4p1/servconf.c ---- openssh-7.4p1/servconf.c.vendor 2016-12-19 05:59:41.000000000 +0100 -+++ openssh-7.4p1/servconf.c 2016-12-23 13:36:07.555268628 +0100 -@@ -143,6 +143,7 @@ initialize_server_options(ServerOptions - options->max_authtries = -1; - options->max_sessions = -1; - options->banner = NULL; -+ options->show_patchlevel = -1; - options->use_dns = -1; - options->client_alive_interval = -1; - options->client_alive_count_max = -1; -@@ -325,6 +326,8 @@ fill_default_server_options(ServerOption - options->ip_qos_bulk = IPTOS_DSCP_CS1; - if (options->version_addendum == NULL) - options->version_addendum = xstrdup(""); -+ if (options->show_patchlevel == -1) -+ options->show_patchlevel = 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) -@@ -402,7 +405,7 @@ typedef enum { - sIgnoreUserKnownHosts, sCiphers, sMacs, sPidFile, - sGatewayPorts, sPubkeyAuthentication, sPubkeyAcceptedKeyTypes, - sXAuthLocation, sSubsystem, sMaxStartups, sMaxAuthTries, sMaxSessions, -- sBanner, sUseDNS, sHostbasedAuthentication, -+ sBanner, sShowPatchLevel, sUseDNS, sHostbasedAuthentication, - sHostbasedUsesNameFromPacketOnly, sHostbasedAcceptedKeyTypes, - sHostKeyAlgorithms, - sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile, -@@ -528,6 +531,7 @@ static struct { - { "maxauthtries", sMaxAuthTries, SSHCFG_ALL }, - { "maxsessions", sMaxSessions, SSHCFG_ALL }, - { "banner", sBanner, SSHCFG_ALL }, -+ { "showpatchlevel", sShowPatchLevel, SSHCFG_GLOBAL }, - { "usedns", sUseDNS, SSHCFG_GLOBAL }, - { "verifyreversemapping", sDeprecated, SSHCFG_GLOBAL }, - { "reversemappingcheck", sDeprecated, SSHCFG_GLOBAL }, -@@ -1369,6 +1373,10 @@ process_server_config_line(ServerOptions - intptr = &options->disable_forwarding; - goto parse_flag; - -+ case sShowPatchLevel: -+ intptr = &options->show_patchlevel; -+ goto parse_flag; -+ - case sAllowUsers: - while ((arg = strdelim(&cp)) && *arg != '\0') { - if (match_user(NULL, NULL, NULL, arg) == -1) -@@ -2269,6 +2277,7 @@ dump_config(ServerOptions *o) - 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(sShowPatchLevel, o->show_patchlevel); - dump_cfg_fmtint(sUseDNS, o->use_dns); - dump_cfg_fmtint(sAllowTcpForwarding, o->allow_tcp_forwarding); - dump_cfg_fmtint(sAllowAgentForwarding, o->allow_agent_forwarding); -diff -up openssh-7.4p1/servconf.h.vendor openssh-7.4p1/servconf.h ---- openssh-7.4p1/servconf.h.vendor 2016-12-19 05:59:41.000000000 +0100 -+++ openssh-7.4p1/servconf.h 2016-12-23 13:34:51.694253847 +0100 -@@ -149,6 +149,7 @@ typedef struct { - int max_authtries; - int max_sessions; - char *banner; /* SSH-2 banner message */ -+ int show_patchlevel; /* Show vendor patch level to clients */ - int use_dns; - int client_alive_interval; /* - * poke the client this often to -diff -up openssh-7.4p1/sshd_config.5.vendor openssh-7.4p1/sshd_config.5 ---- openssh-7.4p1/sshd_config.5.vendor 2016-12-23 13:34:51.695253847 +0100 -+++ openssh-7.4p1/sshd_config.5 2016-12-23 13:37:17.482282253 +0100 -@@ -1334,6 +1334,13 @@ an OpenSSH Key Revocation List (KRL) as - .Cm AcceptEnv - or - .Cm PermitUserEnvironment . -+.It Cm ShowPatchLevel -+Specifies whether -+.Nm sshd -+will display the patch level of the binary in the identification string. -+The patch level is set at compile-time. -+The default is -+.Dq no . - .It Cm StreamLocalBindMask - Sets the octal file creation mode mask - .Pq umask -diff -up openssh-7.4p1/sshd_config.vendor openssh-7.4p1/sshd_config ---- openssh-7.4p1/sshd_config.vendor 2016-12-23 13:34:51.690253846 +0100 -+++ openssh-7.4p1/sshd_config 2016-12-23 13:34:51.695253847 +0100 -@@ -105,6 +105,7 @@ X11Forwarding yes - #Compression delayed - #ClientAliveInterval 0 - #ClientAliveCountMax 3 -+#ShowPatchLevel no - #UseDNS no - #PidFile /var/run/sshd.pid - #MaxStartups 10:30:100 -diff -up openssh-7.4p1/sshd.c.vendor openssh-7.4p1/sshd.c ---- openssh-7.4p1/sshd.c.vendor 2016-12-23 13:34:51.682253844 +0100 -+++ openssh-7.4p1/sshd.c 2016-12-23 13:38:32.434296856 +0100 -@@ -367,7 +367,8 @@ sshd_exchange_identification(struct ssh - char remote_version[256]; /* Must be at least as big as buf. */ - - xasprintf(&server_version_string, "SSH-%d.%d-%.100s%s%s\r\n", -- PROTOCOL_MAJOR_2, PROTOCOL_MINOR_2, SSH_VERSION, -+ PROTOCOL_MAJOR_2, PROTOCOL_MINOR_2, -+ (options.show_patchlevel == 1) ? SSH_VENDOR_PATCHLEVEL : SSH_VERSION, - *options.version_addendum == '\0' ? "" : " ", - options.version_addendum); - -@@ -1650,7 +1651,8 @@ main(int ac, char **av) - exit(1); - } - -- debug("sshd version %s, %s", SSH_VERSION, -+ debug("sshd version %s, %s", -+ (options.show_patchlevel == 1) ? SSH_VENDOR_PATCHLEVEL : SSH_VERSION, - #ifdef WITH_OPENSSL - SSLeay_version(SSLEAY_VERSION) - #else diff --git a/openssh-6.6.1p1-log-in-chroot.patch b/openssh-6.6.1p1-log-in-chroot.patch index 7f822ab..b009d99 100644 --- a/openssh-6.6.1p1-log-in-chroot.patch +++ b/openssh-6.6.1p1-log-in-chroot.patch @@ -46,7 +46,7 @@ diff -up openssh-7.4p1/monitor.c.log-in-chroot openssh-7.4p1/monitor.c + pmonitor->m_state = "preauth"; + - authctxt = _authctxt; + authctxt = (Authctxt *)ssh->authctxt; memset(authctxt, 0, sizeof(*authctxt)); ssh->authctxt = authctxt; @@ -405,6 +407,8 @@ monitor_child_postauth(struct monitor *p @@ -113,7 +113,7 @@ diff -up openssh-7.4p1/monitor.h.log-in-chroot openssh-7.4p1/monitor.h +void monitor_reinit(struct monitor *, const char *); struct Authctxt; - void monitor_child_preauth(struct Authctxt *, struct monitor *); + void monitor_child_preauth(struct ssh *, struct monitor *); diff -up openssh-7.4p1/session.c.log-in-chroot openssh-7.4p1/session.c --- openssh-7.4p1/session.c.log-in-chroot 2016-12-23 15:14:33.319168086 +0100 +++ openssh-7.4p1/session.c 2016-12-23 15:18:18.742211853 +0100 diff --git a/openssh-6.6.1p1-selinux-contexts.patch b/openssh-6.6.1p1-selinux-contexts.patch index 8bf6c78..3a7193e 100644 --- a/openssh-6.6.1p1-selinux-contexts.patch +++ b/openssh-6.6.1p1-selinux-contexts.patch @@ -19,7 +19,7 @@ index 8f32464..18a2ca4 100644 if (!sshd_selinux_enabled()) return; -@@ -461,6 +462,58 @@ sshd_selinux_copy_context(void) +@@ -461,6 +462,72 @@ sshd_selinux_copy_context(void) } } @@ -30,46 +30,60 @@ index 8f32464..18a2ca4 100644 + char line[1024], *preauth_context = NULL, *cp, *arg; + const char *contexts_path; + FILE *contexts_file; ++ struct stat sb; + + contexts_path = selinux_openssh_contexts_path(); -+ if (contexts_path != NULL) { -+ if ((contexts_file = fopen(contexts_path, "r")) != NULL) { -+ struct stat sb; -+ -+ if (fstat(fileno(contexts_file), &sb) == 0 && ((sb.st_uid == 0) && ((sb.st_mode & 022) == 0))) { -+ while (fgets(line, sizeof(line), contexts_file)) { -+ /* Strip trailing whitespace */ -+ for (len = strlen(line) - 1; len > 0; len--) { -+ if (strchr(" \t\r\n", line[len]) == NULL) -+ break; -+ line[len] = '\0'; -+ } -+ -+ if (line[0] == '\0') -+ continue; -+ -+ cp = line; -+ arg = strdelim(&cp); -+ if (arg && *arg == '\0') -+ arg = strdelim(&cp); -+ -+ if (arg && strcmp(arg, "privsep_preauth") == 0) { -+ arg = strdelim(&cp); -+ if (!arg || *arg == '\0') { -+ debug("%s: privsep_preauth is empty", __func__); -+ fclose(contexts_file); -+ return; -+ } -+ preauth_context = xstrdup(arg); -+ } -+ } -+ } -+ fclose(contexts_file); -+ } ++ if (contexts_path == NULL) { ++ debug3("%s: Failed to get the path to SELinux context", __func__); ++ return; + } + -+ if (preauth_context == NULL) -+ preauth_context = xstrdup("sshd_net_t"); ++ if ((contexts_file = fopen(contexts_path, "r")) == NULL) { ++ debug("%s: Failed to open SELinux context file", __func__); ++ return; ++ } ++ ++ if (fstat(fileno(contexts_file), &sb) != 0 || ++ sb.st_uid != 0 || (sb.st_mode & 022) != 0) { ++ logit("%s: SELinux context file needs to be owned by root" ++ " and not writable by anyone else", __func__); ++ fclose(contexts_file); ++ return; ++ } ++ ++ while (fgets(line, sizeof(line), contexts_file)) { ++ /* Strip trailing whitespace */ ++ for (len = strlen(line) - 1; len > 0; len--) { ++ if (strchr(" \t\r\n", line[len]) == NULL) ++ break; ++ line[len] = '\0'; ++ } ++ ++ if (line[0] == '\0') ++ continue; ++ ++ cp = line; ++ arg = strdelim(&cp); ++ if (arg && *arg == '\0') ++ arg = strdelim(&cp); ++ ++ if (arg && strcmp(arg, "privsep_preauth") == 0) { ++ arg = strdelim(&cp); ++ if (!arg || *arg == '\0') { ++ debug("%s: privsep_preauth is empty", __func__); ++ fclose(contexts_file); ++ return; ++ } ++ preauth_context = xstrdup(arg); ++ } ++ } ++ fclose(contexts_file); ++ ++ if (preauth_context == NULL) { ++ debug("%s: Unable to find 'privsep_preauth' option in" ++ " SELinux context file", __func__); ++ return; ++ } + + ssh_selinux_change_context(preauth_context); + free(preauth_context); @@ -82,14 +96,6 @@ diff --git a/openbsd-compat/port-linux.c b/openbsd-compat/port-linux.c index 22ea8ef..1fc963d 100644 --- a/openbsd-compat/port-linux.c +++ b/openbsd-compat/port-linux.c -@@ -26,6 +26,7 @@ - #include - #include - #include -+#include - - #include "log.h" - #include "xmalloc.h" @@ -179,7 +179,7 @@ ssh_selinux_change_context(const char *newname) strlcpy(newctx + len, newname, newlen - len); if ((cx = index(cx + 1, ':'))) diff --git a/openssh-6.6p1-GSSAPIEnablek5users.patch b/openssh-6.6p1-GSSAPIEnablek5users.patch index 37e010d..d943f41 100644 --- a/openssh-6.6p1-GSSAPIEnablek5users.patch +++ b/openssh-6.6p1-GSSAPIEnablek5users.patch @@ -22,15 +22,15 @@ diff -up openssh-7.4p1/servconf.c.GSSAPIEnablek5users openssh-7.4p1/servconf.c --- openssh-7.4p1/servconf.c.GSSAPIEnablek5users 2016-12-23 15:18:40.615216100 +0100 +++ openssh-7.4p1/servconf.c 2016-12-23 15:35:36.354401156 +0100 @@ -168,6 +168,7 @@ initialize_server_options(ServerOptions - options->gss_strict_acceptor = -1; options->gss_store_rekey = -1; + options->gss_kex_algorithms = NULL; options->use_kuserok = -1; + options->enable_k5users = -1; options->password_authentication = -1; options->kbd_interactive_authentication = -1; options->challenge_response_authentication = -1; @@ -345,6 +346,8 @@ fill_default_server_options(ServerOption - options->gss_store_rekey = 0; + #endif if (options->use_kuserok == -1) options->use_kuserok = 1; + if (options->enable_k5users == -1) @@ -44,20 +44,22 @@ diff -up openssh-7.4p1/servconf.c.GSSAPIEnablek5users openssh-7.4p1/servconf.c sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile, - sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor, + sGssAuthentication, sGssCleanupCreds, sGssEnablek5users, sGssStrictAcceptor, - sGssKeyEx, sGssStoreRekey, sAcceptEnv, sSetEnv, sPermitTunnel, + sGssKeyEx, sGssKexAlgorithms, sGssStoreRekey, + sAcceptEnv, sSetEnv, sPermitTunnel, sMatch, sPermitOpen, sPermitListen, sForceCommand, sChrootDirectory, - sUsePrivilegeSeparation, sAllowAgentForwarding, -@@ -497,12 +500,14 @@ static struct { - { "gssapistrictacceptorcheck", sGssStrictAcceptor, SSHCFG_GLOBAL }, +@@ -497,14 +500,16 @@ static struct { { "gssapikeyexchange", sGssKeyEx, SSHCFG_GLOBAL }, { "gssapistorecredentialsonrekey", sGssStoreRekey, SSHCFG_GLOBAL }, + { "gssapikexalgorithms", sGssKexAlgorithms, SSHCFG_GLOBAL }, + { "gssapienablek5users", sGssEnablek5users, SSHCFG_ALL }, #else { "gssapiauthentication", sUnsupported, SSHCFG_ALL }, { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL }, + { "gssapicleanupcreds", sUnsupported, SSHCFG_GLOBAL }, { "gssapistrictacceptorcheck", sUnsupported, SSHCFG_GLOBAL }, { "gssapikeyexchange", sUnsupported, SSHCFG_GLOBAL }, { "gssapistorecredentialsonrekey", sUnsupported, SSHCFG_GLOBAL }, + { "gssapikexalgorithms", sUnsupported, SSHCFG_GLOBAL }, + { "gssapienablek5users", sUnsupported, SSHCFG_ALL }, #endif { "gssusesessionccache", sUnsupported, SSHCFG_GLOBAL }, @@ -83,7 +85,7 @@ diff -up openssh-7.4p1/servconf.c.GSSAPIEnablek5users openssh-7.4p1/servconf.c M_CP_INTOPT(log_level); @@ -2320,6 +2330,7 @@ dump_config(ServerOptions *o) # endif - dump_cfg_fmtint(sKerberosUniqueTicket, o->kerberos_unique_ticket); + dump_cfg_fmtint(sKerberosUniqueCCache, o->kerberos_unique_ccache); dump_cfg_fmtint(sKerberosUseKuserok, o->use_kuserok); + dump_cfg_fmtint(sGssEnablek5users, o->enable_k5users); #endif @@ -93,7 +95,7 @@ diff -up openssh-7.4p1/servconf.h.GSSAPIEnablek5users openssh-7.4p1/servconf.h --- openssh-7.4p1/servconf.h.GSSAPIEnablek5users 2016-12-23 15:18:40.616216100 +0100 +++ openssh-7.4p1/servconf.h 2016-12-23 15:18:40.629216102 +0100 @@ -174,6 +174,7 @@ typedef struct { - int kerberos_unique_ticket; /* If true, the aquired ticket will + int kerberos_unique_ccache; /* If true, the acquired ticket will * be stored in per-session ccache */ int use_kuserok; + int enable_k5users; diff --git a/openssh-6.6p1-ctr-cavstest.patch b/openssh-6.6p1-ctr-cavstest.patch index c56313d..e906b70 100644 --- a/openssh-6.6p1-ctr-cavstest.patch +++ b/openssh-6.6p1-ctr-cavstest.patch @@ -20,10 +20,10 @@ diff -up openssh-6.8p1/Makefile.in.ctr-cavs openssh-6.8p1/Makefile.in ssh-xmss.o \ @@ -194,6 +195,9 @@ ssh-ldap-helper$(EXEEXT): $(LIBCOMPAT) l ssh-keycat$(EXEEXT): $(LIBCOMPAT) $(SSHDOBJS) libssh.a ssh-keycat.o uidswap.o - $(LD) -o $@ ssh-keycat.o uidswap.o $(LDFLAGS) -lssh -lopenbsd-compat -lfipscheck $(KEYCATLIBS) $(LIBS) + $(LD) -o $@ ssh-keycat.o uidswap.o $(LDFLAGS) -lssh -lopenbsd-compat $(KEYCATLIBS) $(LIBS) +ctr-cavstest$(EXEEXT): $(LIBCOMPAT) libssh.a ctr-cavstest.o -+ $(LD) -o $@ ctr-cavstest.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh -lfipscheck $(LIBS) ++ $(LD) -o $@ ctr-cavstest.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh $(LIBS) + ssh-keyscan$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-keyscan.o $(LD) -o $@ ssh-keyscan.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh $(LIBS) @@ -187,7 +187,7 @@ diff -up openssh-6.8p1/ctr-cavstest.c.ctr-cavs openssh-6.8p1/ctr-cavstest.c + usage(); + } + -+ SSLeay_add_all_algorithms(); ++ OpenSSL_add_all_algorithms(); + + c = cipher_by_name(algo); + if (c == NULL) { diff --git a/openssh-6.6p1-force_krb.patch b/openssh-6.6p1-force_krb.patch index aeee77f..90f8322 100644 --- a/openssh-6.6p1-force_krb.patch +++ b/openssh-6.6p1-force_krb.patch @@ -235,9 +235,9 @@ index 28659ec..9c94d8e 100644 +#endif +#endif + + s->forced = 0; if (forced != NULL) { - if (IS_INTERNAL_SFTP(command)) { - s->is_subsystem = s->is_subsystem ? + s->forced = 1; diff --git a/ssh-gss.h b/ssh-gss.h index 0374c88..509109a 100644 --- a/ssh-gss.h diff --git a/openssh-6.6p1-keycat.patch b/openssh-6.6p1-keycat.patch index e22a5f3..c658a87 100644 --- a/openssh-6.6p1-keycat.patch +++ b/openssh-6.6p1-keycat.patch @@ -62,10 +62,10 @@ diff -up openssh/Makefile.in.keycat openssh/Makefile.in ssh-xmss.o \ @@ -190,6 +191,9 @@ ssh-pkcs11-helper$(EXEEXT): $(LIBCOMPAT) ssh-ldap-helper$(EXEEXT): $(LIBCOMPAT) libssh.a ldapconf.o ldapbody.o ldapmisc.o ldap-helper.o - $(LD) -o $@ ldapconf.o ldapbody.o ldapmisc.o ldap-helper.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh -lopenbsd-compat -lfipscheck $(LIBS) $(LDAPLIBS) + $(LD) -o $@ ldapconf.o ldapbody.o ldapmisc.o ldap-helper.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) $(LDAPLIBS) +ssh-keycat$(EXEEXT): $(LIBCOMPAT) $(SSHDOBJS) libssh.a ssh-keycat.o uidswap.o -+ $(LD) -o $@ ssh-keycat.o uidswap.o $(LDFLAGS) -lssh -lopenbsd-compat -lfipscheck $(KEYCATLIBS) $(LIBS) ++ $(LD) -o $@ ssh-keycat.o uidswap.o $(LDFLAGS) -lssh -lopenbsd-compat $(KEYCATLIBS) $(LIBS) + ssh-keyscan$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-keyscan.o $(LD) -o $@ ssh-keyscan.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh $(LIBS) diff --git a/openssh-6.6p1-kuserok.patch b/openssh-6.6p1-kuserok.patch index 81ec2a4..56a6950 100644 --- a/openssh-6.6p1-kuserok.patch +++ b/openssh-6.6p1-kuserok.patch @@ -176,17 +176,17 @@ diff -up openssh-7.4p1/servconf.c.kuserok openssh-7.4p1/servconf.c --- openssh-7.4p1/servconf.c.kuserok 2016-12-23 14:36:07.630465944 +0100 +++ openssh-7.4p1/servconf.c 2016-12-23 15:11:52.278133344 +0100 @@ -116,6 +116,7 @@ initialize_server_options(ServerOptions - options->gss_cleanup_creds = -1; options->gss_strict_acceptor = -1; options->gss_store_rekey = -1; + options->gss_kex_algorithms = NULL; + options->use_kuserok = -1; options->password_authentication = -1; options->kbd_interactive_authentication = -1; options->challenge_response_authentication = -1; @@ -278,6 +279,8 @@ fill_default_server_options(ServerOption - options->gss_strict_acceptor = 1; - if (options->gss_store_rekey == -1) - options->gss_store_rekey = 0; + if (options->gss_kex_algorithms == NULL) + options->gss_kex_algorithms = strdup(GSS_KEX_DEFAULT_KEX); + #endif + if (options->use_kuserok == -1) + options->use_kuserok = 1; if (options->password_authentication == -1) @@ -196,22 +196,22 @@ diff -up openssh-7.4p1/servconf.c.kuserok openssh-7.4p1/servconf.c sPermitRootLogin, sLogFacility, sLogLevel, sRhostsRSAAuthentication, sRSAAuthentication, sKerberosAuthentication, sKerberosOrLocalPasswd, sKerberosTicketCleanup, -- sKerberosGetAFSToken, sKerberosUniqueTicket, -+ sKerberosGetAFSToken, sKerberosUniqueTicket, sKerberosUseKuserok, +- sKerberosGetAFSToken, sKerberosUniqueCCache, ++ sKerberosGetAFSToken, sKerberosUniqueCCache, sKerberosUseKuserok, sChallengeResponseAuthentication, sPasswordAuthentication, sKbdInteractiveAuthentication, sListenAddress, sAddressFamily, @@ -478,12 +481,14 @@ static struct { { "kerberosgetafstoken", sUnsupported, SSHCFG_GLOBAL }, #endif - { "kerberosuniqueticket", sKerberosUniqueTicket, SSHCFG_GLOBAL }, + { "kerberosuniqueccache", sKerberosUniqueCCache, SSHCFG_GLOBAL }, + { "kerberosusekuserok", sKerberosUseKuserok, SSHCFG_ALL }, #else { "kerberosauthentication", sUnsupported, SSHCFG_ALL }, { "kerberosorlocalpasswd", sUnsupported, SSHCFG_GLOBAL }, { "kerberosticketcleanup", sUnsupported, SSHCFG_GLOBAL }, { "kerberosgetafstoken", sUnsupported, SSHCFG_GLOBAL }, - { "kerberosuniqueticket", sUnsupported, SSHCFG_GLOBAL }, + { "kerberosuniqueccache", sUnsupported, SSHCFG_GLOBAL }, + { "kerberosusekuserok", sUnsupported, SSHCFG_ALL }, #endif { "kerberostgtpassing", sUnsupported, SSHCFG_GLOBAL }, @@ -238,7 +238,7 @@ diff -up openssh-7.4p1/servconf.c.kuserok openssh-7.4p1/servconf.c @@ -2309,6 +2319,7 @@ dump_config(ServerOptions *o) dump_cfg_fmtint(sKerberosGetAFSToken, o->kerberos_get_afs_token); # endif - dump_cfg_fmtint(sKerberosUniqueTicket, o->kerberos_unique_ticket); + dump_cfg_fmtint(sKerberosUniqueCCache, o->kerberos_unique_ccache); + dump_cfg_fmtint(sKerberosUseKuserok, o->use_kuserok); #endif #ifdef GSSAPI @@ -248,7 +248,7 @@ diff -up openssh-7.4p1/servconf.h.kuserok openssh-7.4p1/servconf.h +++ openssh-7.4p1/servconf.h 2016-12-23 14:36:07.645465936 +0100 @@ -118,6 +118,7 @@ typedef struct { * authenticated with Kerberos. */ - int kerberos_unique_ticket; /* If true, the aquired ticket will + int kerberos_unique_ccache; /* If true, the acquired ticket will * be stored in per-session ccache */ + int use_kuserok; int gss_authentication; /* If true, permit GSSAPI authentication */ @@ -258,9 +258,9 @@ diff -up openssh-7.4p1/sshd_config.5.kuserok openssh-7.4p1/sshd_config.5 --- openssh-7.4p1/sshd_config.5.kuserok 2016-12-23 14:36:07.637465940 +0100 +++ openssh-7.4p1/sshd_config.5 2016-12-23 15:14:03.117162222 +0100 @@ -850,6 +850,10 @@ Specifies whether to automatically destr - tickets aquired in different sessions of the same user. - The default is - .Cm no . + .Cm no + can lead to overwriting previous tickets by subseqent connections to the same + user account. +.It Cm KerberosUseKuserok +Specifies whether to look at .k5login file for user's aliases. +The default is diff --git a/openssh-6.6p1-privsep-selinux.patch b/openssh-6.6p1-privsep-selinux.patch index 1365506..3d4c287 100644 --- a/openssh-6.6p1-privsep-selinux.patch +++ b/openssh-6.6p1-privsep-selinux.patch @@ -25,15 +25,15 @@ diff -up openssh-7.4p1/openbsd-compat/port-linux-sshd.c.privsep-selinux openssh- + return; + + if (getexeccon((security_context_t *)&ctx) != 0) { -+ logit("%s: getcon failed with %s", __func__, strerror (errno)); ++ logit("%s: getexeccon failed with %s", __func__, strerror(errno)); + return; + } + if (ctx != NULL) { + /* unset exec context before we will lose this capabililty */ + if (setexeccon(NULL) != 0) -+ fatal("%s: setexeccon failed with %s", __func__, strerror (errno)); ++ fatal("%s: setexeccon failed with %s", __func__, strerror(errno)); + if (setcon(ctx) != 0) -+ fatal("%s: setcon failed with %s", __func__, strerror (errno)); ++ fatal("%s: setcon failed with %s", __func__, strerror(errno)); + freecon(ctx); + } +} diff --git a/openssh-6.7p1-coverity.patch b/openssh-6.7p1-coverity.patch index 0d238dd..d24c4a2 100644 --- a/openssh-6.7p1-coverity.patch +++ b/openssh-6.7p1-coverity.patch @@ -1,26 +1,30 @@ -diff -up openssh-7.4p1/channels.c.coverity openssh-7.4p1/channels.c ---- openssh-7.4p1/channels.c.coverity 2016-12-23 16:40:26.881788686 +0100 -+++ openssh-7.4p1/channels.c 2016-12-23 16:42:36.244818763 +0100 -@@ -288,11 +288,11 @@ channel_register_fds(Channel *c, int rfd - - /* enable nonblocking mode */ - if (nonblock) { -- if (rfd != -1) -+ if (rfd >= 0) +diff -up openssh-8.0p1/channels.c.coverity openssh-8.0p1/channels.c +--- openssh-8.0p1/channels.c.coverity 2021-06-21 10:59:17.297473319 +0200 ++++ openssh-8.0p1/channels.c 2021-06-21 11:11:32.467290400 +0200 +@@ -341,15 +341,15 @@ channel_register_fds(struct ssh *ssh, Ch + * restore their blocking state on exit to avoid interfering + * with other programs that follow. + */ +- if (rfd != -1 && !isatty(rfd) && fcntl(rfd, F_GETFL) == 0) { ++ if (rfd >= 0 && !isatty(rfd) && fcntl(rfd, F_GETFL) == 0) { + c->restore_block |= CHANNEL_RESTORE_RFD; set_nonblock(rfd); -- if (wfd != -1) -+ if (wfd >= 0) + } +- if (wfd != -1 && !isatty(wfd) && fcntl(wfd, F_GETFL) == 0) { ++ if (wfd >= 0 && !isatty(wfd) && fcntl(wfd, F_GETFL) == 0) { + c->restore_block |= CHANNEL_RESTORE_WFD; set_nonblock(wfd); -- if (efd != -1) -+ if (efd >= 0) + } +- if (efd != -1 && !isatty(efd) && fcntl(efd, F_GETFL) == 0) { ++ if (efd >= 0 && !isatty(efd) && fcntl(efd, F_GETFL) == 0) { + c->restore_block |= CHANNEL_RESTORE_EFD; set_nonblock(efd); - } - } -diff -up openssh-7.4p1/monitor.c.coverity openssh-7.4p1/monitor.c ---- openssh-7.4p1/monitor.c.coverity 2016-12-23 16:40:26.888788688 +0100 -+++ openssh-7.4p1/monitor.c 2016-12-23 16:40:26.900788691 +0100 -@@ -411,7 +411,7 @@ monitor_child_preauth(Authctxt *_authctx - mm_get_keystate(pmonitor); + } +diff -up openssh-8.0p1/monitor.c.coverity openssh-8.0p1/monitor.c +--- openssh-8.0p1/monitor.c.coverity 2021-06-21 10:59:17.282473202 +0200 ++++ openssh-8.0p1/monitor.c 2021-06-21 10:59:17.297473319 +0200 +@@ -401,7 +401,7 @@ monitor_child_preauth(struct ssh *ssh, s + mm_get_keystate(ssh, pmonitor); /* Drain any buffered messages from the child */ - while (pmonitor->m_log_recvfd != -1 && monitor_read_log(pmonitor) == 0) @@ -124,26 +128,14 @@ diff -up openssh-7.4p1/serverloop.c.coverity openssh-7.4p1/serverloop.c } @@ -518,7 +518,7 @@ server_request_tun(void) + debug("%s: invalid tun", __func__); + goto done; } - - tun = packet_get_int(); - if (auth_opts->force_tun_device != -1) { + if (auth_opts->force_tun_device >= 0) { - if (tun != SSH_TUNID_ANY && auth_opts->force_tun_device != tun) + if (tun != SSH_TUNID_ANY && + auth_opts->force_tun_device != (int)tun) goto done; - tun = auth_opts->force_tun_device; -diff -up openssh-7.4p1/sftp.c.coverity openssh-7.4p1/sftp.c ---- openssh-7.4p1/sftp.c.coverity 2016-12-19 05:59:41.000000000 +0100 -+++ openssh-7.4p1/sftp.c 2016-12-23 16:40:26.903788691 +0100 -@@ -224,7 +224,7 @@ killchild(int signo) - { - if (sshpid > 1) { - kill(sshpid, SIGTERM); -- waitpid(sshpid, NULL, 0); -+ (void) waitpid(sshpid, NULL, 0); - } - - _exit(1); diff -up openssh-7.4p1/ssh-agent.c.coverity openssh-7.4p1/ssh-agent.c --- openssh-7.4p1/ssh-agent.c.coverity 2016-12-19 05:59:41.000000000 +0100 +++ openssh-7.4p1/ssh-agent.c 2016-12-23 16:40:26.903788691 +0100 @@ -163,7 +155,7 @@ diff -up openssh-7.4p1/sshd.c.coverity openssh-7.4p1/sshd.c +++ openssh-7.4p1/sshd.c 2016-12-23 16:40:26.904788692 +0100 @@ -691,8 +691,10 @@ privsep_preauth(Authctxt *authctxt) - privsep_preauth_child(); + privsep_preauth_child(ssh); setproctitle("%s", "[net]"); - if (box != NULL) + if (box != NULL) { @@ -174,8 +166,8 @@ diff -up openssh-7.4p1/sshd.c.coverity openssh-7.4p1/sshd.c return 0; } @@ -1386,6 +1388,9 @@ server_accept_loop(int *sock_in, int *so - if (num_listen_socks < 0) - break; + explicit_bzero(rnd, sizeof(rnd)); + } } + + if (fdset != NULL) diff --git a/openssh-6.7p1-kdf-cavs.patch b/openssh-6.7p1-kdf-cavs.patch index 181267c..549cde4 100644 --- a/openssh-6.7p1-kdf-cavs.patch +++ b/openssh-6.7p1-kdf-cavs.patch @@ -20,7 +20,7 @@ diff -up openssh-6.8p1/Makefile.in.kdf-cavs openssh-6.8p1/Makefile.in ssh-xmss.o \ @@ -198,6 +199,9 @@ ssh-keycat$(EXEEXT): $(LIBCOMPAT) $(SSHD ctr-cavstest$(EXEEXT): $(LIBCOMPAT) libssh.a ctr-cavstest.o - $(LD) -o $@ ctr-cavstest.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh -lfipscheck $(LIBS) + $(LD) -o $@ ctr-cavstest.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh $(LIBS) +ssh-cavs$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-cavs.o + $(LD) -o $@ ssh-cavs.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) @@ -40,7 +40,7 @@ diff -up openssh-6.8p1/Makefile.in.kdf-cavs openssh-6.8p1/Makefile.in diff -up openssh-6.8p1/ssh-cavs.c.kdf-cavs openssh-6.8p1/ssh-cavs.c --- openssh-6.8p1/ssh-cavs.c.kdf-cavs 2015-03-18 11:23:46.348049354 +0100 +++ openssh-6.8p1/ssh-cavs.c 2015-03-18 11:23:46.348049354 +0100 -@@ -0,0 +1,377 @@ +@@ -0,0 +1,387 @@ +/* + * Copyright (C) 2015, Stephan Mueller + * @@ -208,6 +208,7 @@ diff -up openssh-6.8p1/ssh-cavs.c.kdf-cavs openssh-6.8p1/ssh-cavs.c +{ + int ret = 0; + struct kex kex; ++ struct sshbuf *Kb = NULL; + BIGNUM *Kbn = NULL; + int mode = 0; + struct newkeys *ctoskeys; @@ -222,10 +223,17 @@ diff -up openssh-6.8p1/ssh-cavs.c.kdf-cavs openssh-6.8p1/ssh-cavs.c + Kbn = BN_new(); + BN_bin2bn(test->K, test->Klen, Kbn); + if (!Kbn) { -+ printf("cannot convert K into BIGNUM\n"); ++ printf("cannot convert K into bignum\n"); + ret = 1; + goto out; + } ++ Kb = sshbuf_new(); ++ if (!Kb) { ++ printf("cannot convert K into sshbuf\n"); ++ ret = 1; ++ goto out; ++ } ++ sshbuf_put_bignum2(Kb, Kbn); + + kex.session_id = test->session_id; + kex.session_id_len = test->session_id_len; @@ -285,7 +293,7 @@ diff -up openssh-6.8p1/ssh-cavs.c.kdf-cavs openssh-6.8p1/ssh-cavs.c + goto out; + } + ssh->kex = &kex; -+ kex_derive_keys_bn(ssh, test->H, test->Hlen, Kbn); ++ kex_derive_keys(ssh, test->H, test->Hlen, Kb); + + ctoskeys = kex.newkeys[0]; + stockeys = kex.newkeys[1]; @@ -321,6 +329,8 @@ diff -up openssh-6.8p1/ssh-cavs.c.kdf-cavs openssh-6.8p1/ssh-cavs.c +out: + if (Kbn) + BN_free(Kbn); ++ if (Kb) ++ sshbuf_free(Kb); + if (ssh) + ssh_packet_close(ssh); + return ret; diff --git a/openssh-6.7p1-ldap.patch b/openssh-6.7p1-ldap.patch index ee44fdb..e5de1bb 100644 --- a/openssh-6.7p1-ldap.patch +++ b/openssh-6.7p1-ldap.patch @@ -171,7 +171,7 @@ diff -up openssh-6.8p1/Makefile.in.ldap openssh-6.8p1/Makefile.in $(LD) -o $@ ssh-pkcs11-helper.o ssh-pkcs11.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) +ssh-ldap-helper$(EXEEXT): $(LIBCOMPAT) libssh.a ldapconf.o ldapbody.o ldapmisc.o ldap-helper.o -+ $(LD) -o $@ ldapconf.o ldapbody.o ldapmisc.o ldap-helper.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh -lopenbsd-compat -lfipscheck $(LIBS) $(LDAPLIBS) ++ $(LD) -o $@ ldapconf.o ldapbody.o ldapmisc.o ldap-helper.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) $(LDAPLIBS) + ssh-keyscan$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-keyscan.o $(LD) -o $@ ssh-keyscan.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh $(LIBS) @@ -331,7 +331,7 @@ diff -up openssh-6.8p1/configure.ac.ldap openssh-6.8p1/configure.ac + [ac_cv_ldap_set_rebind_proc=3], + [ac_cv_ldap_set_rebind_proc=2]) + AC_MSG_RESULT($ac_cv_ldap_set_rebind_proc) -+ AC_DEFINE(LDAP_SET_REBIND_PROC_ARGS, $ac_cv_ldap_set_rebind_proc, [number arguments of ldap_set_rebind_proc]) ++ AC_DEFINE_UNQUOTED(LDAP_SET_REBIND_PROC_ARGS, $ac_cv_ldap_set_rebind_proc, [number arguments of ldap_set_rebind_proc]) + ) + LIBS="$saved_LIBS" + fi @@ -646,7 +646,7 @@ diff -up openssh-6.8p1/ldap.conf.ldap openssh-6.8p1/ldap.conf diff -up openssh-6.8p1/ldapbody.c.ldap openssh-6.8p1/ldapbody.c --- openssh-6.8p1/ldapbody.c.ldap 2015-03-18 11:11:29.031801462 +0100 +++ openssh-6.8p1/ldapbody.c 2015-03-18 11:11:29.031801462 +0100 -@@ -0,0 +1,494 @@ +@@ -0,0 +1,499 @@ +/* $OpenBSD: ldapbody.c,v 1.1 2009/12/03 03:34:42 jfch Exp $ */ +/* + * Copyright (c) 2009 Jan F. Chadima. All rights reserved. @@ -708,7 +708,11 @@ diff -up openssh-6.8p1/ldapbody.c.ldap openssh-6.8p1/ldapbody.c + +#if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000) +static int ++#if LDAP_API_VERSION > 3000 ++_rebind_proc (LDAP * ld, LDAP_CONST char *url, ber_tag_t request, ber_int_t msgid, void *params) ++#else +_rebind_proc (LDAP * ld, LDAP_CONST char *url, int request, ber_int_t msgid) ++#endif +{ + struct timeval timeout; + int rc; diff --git a/openssh-6.7p1-sftp-force-permission.patch b/openssh-6.7p1-sftp-force-permission.patch index 2d6e730..c6a28e5 100644 --- a/openssh-6.7p1-sftp-force-permission.patch +++ b/openssh-6.7p1-sftp-force-permission.patch @@ -1,6 +1,6 @@ -diff -up openssh-7.2p2/sftp-server.8.sftp-force-mode openssh-7.2p2/sftp-server.8 ---- openssh-7.2p2/sftp-server.8.sftp-force-mode 2016-03-09 19:04:48.000000000 +0100 -+++ openssh-7.2p2/sftp-server.8 2016-06-23 16:18:20.463854117 +0200 +diff --color -ru a/sftp-server.8 b/sftp-server.8 +--- a/sftp-server.8 2019-04-18 00:52:57.000000000 +0200 ++++ b/sftp-server.8 2022-06-20 16:03:47.892540068 +0200 @@ -38,6 +38,7 @@ .Op Fl P Ar blacklisted_requests .Op Fl p Ar whitelisted_requests @@ -9,21 +9,23 @@ diff -up openssh-7.2p2/sftp-server.8.sftp-force-mode openssh-7.2p2/sftp-server.8 .Ek .Nm .Fl Q Ar protocol_feature -@@ -138,6 +139,10 @@ Sets an explicit +@@ -138,6 +139,12 @@ .Xr umask 2 to be applied to newly-created files and directories, instead of the user's default mask. +.It Fl m Ar force_file_perms +Sets explicit file permissions to be applied to newly-created files instead +of the default or client requested mode. Numeric values include: -+777, 755, 750, 666, 644, 640, etc. Option -u is ineffective if -m is set. ++777, 755, 750, 666, 644, 640, etc. Using both -m and -u switches makes the ++umask (-u) effective only for newly created directories and explicit mode (-m) ++for newly created files. .El .Pp On some systems, -diff -up openssh-7.2p2/sftp-server.c.sftp-force-mode openssh-7.2p2/sftp-server.c ---- openssh-7.2p2/sftp-server.c.sftp-force-mode 2016-06-23 16:18:20.446854128 +0200 -+++ openssh-7.2p2/sftp-server.c 2016-06-23 16:20:37.950766082 +0200 -@@ -69,6 +69,10 @@ struct sshbuf *oqueue; +diff --color -ru a/sftp-server.c b/sftp-server.c +--- a/sftp-server.c 2022-06-20 16:01:26.183793633 +0200 ++++ b/sftp-server.c 2022-06-20 16:02:12.442690608 +0200 +@@ -65,6 +65,10 @@ /* Version of client */ static u_int version; @@ -34,7 +36,7 @@ diff -up openssh-7.2p2/sftp-server.c.sftp-force-mode openssh-7.2p2/sftp-server.c /* SSH2_FXP_INIT received */ static int init_done; -@@ -683,6 +687,7 @@ process_open(u_int32_t id) +@@ -683,6 +687,7 @@ Attrib a; char *name; int r, handle, fd, flags, mode, status = SSH2_FX_FAILURE; @@ -42,7 +44,7 @@ diff -up openssh-7.2p2/sftp-server.c.sftp-force-mode openssh-7.2p2/sftp-server.c if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0 || (r = sshbuf_get_u32(iqueue, &pflags)) != 0 || /* portable flags */ -@@ -692,6 +697,10 @@ process_open(u_int32_t id) +@@ -692,6 +697,10 @@ debug3("request %u: open flags %d", id, pflags); flags = flags_from_portable(pflags); mode = (a.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a.perm : 0666; @@ -53,7 +55,7 @@ diff -up openssh-7.2p2/sftp-server.c.sftp-force-mode openssh-7.2p2/sftp-server.c logit("open \"%s\" flags %s mode 0%o", name, string_from_portable(pflags), mode); if (readonly && -@@ -713,6 +722,8 @@ process_open(u_int32_t id) +@@ -713,6 +722,8 @@ } } } @@ -62,7 +64,7 @@ diff -up openssh-7.2p2/sftp-server.c.sftp-force-mode openssh-7.2p2/sftp-server.c if (status != SSH2_FX_OK) send_status(id, status); free(name); -@@ -1494,7 +1505,7 @@ sftp_server_usage(void) +@@ -1555,7 +1566,7 @@ fprintf(stderr, "usage: %s [-ehR] [-d start_directory] [-f log_facility] " "[-l log_level]\n\t[-P blacklisted_requests] " @@ -71,7 +73,7 @@ diff -up openssh-7.2p2/sftp-server.c.sftp-force-mode openssh-7.2p2/sftp-server.c " %s -Q protocol_feature\n", __progname, __progname); exit(1); -@@ -1520,7 +1531,7 @@ sftp_server_main(int argc, char **argv, +@@ -1581,7 +1592,7 @@ pw = pwcopy(user_pw); while (!skipargs && (ch = getopt(argc, argv, @@ -80,7 +82,7 @@ diff -up openssh-7.2p2/sftp-server.c.sftp-force-mode openssh-7.2p2/sftp-server.c switch (ch) { case 'Q': if (strcasecmp(optarg, "requests") != 0) { -@@ -1580,6 +1591,15 @@ sftp_server_main(int argc, char **argv, +@@ -1643,6 +1654,15 @@ fatal("Invalid umask \"%s\"", optarg); (void)umask((mode_t)mask); break; diff --git a/openssh-6.8p1-sshdT-output.patch b/openssh-6.8p1-sshdT-output.patch index 7a843e7..ac9169a 100644 --- a/openssh-6.8p1-sshdT-output.patch +++ b/openssh-6.8p1-sshdT-output.patch @@ -10,18 +10,3 @@ diff -up openssh/servconf.c.sshdt openssh/servconf.c dump_cfg_string(sForceCommand, o->adm_forced_command); dump_cfg_string(sChrootDirectory, o->chroot_directory); dump_cfg_string(sTrustedUserCAKeys, o->trusted_user_ca_keys); -diff -up openssh/ssh.1.sshdt openssh/ssh.1 ---- openssh/ssh.1.sshdt 2015-06-24 11:42:19.565102807 +0200 -+++ openssh/ssh.1 2015-06-24 11:42:29.042078701 +0200 -@@ -441,7 +441,11 @@ For full details of the options listed b - .It GatewayPorts - .It GlobalKnownHostsFile - .It GSSAPIAuthentication -+.It GSSAPIKeyExchange -+.It GSSAPIClientIdentity - .It GSSAPIDelegateCredentials -+.It GSSAPIRenewalForcesRekey -+.It GSSAPITrustDNS - .It HashKnownHosts - .It Host - .It HostbasedAuthentication diff --git a/openssh-7.0p1-gssKexAlgorithms.patch b/openssh-7.0p1-gssKexAlgorithms.patch deleted file mode 100644 index 2ba360e..0000000 --- a/openssh-7.0p1-gssKexAlgorithms.patch +++ /dev/null @@ -1,422 +0,0 @@ -diff -up openssh-7.0p1/gss-genr.c.gsskexalg openssh-7.0p1/gss-genr.c ---- openssh-7.0p1/gss-genr.c.gsskexalg 2015-08-19 12:28:38.024518959 +0200 -+++ openssh-7.0p1/gss-genr.c 2015-08-19 12:28:38.078518839 +0200 -@@ -78,7 +78,8 @@ ssh_gssapi_oid_table_ok() { - */ - - char * --ssh_gssapi_client_mechanisms(const char *host, const char *client) { -+ssh_gssapi_client_mechanisms(const char *host, const char *client, -+ const char *kex) { - gss_OID_set gss_supported; - OM_uint32 min_status; - -@@ -86,12 +87,12 @@ ssh_gssapi_client_mechanisms(const char - return NULL; - - return(ssh_gssapi_kex_mechs(gss_supported, ssh_gssapi_check_mechanism, -- host, client)); -+ host, client, kex)); - } - - char * - ssh_gssapi_kex_mechs(gss_OID_set gss_supported, ssh_gssapi_check_fn *check, -- const char *host, const char *client) { -+ const char *host, const char *client, const char *kex) { - struct sshbuf *buf; - size_t i; - int oidpos, enclen, r; -@@ -100,6 +101,7 @@ ssh_gssapi_kex_mechs(gss_OID_set gss_sup - char deroid[2]; - const EVP_MD *evp_md = EVP_md5(); - EVP_MD_CTX md; -+ char *s, *cp, *p; - - if (gss_enc2oid != NULL) { - for (i = 0; gss_enc2oid[i].encoded != NULL; i++) -@@ -113,6 +115,7 @@ ssh_gssapi_kex_mechs(gss_OID_set gss_sup - fatal("%s: sshbuf_new failed", __func__); - - oidpos = 0; -+ s = cp = xstrdup(kex); - for (i = 0; i < gss_supported->count; i++) { - if (gss_supported->elements[i].length < 128 && - (*check)(NULL, &(gss_supported->elements[i]), host, client)) { -@@ -131,28 +134,25 @@ ssh_gssapi_kex_mechs(gss_OID_set gss_sup - enclen = __b64_ntop(digest, EVP_MD_size(evp_md), - encoded, EVP_MD_size(evp_md) * 2); - -- if (oidpos != 0) -- if ((r = sshbuf_put_u8(buf, ',')) != 0) -- fatal("%s: buffer error: %s", __func__, ssh_err(r)); -- -- if ((r = sshbuf_put(buf, KEX_GSS_GEX_SHA1_ID, -- sizeof(KEX_GSS_GEX_SHA1_ID) - 1)) != 0 || -- (r = sshbuf_put(buf, encoded, enclen)) != 0 || -- (r = sshbuf_put_u8(buf, ',')) != 0 || -- (r = sshbuf_put(buf, KEX_GSS_GRP1_SHA1_ID, -- sizeof(KEX_GSS_GRP1_SHA1_ID) - 1)) != 0 || -- (r = sshbuf_put(buf, encoded, enclen)) != 0 || -- (r = sshbuf_put_u8(buf, ',')) != 0 || -- (r = sshbuf_put(buf, KEX_GSS_GRP14_SHA1_ID, -- sizeof(KEX_GSS_GRP14_SHA1_ID) - 1)) != 0 || -- (r = sshbuf_put(buf, encoded, enclen)) != 0) -- fatal("%s: buffer error: %s", __func__, ssh_err(r)); -+ cp = strncpy(s, kex, strlen(kex)); -+ for ((p = strsep(&cp, ",")); p && *p != '\0'; -+ (p = strsep(&cp, ","))) { -+ if (sshbuf_len(buf) != 0) -+ if ((r = sshbuf_put_u8(buf, ',')) != 0) -+ fatal("%s: buffer error: %s", -+ __func__, ssh_err(r)); -+ if ((r = sshbuf_put(buf, p, strlen(p))) != 0 || -+ (r = sshbuf_put(buf, encoded, enclen)) != 0) -+ fatal("%s: buffer error: %s", -+ __func__, ssh_err(r)); -+ } - - gss_enc2oid[oidpos].oid = &(gss_supported->elements[i]); - gss_enc2oid[oidpos].encoded = encoded; - oidpos++; - } - } -+ free(s); - gss_enc2oid[oidpos].oid = NULL; - gss_enc2oid[oidpos].encoded = NULL; - -diff -up openssh-7.0p1/gss-serv.c.gsskexalg openssh-7.0p1/gss-serv.c ---- openssh-7.0p1/gss-serv.c.gsskexalg 2015-08-19 12:28:38.024518959 +0200 -+++ openssh-7.0p1/gss-serv.c 2015-08-19 12:28:38.078518839 +0200 -@@ -149,7 +149,8 @@ ssh_gssapi_server_mechanisms() { - if (supported_oids == NULL) - ssh_gssapi_prepare_supported_oids(); - return (ssh_gssapi_kex_mechs(supported_oids, -- &ssh_gssapi_server_check_mech, NULL, NULL)); -+ &ssh_gssapi_server_check_mech, NULL, NULL, -+ options.gss_kex_algorithms)); - } - - /* Unprivileged */ -diff -up openssh-7.0p1/kex.c.gsskexalg openssh-7.0p1/kex.c ---- openssh-7.0p1/kex.c.gsskexalg 2015-08-19 12:28:38.078518839 +0200 -+++ openssh-7.0p1/kex.c 2015-08-19 12:30:13.249306371 +0200 -@@ -50,6 +50,7 @@ - #include "misc.h" - #include "dispatch.h" - #include "monitor.h" -+#include "xmalloc.h" - - #include "ssherr.h" - #include "sshbuf.h" -@@ -232,6 +232,29 @@ kex_assemble_names(const char *def, char - return r; - } - -+/* Validate GSS KEX method name list */ -+int -+gss_kex_names_valid(const char *names) -+{ -+ char *s, *cp, *p; -+ -+ if (names == NULL || *names == '\0') -+ return 0; -+ s = cp = xstrdup(names); -+ for ((p = strsep(&cp, ",")); p && *p != '\0'; -+ (p = strsep(&cp, ","))) { -+ if (strncmp(p, "gss-", 4) != 0 -+ || kex_alg_by_name(p) == NULL) { -+ error("Unsupported KEX algorithm \"%.100s\"", p); -+ free(s); -+ return 0; -+ } -+ } -+ debug3("gss kex names ok: [%s]", names); -+ free(s); -+ return 1; -+} -+ - /* put algorithm proposal into buffer */ - int - kex_prop2buf(struct sshbuf *b, char *proposal[PROPOSAL_MAX]) -diff -up openssh-7.0p1/kex.h.gsskexalg openssh-7.0p1/kex.h ---- openssh-7.0p1/kex.h.gsskexalg 2015-08-19 12:28:38.078518839 +0200 -+++ openssh-7.0p1/kex.h 2015-08-19 12:30:52.404218958 +0200 -@@ -173,6 +173,7 @@ 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 *); -+int gss_kex_names_valid(const char *); - - int kex_new(struct ssh *, char *[PROPOSAL_MAX], struct kex **); - int kex_setup(struct ssh *, char *[PROPOSAL_MAX]); -diff -up openssh-7.0p1/readconf.c.gsskexalg openssh-7.0p1/readconf.c ---- openssh-7.0p1/readconf.c.gsskexalg 2015-08-19 12:28:38.026518955 +0200 -+++ openssh-7.0p1/readconf.c 2015-08-19 12:31:28.333138747 +0200 -@@ -61,6 +61,7 @@ - #include "uidswap.h" - #include "myproposal.h" - #include "digest.h" -+#include "ssh-gss.h" - - /* Format of the configuration file: - -@@ -148,7 +149,7 @@ typedef enum { - oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout, - oAddressFamily, oGssAuthentication, oGssDelegateCreds, - oGssTrustDns, oGssKeyEx, oGssClientIdentity, oGssRenewalRekey, -- oGssServerIdentity, -+ oGssServerIdentity, oGssKexAlgorithms, - oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly, - oSendEnv, oSetEnv, oControlPath, oControlMaster, oControlPersist, - oHashKnownHosts, -@@ -200,6 +201,7 @@ static struct { - { "gssapiclientidentity", oGssClientIdentity }, - { "gssapiserveridentity", oGssServerIdentity }, - { "gssapirenewalforcesrekey", oGssRenewalRekey }, -+ { "gssapikexalgorithms", oGssKexAlgorithms }, - # else - { "gssapiauthentication", oUnsupported }, - { "gssapikeyexchange", oUnsupported }, -@@ -207,6 +209,7 @@ static struct { - { "gssapitrustdns", oUnsupported }, - { "gssapiclientidentity", oUnsupported }, - { "gssapirenewalforcesrekey", oUnsupported }, -+ { "gssapikexalgorithms", oUnsupported }, - #endif - #ifdef ENABLE_PKCS11 - { "smartcarddevice", oPKCS11Provider }, -@@ -929,6 +932,18 @@ parse_time: - intptr = &options->gss_renewal_rekey; - goto parse_flag; - -+ case oGssKexAlgorithms: -+ arg = strdelim(&s); -+ if (!arg || *arg == '\0') -+ fatal("%.200s line %d: Missing argument.", -+ filename, linenum); -+ if (!gss_kex_names_valid(arg)) -+ fatal("%.200s line %d: Bad GSSAPI KexAlgorithms '%s'.", -+ filename, linenum, arg ? arg : ""); -+ if (*activep && options->gss_kex_algorithms == NULL) -+ options->gss_kex_algorithms = xstrdup(arg); -+ break; -+ - case oBatchMode: - intptr = &options->batch_mode; - goto parse_flag; -@@ -1638,6 +1653,7 @@ initialize_options(Options * options) - options->gss_renewal_rekey = -1; - options->gss_client_identity = NULL; - options->gss_server_identity = NULL; -+ options->gss_kex_algorithms = NULL; - options->password_authentication = -1; - options->kbd_interactive_authentication = -1; - options->kbd_interactive_devices = NULL; -@@ -1773,6 +1789,10 @@ fill_default_options(Options * options) - options->gss_trust_dns = 0; - if (options->gss_renewal_rekey == -1) - options->gss_renewal_rekey = 0; -+#ifdef GSSAPI -+ if (options->gss_kex_algorithms == NULL) -+ options->gss_kex_algorithms = strdup(GSS_KEX_DEFAULT_KEX); -+#endif - if (options->password_authentication == -1) - options->password_authentication = 1; - if (options->kbd_interactive_authentication == -1) -diff -up openssh-7.0p1/readconf.h.gsskexalg openssh-7.0p1/readconf.h ---- openssh-7.0p1/readconf.h.gsskexalg 2015-08-19 12:28:38.026518955 +0200 -+++ openssh-7.0p1/readconf.h 2015-08-19 12:28:38.079518836 +0200 -@@ -51,6 +51,7 @@ typedef struct { - int gss_renewal_rekey; /* Credential renewal forces rekey */ - char *gss_client_identity; /* Principal to initiate GSSAPI with */ - char *gss_server_identity; /* GSSAPI target principal */ -+ char *gss_kex_algorithms; /* GSSAPI kex methods to be offered by client. */ - int password_authentication; /* Try password - * authentication. */ - int kbd_interactive_authentication; /* Try keyboard-interactive auth. */ -diff -up openssh-7.0p1/servconf.c.gsskexalg openssh-7.0p1/servconf.c ---- openssh-7.0p1/servconf.c.gsskexalg 2015-08-19 12:28:38.074518847 +0200 -+++ openssh-7.0p1/servconf.c 2015-08-19 12:33:13.599902732 +0200 -@@ -57,6 +57,7 @@ - #include "auth.h" - #include "myproposal.h" - #include "digest.h" -+#include "ssh-gss.h" - - static void add_listen_addr(ServerOptions *, const char *, - const char *, int); -@@ -121,6 +122,7 @@ initialize_server_options(ServerOptions - options->gss_cleanup_creds = -1; - options->gss_strict_acceptor = -1; - options->gss_store_rekey = -1; -+ options->gss_kex_algorithms = NULL; - options->use_kuserok = -1; - options->enable_k5users = -1; - options->password_authentication = -1; -@@ -288,6 +290,10 @@ fill_default_server_options(ServerOption - options->gss_strict_acceptor = 1; - if (options->gss_store_rekey == -1) - options->gss_store_rekey = 0; -+#ifdef GSSAPI -+ if (options->gss_kex_algorithms == NULL) -+ options->gss_kex_algorithms = strdup(GSS_KEX_DEFAULT_KEX); -+#endif - if (options->use_kuserok == -1) - options->use_kuserok = 1; - if (options->enable_k5users == -1) -@@ -427,7 +431,7 @@ typedef enum { - sHostKeyAlgorithms, - sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile, - sGssAuthentication, sGssCleanupCreds, sGssEnablek5users, sGssStrictAcceptor, -- sGssKeyEx, sGssStoreRekey, sAcceptEnv, sSetEnv, sPermitTunnel, -+ sGssKeyEx, sGssStoreRekey, sGssKexAlgorithms, sAcceptEnv, sSetEnv, sPermitTunnel, - sMatch, sPermitOpen, sPermitListen, sForceCommand, sChrootDirectory, - sUsePrivilegeSeparation, sAllowAgentForwarding, - sHostCertificate, -@@ -506,6 +510,7 @@ static struct { - { "gssapikeyexchange", sGssKeyEx, SSHCFG_GLOBAL }, - { "gssapistorecredentialsonrekey", sGssStoreRekey, SSHCFG_GLOBAL }, - { "gssapienablek5users", sGssEnablek5users, SSHCFG_ALL }, -+ { "gssapikexalgorithms", sGssKexAlgorithms, SSHCFG_GLOBAL }, - #else - { "gssapiauthentication", sUnsupported, SSHCFG_ALL }, - { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL }, -@@ -513,6 +518,7 @@ static struct { - { "gssapikeyexchange", sUnsupported, SSHCFG_GLOBAL }, - { "gssapistorecredentialsonrekey", sUnsupported, SSHCFG_GLOBAL }, - { "gssapienablek5users", sUnsupported, SSHCFG_ALL }, -+ { "gssapikexalgorithms", sUnsupported, SSHCFG_GLOBAL }, - #endif - { "gssusesessionccache", sUnsupported, SSHCFG_GLOBAL }, - { "gssapiusesessioncredcache", sUnsupported, SSHCFG_GLOBAL }, -@@ -1273,6 +1279,18 @@ process_server_config_line(ServerOptions - intptr = &options->gss_store_rekey; - goto parse_flag; - -+ case sGssKexAlgorithms: -+ arg = strdelim(&cp); -+ if (!arg || *arg == '\0') -+ fatal("%.200s line %d: Missing argument.", -+ filename, linenum); -+ if (!gss_kex_names_valid(arg)) -+ fatal("%.200s line %d: Bad GSSAPI KexAlgorithms '%s'.", -+ filename, linenum, arg ? arg : ""); -+ if (*activep && options->gss_kex_algorithms == NULL) -+ options->gss_kex_algorithms = xstrdup(arg); -+ break; -+ - case sPasswordAuthentication: - intptr = &options->password_authentication; - goto parse_flag; -@@ -2304,6 +2322,7 @@ dump_config(ServerOptions *o) - dump_cfg_fmtint(sGssKeyEx, o->gss_keyex); - dump_cfg_fmtint(sGssStrictAcceptor, o->gss_strict_acceptor); - dump_cfg_fmtint(sGssStoreRekey, o->gss_store_rekey); -+ dump_cfg_string(sGssKexAlgorithms, o->gss_kex_algorithms); - #endif - dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication); - dump_cfg_fmtint(sKbdInteractiveAuthentication, -diff -up openssh-7.0p1/servconf.h.gsskexalg openssh-7.0p1/servconf.h ---- openssh-7.0p1/servconf.h.gsskexalg 2015-08-19 12:28:38.080518834 +0200 -+++ openssh-7.0p1/servconf.h 2015-08-19 12:34:46.328693944 +0200 -@@ -122,6 +122,7 @@ typedef struct { - int gss_cleanup_creds; /* If true, destroy cred cache on logout */ - int gss_strict_acceptor; /* If true, restrict the GSSAPI acceptor name */ - int gss_store_rekey; -+ char *gss_kex_algorithms; /* GSSAPI kex methods to be offered by client. */ - int password_authentication; /* If true, permit password - * authentication. */ - int kbd_interactive_authentication; /* If true, permit */ -diff -up openssh-7.0p1/ssh.1.gsskexalg openssh-7.0p1/ssh.1 ---- openssh-7.0p1/ssh.1.gsskexalg 2015-08-19 12:28:38.081518832 +0200 -+++ openssh-7.0p1/ssh.1 2015-08-19 12:35:31.741591692 +0200 -@@ -496,6 +496,7 @@ For full details of the options listed b - .It GSSAPIDelegateCredentials - .It GSSAPIRenewalForcesRekey - .It GSSAPITrustDNS -+.It GSSAPIKexAlgorithms - .It HashKnownHosts - .It Host - .It HostbasedAuthentication -diff -up openssh-7.0p1/ssh_config.5.gsskexalg openssh-7.0p1/ssh_config.5 ---- openssh-7.0p1/ssh_config.5.gsskexalg 2015-08-19 12:28:38.028518950 +0200 -+++ openssh-7.0p1/ssh_config.5 2015-08-19 12:28:38.082518830 +0200 -@@ -786,6 +786,18 @@ command line will be passed untouched to - command line will be passed untouched to the GSSAPI library. - The default is - .Dq no . -+.It Cm GSSAPIKexAlgorithms -+The list of key exchange algorithms that are offered for GSSAPI -+key exchange. Possible values are -+.Bd -literal -offset 3n -+gss-gex-sha1-, -+gss-group1-sha1-, -+gss-group14-sha1- -+.Ed -+.Pp -+The default is -+.Dq gss-gex-sha1-,gss-group14-sha1- . -+This option only applies to protocol version 2 connections using GSSAPI. - .It Cm HashKnownHosts - Indicates that - .Xr ssh 1 -diff -up openssh-7.0p1/sshconnect2.c.gsskexalg openssh-7.0p1/sshconnect2.c ---- openssh-7.0p1/sshconnect2.c.gsskexalg 2015-08-19 12:28:38.045518912 +0200 -+++ openssh-7.0p1/sshconnect2.c 2015-08-19 12:28:38.081518832 +0200 -@@ -179,7 +179,8 @@ ssh_kex2(char *host, struct sockaddr *ho - else - gss_host = host; - -- gss = ssh_gssapi_client_mechanisms(gss_host, options.gss_client_identity); -+ gss = ssh_gssapi_client_mechanisms(gss_host, -+ options.gss_client_identity, options.gss_kex_algorithms); - if (gss) { - debug("Offering GSSAPI proposal: %s", gss); - xasprintf(&options.kex_algorithms, ---- openssh-7.1p1/sshd_config.5.gsskexalg 2015-12-10 15:32:48.105418092 +0100 -+++ openssh-7.1p1/sshd_config.5 2015-12-10 15:33:47.771279548 +0100 -@@ -663,6 +663,18 @@ or updated credentials from a compatible - For this to work - .Cm GSSAPIKeyExchange - needs to be enabled in the server and also used by the client. -+.It Cm GSSAPIKexAlgorithms -+The list of key exchange algorithms that are accepted by GSSAPI -+key exchange. Possible values are -+.Bd -literal -offset 3n -+gss-gex-sha1-, -+gss-group1-sha1-, -+gss-group14-sha1- -+.Ed -+.Pp -+The default is -+.Dq gss-gex-sha1-,gss-group14-sha1- . -+This option only applies to protocol version 2 connections using GSSAPI. - .It Cm HostbasedAcceptedKeyTypes - Specifies the key types that will be accepted for hostbased authentication - as a list of comma-separated patterns. -diff -up openssh-7.0p1/ssh-gss.h.gsskexalg openssh-7.0p1/ssh-gss.h ---- openssh-7.0p1/ssh-gss.h.gsskexalg 2015-08-19 12:28:38.031518944 +0200 -+++ openssh-7.0p1/ssh-gss.h 2015-08-19 12:28:38.081518832 +0200 -@@ -76,6 +76,10 @@ extern char **k5users_allowed_cmds; - #define KEX_GSS_GRP14_SHA1_ID "gss-group14-sha1-" - #define KEX_GSS_GEX_SHA1_ID "gss-gex-sha1-" - -+#define GSS_KEX_DEFAULT_KEX \ -+ KEX_GSS_GEX_SHA1_ID "," \ -+ KEX_GSS_GRP14_SHA1_ID -+ - typedef struct { - char *envvar; - char *envval; -@@ -147,9 +151,9 @@ int ssh_gssapi_credentials_updated(Gssct - /* In the server */ - typedef int ssh_gssapi_check_fn(Gssctxt **, gss_OID, const char *, - const char *); --char *ssh_gssapi_client_mechanisms(const char *, const char *); -+char *ssh_gssapi_client_mechanisms(const char *, const char *, const char *); - char *ssh_gssapi_kex_mechs(gss_OID_set, ssh_gssapi_check_fn *, const char *, -- const char *); -+ const char *, const char *); - gss_OID ssh_gssapi_id_kex(Gssctxt *, char *, int); - int ssh_gssapi_server_check_mech(Gssctxt **,gss_OID, const char *, - const char *); diff --git a/openssh-7.1p1-gssapi-documentation.patch b/openssh-7.1p1-gssapi-documentation.patch deleted file mode 100644 index db689d4..0000000 --- a/openssh-7.1p1-gssapi-documentation.patch +++ /dev/null @@ -1,52 +0,0 @@ -diff -up openssh-7.4p1/ssh_config.5.gss-docs openssh-7.4p1/ssh_config.5 ---- openssh-7.4p1/ssh_config.5.gss-docs 2016-12-23 14:28:34.051714486 +0100 -+++ openssh-7.4p1/ssh_config.5 2016-12-23 14:34:24.568522417 +0100 -@@ -765,10 +765,19 @@ The default is - If set to - .Dq yes - then renewal of the client's GSSAPI credentials will force the rekeying of the --ssh connection. With a compatible server, this can delegate the renewed -+ssh connection. With a compatible server, this will delegate the renewed - credentials to a session on the server. -+.Pp -+Checks are made to ensure that credentials are only propagated when the new -+credentials match the old ones on the originating client and where the -+receiving server still has the old set in its cache. -+.Pp - The default is - .Dq no . -+.Pp -+For this to work -+.Cm GSSAPIKeyExchange -+needs to be enabled in the server and also used by the client. - .It Cm GSSAPIServerIdentity - If set, specifies the GSSAPI server identity that ssh should expect when - connecting to the server. The default is unset, which means that the -@@ -776,9 +785,11 @@ expected GSSAPI server identity will be - hostname. - .It Cm GSSAPITrustDns - Set to --.Dq yes to indicate that the DNS is trusted to securely canonicalize -+.Dq yes -+to indicate that the DNS is trusted to securely canonicalize - the name of the host being connected to. If --.Dq no, the hostname entered on the -+.Dq no , -+the hostname entered on the - command line will be passed untouched to the GSSAPI library. - The default is - .Dq no . -diff -up openssh-7.4p1/sshd_config.5.gss-docs openssh-7.4p1/sshd_config.5 ---- openssh-7.4p1/sshd_config.5.gss-docs 2016-12-23 14:28:34.043714490 +0100 -+++ openssh-7.4p1/sshd_config.5 2016-12-23 14:28:34.051714486 +0100 -@@ -652,6 +652,10 @@ Controls whether the user's GSSAPI crede - successful connection rekeying. This option can be used to accepted renewed - or updated credentials from a compatible client. The default is - .Dq no . -+.Pp -+For this to work -+.Cm GSSAPIKeyExchange -+needs to be enabled in the server and also used by the client. - .It Cm HostbasedAcceptedKeyTypes - Specifies the key types that will be accepted for hostbased authentication - as a list of comma-separated patterns. diff --git a/openssh-7.1p2-audit-race-condition.patch b/openssh-7.1p2-audit-race-condition.patch index 9a0d917..9c9a680 100644 --- a/openssh-7.1p2-audit-race-condition.patch +++ b/openssh-7.1p2-audit-race-condition.patch @@ -56,9 +56,9 @@ diff -up openssh-7.4p1/monitor_wrap.h.audit-race openssh-7.4p1/monitor_wrap.h --- openssh-7.4p1/monitor_wrap.h.audit-race 2016-12-23 16:35:52.694685771 +0100 +++ openssh-7.4p1/monitor_wrap.h 2016-12-23 16:35:52.698685772 +0100 @@ -83,6 +83,8 @@ void mm_audit_unsupported_body(int); - void mm_audit_kex_body(int, char *, char *, char *, char *, pid_t, uid_t); - void mm_audit_session_key_free_body(int, pid_t, uid_t); - void mm_audit_destroy_sensitive_data(const char *, pid_t, uid_t); + void mm_audit_kex_body(struct ssh *, int, char *, char *, char *, char *, pid_t, uid_t); + void mm_audit_session_key_free_body(struct ssh *, int, pid_t, uid_t); + void mm_audit_destroy_sensitive_data(struct ssh *, const char *, pid_t, uid_t); +int mm_forward_audit_messages(int); +void mm_set_monitor_pipe(int); #endif @@ -82,7 +82,7 @@ diff -up openssh-7.4p1/session.c.audit-race openssh-7.4p1/session.c return 1; } -+void child_destory_sensitive_data(); ++void child_destory_sensitive_data(struct ssh *ssh); + #define USE_PIPES 1 /* @@ -91,7 +91,7 @@ diff -up openssh-7.4p1/session.c.audit-race openssh-7.4p1/session.c close(err[0]); #endif -+ child_destory_sensitive_data(); ++ child_destory_sensitive_data(ssh); + /* Do processing for the child (exec command etc). */ do_child(ssh, s, command); @@ -101,7 +101,7 @@ diff -up openssh-7.4p1/session.c.audit-race openssh-7.4p1/session.c close(ttyfd); + /* Do this early, so we will not block large MOTDs */ -+ child_destory_sensitive_data(); ++ child_destory_sensitive_data(ssh); + /* record login, etc. similar to login(1) */ #ifndef HAVE_OSF_SIA @@ -109,7 +109,7 @@ diff -up openssh-7.4p1/session.c.audit-race openssh-7.4p1/session.c @@ -717,6 +728,8 @@ do_exec(Session *s, const char *command) } if (s->command != NULL && s->ptyfd == -1) - s->command_handle = PRIVSEP(audit_run_command(s->command)); + s->command_handle = PRIVSEP(audit_run_command(ssh, s->command)); + if (pipe(paudit) < 0) + fatal("pipe: %s", strerror(errno)); #endif @@ -141,7 +141,7 @@ diff -up openssh-7.4p1/session.c.audit-race openssh-7.4p1/session.c } +void -+child_destory_sensitive_data() ++child_destory_sensitive_data(struct ssh *ssh) +{ +#ifdef SSH_AUDIT_EVENTS + int pparent = paudit[1]; @@ -152,15 +152,15 @@ diff -up openssh-7.4p1/session.c.audit-race openssh-7.4p1/session.c +#endif + + /* remove hostkey from the child's memory */ -+ destroy_sensitive_data(use_privsep); ++ destroy_sensitive_data(ssh, use_privsep); + /* + * We can audit this, because we hacked the pipe to direct the + * messages over postauth child. But this message requires answer + * which we can't do using one-way pipe. + */ -+ packet_destroy_all(0, 1); ++ packet_destroy_all(ssh, 0, 1); + /* XXX this will clean the rest but should not audit anymore */ -+ /* packet_clear_keys(); */ ++ /* packet_clear_keys(ssh); */ + +#ifdef SSH_AUDIT_EVENTS + /* Notify parent that we are done */ @@ -172,15 +172,15 @@ diff -up openssh-7.4p1/session.c.audit-race openssh-7.4p1/session.c * Performs common processing for the child, such as setting up the * environment, closing extra file descriptors, setting the user and group @@ -1554,13 +1608,6 @@ do_child(Session *s, const char *command - 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(1); -- packet_clear_keys(); +- destroy_sensitive_data(ssh, 1); +- ssh_packet_clear_keys(ssh); - /* Don't audit this - both us and the parent would be talking to the - monitor over a single socket, with no synchronization. */ -- packet_destroy_all(0, 1); +- packet_destroy_all(ssh, 0, 1); - /* Force a password change */ if (s->authctxt->force_pwchange) { diff --git a/openssh-7.2p2-k5login_directory.patch b/openssh-7.2p2-k5login_directory.patch index 09369aa..600117f 100644 --- a/openssh-7.2p2-k5login_directory.patch +++ b/openssh-7.2p2-k5login_directory.patch @@ -2,10 +2,11 @@ diff --git a/auth-krb5.c b/auth-krb5.c index 2b02a04..19b9364 100644 --- a/auth-krb5.c +++ b/auth-krb5.c -@@ -375,6 +375,22 @@ cleanup: - return -1; +@@ -375,5 +375,21 @@ cleanup: + return (krb5_cc_resolve(ctx, ccname, ccache)); + } } - ++ +/* + * Reads k5login_directory option from the krb5.conf + */ @@ -21,10 +22,8 @@ index 2b02a04..19b9364 100644 + return profile_get_string(p, "libdefaults", "k5login_directory", NULL, NULL, + k5login_directory); +} -+ - krb5_error_code - ssh_krb5_get_cctemplate(krb5_context ctx, char **ccname) { - profile_t p; + #endif /* !HEIMDAL */ + #endif /* KRB5 */ diff --git a/auth.h b/auth.h index f9d191c..c432d2f 100644 --- a/auth.h diff --git a/openssh-7.3p1-openssl-1.1.0.patch b/openssh-7.3p1-openssl-1.1.0.patch deleted file mode 100644 index 5c18666..0000000 --- a/openssh-7.3p1-openssl-1.1.0.patch +++ /dev/null @@ -1,2677 +0,0 @@ -diff -up openssh/auth-pam.c.openssl openssh/auth-pam.c ---- openssh/auth-pam.c.openssl 2017-09-26 13:19:31.662248869 +0200 -+++ openssh/auth-pam.c 2017-09-26 13:19:31.793249672 +0200 -@@ -128,6 +128,10 @@ extern u_int utmp_len; - typedef pthread_t sp_pthread_t; - #else - typedef pid_t sp_pthread_t; -+# define pthread_create(a, b, c, d) _ssh_compat_pthread_create(a, b, c, d) -+# define pthread_exit(a) _ssh_compat_pthread_exit(a) -+# define pthread_cancel(a) _ssh_compat_pthread_cancel(a) -+# define pthread_join(a, b) _ssh_compat_pthread_join(a, b) - #endif - - struct pam_ctxt { -diff -up openssh/cipher.c.openssl openssh/cipher.c ---- openssh/cipher.c.openssl 2017-09-26 13:19:31.782249605 +0200 -+++ openssh/cipher.c 2017-09-26 13:27:37.424040367 +0200 -@@ -283,7 +283,7 @@ cipher_init(struct sshcipher_ctx **ccp, - ret = SSH_ERR_ALLOC_FAIL; - goto out; - } -- if (EVP_CipherInit(cc->evp, type, NULL, (u_char *)iv, -+ if (EVP_CipherInit(cc->evp, type, (u_char *)key, (u_char *)iv, - (do_encrypt == CIPHER_ENCRYPT)) == 0) { - ret = SSH_ERR_LIBCRYPTO_ERROR; - goto out; -@@ -301,10 +301,6 @@ cipher_init(struct sshcipher_ctx **ccp, - goto out; - } - } -- if (EVP_CipherInit(cc->evp, NULL, (u_char *)key, NULL, -1) == 0) { -- ret = SSH_ERR_LIBCRYPTO_ERROR; -- goto out; -- } - ret = 0; - #endif /* WITH_OPENSSL */ - out: -@@ -490,7 +486,7 @@ cipher_get_keyiv(struct sshcipher_ctx *c - len, iv)) - return SSH_ERR_LIBCRYPTO_ERROR; - } else -- memcpy(iv, cc->evp->iv, len); -+ memcpy(iv, EVP_CIPHER_CTX_iv(cc->evp), len); - #endif - return 0; - } -@@ -524,14 +520,14 @@ cipher_set_keyiv(struct sshcipher_ctx *c - EVP_CTRL_GCM_SET_IV_FIXED, -1, (void *)iv)) - return SSH_ERR_LIBCRYPTO_ERROR; - } else -- memcpy(cc->evp->iv, iv, evplen); -+ memcpy(EVP_CIPHER_CTX_iv_noconst(cc->evp), iv, evplen); - #endif - return 0; - } - - #ifdef WITH_OPENSSL --#define EVP_X_STATE(evp) (evp)->cipher_data --#define EVP_X_STATE_LEN(evp) (evp)->cipher->ctx_size -+#define EVP_X_STATE(evp) EVP_CIPHER_CTX_get_cipher_data(evp) -+#define EVP_X_STATE_LEN(evp) EVP_CIPHER_impl_ctx_size(EVP_CIPHER_CTX_cipher(evp)) - #endif - - int -diff -up openssh/ctr-cavstest.c.openssl openssh/ctr-cavstest.c ---- openssh/ctr-cavstest.c.openssl 2017-09-26 13:19:31.707249145 +0200 -+++ openssh/ctr-cavstest.c 2017-09-26 13:19:31.794249679 +0200 -@@ -144,7 +144,7 @@ int main (int argc, char *argv[]) - usage(); - } - -- SSLeay_add_all_algorithms(); -+ OpenSSL_add_all_algorithms(); - - c = cipher_by_name(algo); - if (c == NULL) { -diff -up openssh/dh.c.openssl openssh/dh.c ---- openssh/dh.c.openssl 2017-09-19 06:26:43.000000000 +0200 -+++ openssh/dh.c 2017-09-26 13:19:31.794249679 +0200 -@@ -212,14 +212,15 @@ choose_dh(int min, int wantbits, int max - /* diffie-hellman-groupN-sha1 */ - - int --dh_pub_is_valid(DH *dh, BIGNUM *dh_pub) -+dh_pub_is_valid(const DH *dh, const BIGNUM *dh_pub) - { - int i; - int n = BN_num_bits(dh_pub); - int bits_set = 0; - BIGNUM *tmp; -+ const BIGNUM *p; - -- if (dh_pub->neg) { -+ if (BN_is_negative(dh_pub)) { - logit("invalid public DH value: negative"); - return 0; - } -@@ -232,7 +233,8 @@ dh_pub_is_valid(DH *dh, BIGNUM *dh_pub) - error("%s: BN_new failed", __func__); - return 0; - } -- if (!BN_sub(tmp, dh->p, BN_value_one()) || -+ DH_get0_pqg(dh, &p, NULL, NULL); -+ if (!BN_sub(tmp, p, BN_value_one()) || - BN_cmp(dh_pub, tmp) != -1) { /* pub_exp > p-2 */ - BN_clear_free(tmp); - logit("invalid public DH value: >= p-1"); -@@ -243,14 +245,14 @@ dh_pub_is_valid(DH *dh, BIGNUM *dh_pub) - for (i = 0; i <= n; i++) - if (BN_is_bit_set(dh_pub, i)) - bits_set++; -- debug2("bits set: %d/%d", bits_set, BN_num_bits(dh->p)); -+ debug2("bits set: %d/%d", bits_set, BN_num_bits(p)); - - /* - * if g==2 and bits_set==1 then computing log_g(dh_pub) is trivial - */ - if (bits_set < 4) { - logit("invalid public DH value (%d/%d)", -- bits_set, BN_num_bits(dh->p)); -+ bits_set, BN_num_bits(p)); - return 0; - } - return 1; -@@ -260,9 +262,11 @@ int - dh_gen_key(DH *dh, int need) - { - int pbits; -+ const BIGNUM *p, *pub_key; - -- if (need < 0 || dh->p == NULL || -- (pbits = BN_num_bits(dh->p)) <= 0 || -+ DH_get0_pqg(dh, &p, NULL, NULL); -+ if (need < 0 || p == NULL || -+ (pbits = BN_num_bits(p)) <= 0 || - need > INT_MAX / 2 || 2 * need > pbits) - return SSH_ERR_INVALID_ARGUMENT; - if (need < 256) -@@ -271,11 +275,11 @@ dh_gen_key(DH *dh, int need) - * Pollard Rho, Big step/Little Step attacks are O(sqrt(n)), - * so double requested need here. - */ -- dh->length = MINIMUM(need * 2, pbits - 1); -- if (DH_generate_key(dh) == 0 || -- !dh_pub_is_valid(dh, dh->pub_key)) { -- BN_clear_free(dh->priv_key); -- dh->priv_key = NULL; -+ DH_set_length(dh, MINIMUM(need * 2, pbits - 1)); -+ if (DH_generate_key(dh) == 0) -+ return SSH_ERR_LIBCRYPTO_ERROR; -+ DH_get0_key(dh, &pub_key, NULL); -+ if (!dh_pub_is_valid(dh, pub_key)) { - return SSH_ERR_LIBCRYPTO_ERROR; - } - return 0; -@@ -284,15 +289,22 @@ DH * - dh_new_group_asc(const char *gen, const char *modulus) - { - DH *dh; -+ BIGNUM *p = NULL, *g = NULL; - -- if ((dh = DH_new()) == NULL) -- return NULL; -- if (BN_hex2bn(&dh->p, modulus) == 0 || -- BN_hex2bn(&dh->g, gen) == 0) { -- DH_free(dh); -- return NULL; -- } -+ if ((dh = DH_new()) == NULL || -+ (p = BN_new()) == NULL || -+ (g = BN_new()) == NULL) -+ goto err; -+ if (BN_hex2bn(&p, modulus) == 0 || -+ BN_hex2bn(&g, gen) == 0 || -+ DH_set0_pqg(dh, p, NULL, g) == 0) -+ goto err; - return (dh); -+err: -+ DH_free(dh); -+ BN_free(p); -+ BN_free(g); -+ return NULL; - } - - /* -@@ -307,8 +319,7 @@ dh_new_group(BIGNUM *gen, BIGNUM *modulu - - if ((dh = DH_new()) == NULL) - return NULL; -- dh->p = modulus; -- dh->g = gen; -+ DH_set0_pqg(dh, modulus, NULL, gen); - - return (dh); - } -diff -up openssh/dh.h.openssl openssh/dh.h ---- openssh/dh.h.openssl 2017-09-26 13:19:31.783249611 +0200 -+++ openssh/dh.h 2017-09-26 13:19:31.794249679 +0200 -@@ -42,7 +42,7 @@ DH *dh_new_group18(void); - DH *dh_new_group_fallback(int); - - int dh_gen_key(DH *, int); --int dh_pub_is_valid(DH *, BIGNUM *); -+int dh_pub_is_valid(const DH *, const BIGNUM *); - - u_int dh_estimate(int); - -diff -up openssh/digest-openssl.c.openssl openssh/digest-openssl.c ---- openssh/digest-openssl.c.openssl 2017-09-19 06:26:43.000000000 +0200 -+++ openssh/digest-openssl.c 2017-09-26 13:19:31.795249685 +0200 -@@ -43,7 +43,7 @@ - - struct ssh_digest_ctx { - int alg; -- EVP_MD_CTX mdctx; -+ EVP_MD_CTX *mdctx; - }; - - struct ssh_digest { -@@ -106,7 +106,7 @@ ssh_digest_bytes(int alg) - size_t - ssh_digest_blocksize(struct ssh_digest_ctx *ctx) - { -- return EVP_MD_CTX_block_size(&ctx->mdctx); -+ return EVP_MD_CTX_block_size(ctx->mdctx); - } - - struct ssh_digest_ctx * -@@ -118,8 +118,10 @@ ssh_digest_start(int alg) - if (digest == NULL || ((ret = calloc(1, sizeof(*ret))) == NULL)) - return NULL; - ret->alg = alg; -- EVP_MD_CTX_init(&ret->mdctx); -- if (EVP_DigestInit_ex(&ret->mdctx, digest->mdfunc(), NULL) != 1) { -+ ret->mdctx = EVP_MD_CTX_new(); -+ if (ret->mdctx == NULL || -+ EVP_DigestInit_ex(ret->mdctx, digest->mdfunc(), NULL) != 1) { -+ EVP_MD_CTX_free(ret->mdctx); - free(ret); - return NULL; - } -@@ -132,7 +133,7 @@ ssh_digest_copy_state(struct ssh_digest_ - if (from->alg != to->alg) - return SSH_ERR_INVALID_ARGUMENT; - /* we have bcopy-style order while openssl has memcpy-style */ -- if (!EVP_MD_CTX_copy_ex(&to->mdctx, &from->mdctx)) -+ if (!EVP_MD_CTX_copy_ex(to->mdctx, from->mdctx)) - return SSH_ERR_LIBCRYPTO_ERROR; - return 0; - } -@@ -140,7 +141,7 @@ ssh_digest_copy_state(struct ssh_digest_ - int - ssh_digest_update(struct ssh_digest_ctx *ctx, const void *m, size_t mlen) - { -- if (EVP_DigestUpdate(&ctx->mdctx, m, mlen) != 1) -+ if (EVP_DigestUpdate(ctx->mdctx, m, mlen) != 1) - return SSH_ERR_LIBCRYPTO_ERROR; - return 0; - } -@@ -161,7 +162,7 @@ ssh_digest_final(struct ssh_digest_ctx * - return SSH_ERR_INVALID_ARGUMENT; - if (dlen < digest->digest_len) /* No truncation allowed */ - return SSH_ERR_INVALID_ARGUMENT; -- if (EVP_DigestFinal_ex(&ctx->mdctx, d, &l) != 1) -+ if (EVP_DigestFinal_ex(ctx->mdctx, d, &l) != 1) - return SSH_ERR_LIBCRYPTO_ERROR; - if (l != digest->digest_len) /* sanity */ - return SSH_ERR_INTERNAL_ERROR; -@@ -172,7 +173,7 @@ void - ssh_digest_free(struct ssh_digest_ctx *ctx) - { - if (ctx != NULL) { -- EVP_MD_CTX_cleanup(&ctx->mdctx); -+ EVP_MD_CTX_free(ctx->mdctx); - explicit_bzero(ctx, sizeof(*ctx)); - free(ctx); - } -diff -up openssh/entropy.c.openssl openssh/entropy.c ---- openssh/entropy.c.openssl 2017-09-26 13:19:31.783249611 +0200 -+++ openssh/entropy.c 2017-09-26 13:19:31.795249685 +0200 -@@ -218,7 +218,9 @@ seed_rng(void) - "have %lx", (u_long)OPENSSL_VERSION_NUMBER, SSLeay()); - - /* clean the PRNG status when exiting the program */ -+#if OPENSSL_VERSION_NUMBER < 0x10100000L - atexit(RAND_cleanup); -+#endif - - #ifndef OPENSSL_PRNG_ONLY - if (RAND_status() == 1) { -diff -up openssh/gss-genr.c.openssl openssh/gss-genr.c ---- openssh/gss-genr.c.openssl 2017-09-26 13:19:31.773249550 +0200 -+++ openssh/gss-genr.c 2017-09-26 13:19:31.796249691 +0200 -@@ -99,7 +99,7 @@ ssh_gssapi_kex_mechs(gss_OID_set gss_sup - u_char digest[EVP_MAX_MD_SIZE]; - char deroid[2]; - const EVP_MD *evp_md = EVP_md5(); -- EVP_MD_CTX md; -+ EVP_MD_CTX *md; - char *s, *cp, *p; - - if (gss_enc2oid != NULL) { -@@ -113,6 +113,7 @@ ssh_gssapi_kex_mechs(gss_OID_set gss_sup - if ((buf = sshbuf_new()) == NULL) - fatal("%s: sshbuf_new failed", __func__); - -+ md = EVP_MD_CTX_new(); - oidpos = 0; - s = cp = xstrdup(kex); - for (i = 0; i < gss_supported->count; i++) { -@@ -122,12 +123,13 @@ ssh_gssapi_kex_mechs(gss_OID_set gss_sup - deroid[0] = SSH_GSS_OIDTYPE; - deroid[1] = gss_supported->elements[i].length; - -- EVP_DigestInit(&md, evp_md); -- EVP_DigestUpdate(&md, deroid, 2); -- EVP_DigestUpdate(&md, -+ EVP_MD_CTX_reset(md); -+ EVP_DigestInit(md, evp_md); -+ EVP_DigestUpdate(md, deroid, 2); -+ EVP_DigestUpdate(md, - gss_supported->elements[i].elements, - gss_supported->elements[i].length); -- EVP_DigestFinal(&md, digest, NULL); -+ EVP_DigestFinal(md, digest, NULL); - - encoded = xmalloc(EVP_MD_size(evp_md) * 2); - enclen = __b64_ntop(digest, EVP_MD_size(evp_md), -@@ -149,6 +151,7 @@ ssh_gssapi_kex_mechs(gss_OID_set gss_sup - } - } - free(s); -+ EVP_MD_CTX_free(md); - gss_enc2oid[oidpos].oid = NULL; - gss_enc2oid[oidpos].encoded = NULL; - -diff -up openssh/includes.h.openssl openssh/includes.h ---- openssh/includes.h.openssl 2017-09-19 06:26:43.000000000 +0200 -+++ openssh/includes.h 2017-09-26 13:19:31.796249691 +0200 -@@ -166,6 +166,7 @@ - - #ifdef WITH_OPENSSL - #include /* For OPENSSL_VERSION_NUMBER */ -+#include "libcrypto-compat.h" - #endif - - #include "defines.h" -diff -up openssh/kexdhc.c.openssl openssh/kexdhc.c ---- openssh/kexdhc.c.openssl 2017-09-19 06:26:43.000000000 +0200 -+++ openssh/kexdhc.c 2017-09-26 13:19:31.797249697 +0200 -@@ -56,6 +56,7 @@ kexdh_client(struct ssh *ssh) - { - struct kex *kex = ssh->kex; - int r; -+ const BIGNUM *pub_key; - - /* generate and send 'e', client DH public key */ - switch (kex->kex_type) { -@@ -81,21 +82,27 @@ kexdh_client(struct ssh *ssh) - goto out; - } - debug("sending SSH2_MSG_KEXDH_INIT"); -- if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0 || -- (r = sshpkt_start(ssh, SSH2_MSG_KEXDH_INIT)) != 0 || -- (r = sshpkt_put_bignum2(ssh, kex->dh->pub_key)) != 0 || -+ if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0) -+ goto out; -+ DH_get0_key(kex->dh, &pub_key, NULL); -+ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXDH_INIT)) != 0 || -+ (r = sshpkt_put_bignum2(ssh, pub_key)) != 0 || - (r = sshpkt_send(ssh)) != 0) - goto out; - #ifdef DEBUG_KEXDH - DHparams_print_fp(stderr, kex->dh); - fprintf(stderr, "pub= "); -- BN_print_fp(stderr, kex->dh->pub_key); -+ BN_print_fp(stderr, pub_key); - fprintf(stderr, "\n"); - #endif - debug("expecting SSH2_MSG_KEXDH_REPLY"); - ssh_dispatch_set(ssh, SSH2_MSG_KEXDH_REPLY, &input_kex_dh); - r = 0; - out: -+ if (r != 0) { -+ DH_free(kex->dh); -+ kex->dh = NULL; -+ } - return r; - } - -@@ -109,6 +116,7 @@ input_kex_dh(int type, u_int32_t seq, st - u_char hash[SSH_DIGEST_MAX_LENGTH]; - size_t klen = 0, slen, sbloblen, hashlen; - int kout, r; -+ const BIGNUM *pub_key; - - if (kex->verify_host_key == NULL) { - r = SSH_ERR_INVALID_ARGUMENT; -@@ -168,6 +176,7 @@ input_kex_dh(int type, u_int32_t seq, st - #endif - - /* calc and verify H */ -+ DH_get0_key(kex->dh, &pub_key, NULL); - hashlen = sizeof(hash); - if ((r = kex_dh_hash( - kex->hash_alg, -@@ -176,7 +185,7 @@ input_kex_dh(int type, u_int32_t seq, st - sshbuf_ptr(kex->my), sshbuf_len(kex->my), - sshbuf_ptr(kex->peer), sshbuf_len(kex->peer), - server_host_key_blob, sbloblen, -- kex->dh->pub_key, -+ pub_key, - dh_server_pub, - shared_secret, - hash, &hashlen)) != 0) -diff -up openssh/kexdhs.c.openssl openssh/kexdhs.c ---- openssh/kexdhs.c.openssl 2017-09-19 06:26:43.000000000 +0200 -+++ openssh/kexdhs.c 2017-09-26 13:19:31.797249697 +0200 -@@ -87,6 +87,10 @@ kexdh_server(struct ssh *ssh) - ssh_dispatch_set(ssh, SSH2_MSG_KEXDH_INIT, &input_kex_dh_init); - r = 0; - out: -+ if (r != 0) { -+ DH_free(kex->dh); -+ kex->dh = NULL; -+ } - return r; - } - -@@ -101,6 +105,7 @@ input_kex_dh_init(int type, u_int32_t se - size_t sbloblen, slen; - size_t klen = 0, hashlen; - int kout, r; -+ const BIGNUM *pub_key; - - if (kex->load_host_public_key == NULL || - kex->load_host_private_key == NULL) { -@@ -163,6 +168,7 @@ input_kex_dh_init(int type, u_int32_t se - goto out; - /* calc H */ - hashlen = sizeof(hash); -+ DH_get0_key(kex->dh, &pub_key, NULL); - if ((r = kex_dh_hash( - kex->hash_alg, - kex->client_version_string, -@@ -171,7 +177,7 @@ input_kex_dh_init(int type, u_int32_t se - sshbuf_ptr(kex->my), sshbuf_len(kex->my), - server_host_key_blob, sbloblen, - dh_client_pub, -- kex->dh->pub_key, -+ pub_key, - shared_secret, - hash, &hashlen)) != 0) - goto out; -@@ -197,7 +203,7 @@ input_kex_dh_init(int type, u_int32_t se - /* send server hostkey, DH pubkey 'f' and signed H */ - if ((r = sshpkt_start(ssh, SSH2_MSG_KEXDH_REPLY)) != 0 || - (r = sshpkt_put_string(ssh, server_host_key_blob, sbloblen)) != 0 || -- (r = sshpkt_put_bignum2(ssh, kex->dh->pub_key)) != 0 || /* f */ -+ (r = sshpkt_put_bignum2(ssh, pub_key)) != 0 || /* f */ - (r = sshpkt_put_string(ssh, signature, slen)) != 0 || - (r = sshpkt_send(ssh)) != 0) - goto out; -diff -up openssh/kexgexc.c.openssl openssh/kexgexc.c ---- openssh/kexgexc.c.openssl 2017-09-26 13:19:31.783249611 +0200 -+++ openssh/kexgexc.c 2017-09-26 13:19:31.797249697 +0200 -@@ -95,6 +95,7 @@ input_kex_dh_gex_group(int type, u_int32 - struct kex *kex = ssh->kex; - BIGNUM *p = NULL, *g = NULL; - int r, bits; -+ const BIGNUM *pub_key; - - debug("got SSH2_MSG_KEX_DH_GEX_GROUP"); - -@@ -119,24 +120,30 @@ input_kex_dh_gex_group(int type, u_int32 - p = g = NULL; /* belong to kex->dh now */ - - /* generate and send 'e', client DH public key */ -- if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0 || -- (r = sshpkt_start(ssh, SSH2_MSG_KEX_DH_GEX_INIT)) != 0 || -- (r = sshpkt_put_bignum2(ssh, kex->dh->pub_key)) != 0 || -+ if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0) -+ goto out; -+ DH_get0_key(kex->dh, &pub_key, NULL); -+ if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_DH_GEX_INIT)) != 0 || -+ (r = sshpkt_put_bignum2(ssh, pub_key)) != 0 || - (r = sshpkt_send(ssh)) != 0) - goto out; - debug("SSH2_MSG_KEX_DH_GEX_INIT sent"); - #ifdef DEBUG_KEXDH - DHparams_print_fp(stderr, kex->dh); - fprintf(stderr, "pub= "); -- BN_print_fp(stderr, kex->dh->pub_key); -+ BN_print_fp(stderr, pub_key); - fprintf(stderr, "\n"); - #endif - ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_GROUP, NULL); - ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_REPLY, &input_kex_dh_gex_reply); - r = 0; - out: - BN_clear_free(p); - BN_clear_free(g); -+ if (r != 0) { -+ DH_free(kex->dh); -+ kex->dh = NULL; -+ } - return r; - } - -@@ -152,6 +157,7 @@ input_kex_dh_gex_reply(int type, u_int32 - u_char hash[SSH_DIGEST_MAX_LENGTH]; - size_t klen = 0, slen, sbloblen, hashlen; - int kout, r; -+ const BIGNUM *p, *g, *pub_key; - - debug("got SSH2_MSG_KEX_DH_GEX_REPLY"); - if (kex->verify_host_key == NULL) { -@@ -214,6 +220,8 @@ input_kex_dh_gex_reply(int type, u_int32 - kex->min = kex->max = -1; - - /* calc and verify H */ -+ DH_get0_pqg(kex->dh, &p, NULL, &g); -+ DH_get0_key(kex->dh, &pub_key, NULL); - hashlen = sizeof(hash); - if ((r = kexgex_hash( - kex->hash_alg, -@@ -223,8 +231,8 @@ input_kex_dh_gex_reply(int type, u_int32 - sshbuf_ptr(kex->peer), sshbuf_len(kex->peer), - server_host_key_blob, sbloblen, - kex->min, kex->nbits, kex->max, -- kex->dh->p, kex->dh->g, -- kex->dh->pub_key, -+ p, g, -+ pub_key, - dh_server_pub, - shared_secret, - hash, &hashlen)) != 0) -diff -up openssh/kexgexs.c.openssl openssh/kexgexs.c ---- openssh/kexgexs.c.openssl 2017-09-26 13:19:31.783249611 +0200 -+++ openssh/kexgexs.c 2017-09-26 13:19:31.797249697 +0200 -@@ -72,6 +72,7 @@ input_kex_dh_gex_request(int type, u_int - struct kex *kex = ssh->kex; - int r; - u_int min = 0, max = 0, nbits = 0; -+ const BIGNUM *p, *g; - - debug("SSH2_MSG_KEX_DH_GEX_REQUEST received"); - if ((r = sshpkt_get_u32(ssh, &min)) != 0 || -@@ -101,9 +102,10 @@ input_kex_dh_gex_request(int type, u_int - goto out; - } - debug("SSH2_MSG_KEX_DH_GEX_GROUP sent"); -+ DH_get0_pqg(kex->dh, &p, NULL, &g); - if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_DH_GEX_GROUP)) != 0 || -- (r = sshpkt_put_bignum2(ssh, kex->dh->p)) != 0 || -- (r = sshpkt_put_bignum2(ssh, kex->dh->g)) != 0 || -+ (r = sshpkt_put_bignum2(ssh, p)) != 0 || -+ (r = sshpkt_put_bignum2(ssh, g)) != 0 || - (r = sshpkt_send(ssh)) != 0) - goto out; - -@@ -115,6 +117,10 @@ input_kex_dh_gex_request(int type, u_int - ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_INIT, &input_kex_dh_gex_init); - r = 0; - out: -+ if (r != 0) { -+ DH_free(kex->dh); -+ kex->dh = NULL; -+ } - return r; - } - -@@ -129,6 +135,7 @@ input_kex_dh_gex_init(int type, u_int32_ - size_t sbloblen, slen; - size_t klen = 0, hashlen; - int kout, r; -+ const BIGNUM *p, *g, *pub_key; - - if (kex->load_host_public_key == NULL || - kex->load_host_private_key == NULL) { -@@ -191,6 +198,8 @@ input_kex_dh_gex_init(int type, u_int32_ - goto out; - /* calc H */ - hashlen = sizeof(hash); -+ DH_get0_pqg(kex->dh, &p, NULL, &g); -+ DH_get0_key(kex->dh, &pub_key, NULL); - if ((r = kexgex_hash( - kex->hash_alg, - kex->client_version_string, -@@ -199,9 +208,9 @@ input_kex_dh_gex_init(int type, u_int32_ - sshbuf_ptr(kex->my), sshbuf_len(kex->my), - server_host_key_blob, sbloblen, - kex->min, kex->nbits, kex->max, -- kex->dh->p, kex->dh->g, -+ p, g, - dh_client_pub, -- kex->dh->pub_key, -+ pub_key, - shared_secret, - hash, &hashlen)) != 0) - goto out; -@@ -227,7 +236,7 @@ input_kex_dh_gex_init(int type, u_int32_ - /* send server hostkey, DH pubkey 'f' and signed H */ - if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_DH_GEX_REPLY)) != 0 || - (r = sshpkt_put_string(ssh, server_host_key_blob, sbloblen)) != 0 || -- (r = sshpkt_put_bignum2(ssh, kex->dh->pub_key)) != 0 || /* f */ -+ (r = sshpkt_put_bignum2(ssh, pub_key)) != 0 || /* f */ - (r = sshpkt_put_string(ssh, signature, slen)) != 0 || - (r = sshpkt_send(ssh)) != 0) - goto out; -diff -up openssh/kexgssc.c.openssl openssh/kexgssc.c ---- openssh/kexgssc.c.openssl 2017-09-26 13:19:31.771249537 +0200 -+++ openssh/kexgssc.c 2017-09-26 13:19:31.797249697 +0200 -@@ -59,6 +59,7 @@ kexgss_client(struct ssh *ssh) { - BIGNUM *shared_secret = NULL; - BIGNUM *p = NULL; - BIGNUM *g = NULL; -+ const BIGNUM *pub_key, *p1, *g1; - u_char *kbuf; - u_char *serverhostkey = NULL; - u_char *empty = ""; -@@ -126,6 +127,7 @@ kexgss_client(struct ssh *ssh) { - - /* Step 1 - e is dh->pub_key */ - dh_gen_key(dh, ssh->kex->we_need * 8); -+ DH_get0_key(dh, &pub_key, NULL); - - /* This is f, we initialise it now to make life easier */ - dh_server_pub = BN_new(); -@@ -173,7 +175,7 @@ kexgss_client(struct ssh *ssh) { - packet_start(SSH2_MSG_KEXGSS_INIT); - packet_put_string(send_tok.value, - send_tok.length); -- packet_put_bignum2(dh->pub_key); -+ packet_put_bignum2((BIGNUM *)pub_key); - first = 0; - } else { - packet_start(SSH2_MSG_KEXGSS_CONTINUE); -@@ -282,13 +284,14 @@ kexgss_client(struct ssh *ssh) { - sshbuf_ptr(ssh->kex->my), sshbuf_len(ssh->kex->my), - sshbuf_ptr(ssh->kex->peer), sshbuf_len(ssh->kex->peer), - (serverhostkey ? serverhostkey : empty), slen, -- dh->pub_key, /* e */ -+ pub_key, /* e */ - dh_server_pub, /* f */ - shared_secret, /* K */ - hash, &hashlen - ); - break; - case KEX_GSS_GEX_SHA1: -+ DH_get0_pqg(dh, &p1, NULL, &g1); - kexgex_hash( - ssh->kex->hash_alg, - ssh->kex->client_version_string, -@@ -297,8 +300,8 @@ kexgss_client(struct ssh *ssh) { - sshbuf_ptr(ssh->kex->peer), sshbuf_len(ssh->kex->peer), - (serverhostkey ? serverhostkey : empty), slen, - min, nbits, max, -- dh->p, dh->g, -- dh->pub_key, -+ p1, g1, -+ pub_key, - dh_server_pub, - shared_secret, - hash, &hashlen -diff -up openssh/kexgsss.c.openssl openssh/kexgsss.c ---- openssh/kexgsss.c.openssl 2017-09-26 13:19:31.771249537 +0200 -+++ openssh/kexgsss.c 2017-09-26 13:19:31.798249703 +0200 -@@ -78,6 +78,7 @@ kexgss_server(struct ssh *ssh) - char *mechs; - u_char hash[SSH_DIGEST_MAX_LENGTH]; - size_t hashlen; -+ const BIGNUM *p, *g, *pub_key; - - /* Initialise GSSAPI */ - -@@ -127,9 +128,10 @@ kexgss_server(struct ssh *ssh) - if (dh == NULL) - packet_disconnect("Protocol error: no matching group found"); - -+ DH_get0_pqg(dh, &p, NULL, &g); - packet_start(SSH2_MSG_KEXGSS_GROUP); -- packet_put_bignum2(dh->p); -- packet_put_bignum2(dh->g); -+ packet_put_bignum2((BIGNUM *)p); -+ packet_put_bignum2((BIGNUM *)g); - packet_send(); - - packet_write_wait(); -@@ -221,6 +223,7 @@ kexgss_server(struct ssh *ssh) - memset(kbuf, 0, klen); - free(kbuf); - -+ DH_get0_key(dh, &pub_key, NULL); - hashlen = sizeof(hash); - switch (ssh->kex->kex_type) { - case KEX_GSS_GRP1_SHA1: -@@ -232,7 +235,7 @@ kexgss_server(struct ssh *ssh) - sshbuf_ptr(ssh->kex->peer), sshbuf_len(ssh->kex->peer), - sshbuf_ptr(ssh->kex->my), sshbuf_len(ssh->kex->my), - NULL, 0, /* Change this if we start sending host keys */ -- dh_client_pub, dh->pub_key, shared_secret, -+ dh_client_pub, pub_key, shared_secret, - hash, &hashlen - ); - break; -@@ -244,9 +247,9 @@ kexgss_server(struct ssh *ssh) - sshbuf_ptr(ssh->kex->my), sshbuf_len(ssh->kex->my), - NULL, 0, - cmin, nbits, cmax, -- dh->p, dh->g, -+ p, g, - dh_client_pub, -- dh->pub_key, -+ pub_key, - shared_secret, - hash, &hashlen - ); -@@ -270,7 +273,7 @@ kexgss_server(struct ssh *ssh) - fatal("Couldn't get MIC"); - - packet_start(SSH2_MSG_KEXGSS_COMPLETE); -- packet_put_bignum2(dh->pub_key); -+ packet_put_bignum2((BIGNUM *)pub_key); - packet_put_string(msg_tok.value,msg_tok.length); - - if (send_tok.length != 0) { -diff -up openssh/libcrypto-compat.c.openssl openssh/libcrypto-compat.c ---- openssh/libcrypto-compat.c.openssl 2017-09-26 13:19:31.798249703 +0200 -+++ openssh/libcrypto-compat.c 2017-09-26 13:19:31.798249703 +0200 -@@ -0,0 +1,428 @@ -+/* -+ * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved. -+ * -+ * Licensed under the OpenSSL license (the "License"). You may not use -+ * this file except in compliance with the License. You can obtain a copy -+ * in the file LICENSE in the source distribution or at -+ * https://www.openssl.org/source/license.html -+ */ -+ -+#include "includes.h" -+ -+#if OPENSSL_VERSION_NUMBER < 0x10100000L -+ -+#include -+#include -+ -+static void *OPENSSL_zalloc(size_t num) -+{ -+ void *ret = OPENSSL_malloc(num); -+ -+ if (ret != NULL) -+ memset(ret, 0, num); -+ return ret; -+} -+ -+int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d) -+{ -+ /* If the fields n and e in r are NULL, the corresponding input -+ * parameters MUST be non-NULL for n and e. d may be -+ * left NULL (in case only the public key is used). -+ */ -+ if ((r->n == NULL && n == NULL) -+ || (r->e == NULL && e == NULL)) -+ return 0; -+ -+ if (n != NULL) { -+ BN_free(r->n); -+ r->n = n; -+ } -+ if (e != NULL) { -+ BN_free(r->e); -+ r->e = e; -+ } -+ if (d != NULL) { -+ BN_free(r->d); -+ r->d = d; -+ } -+ -+ return 1; -+} -+ -+int RSA_set0_factors(RSA *r, BIGNUM *p, BIGNUM *q) -+{ -+ /* If the fields p and q in r are NULL, the corresponding input -+ * parameters MUST be non-NULL. -+ */ -+ if ((r->p == NULL && p == NULL) -+ || (r->q == NULL && q == NULL)) -+ return 0; -+ -+ if (p != NULL) { -+ BN_free(r->p); -+ r->p = p; -+ } -+ if (q != NULL) { -+ BN_free(r->q); -+ r->q = q; -+ } -+ -+ return 1; -+} -+ -+int RSA_set0_crt_params(RSA *r, BIGNUM *dmp1, BIGNUM *dmq1, BIGNUM *iqmp) -+{ -+ /* If the fields dmp1, dmq1 and iqmp in r are NULL, the corresponding input -+ * parameters MUST be non-NULL. -+ */ -+ if ((r->dmp1 == NULL && dmp1 == NULL) -+ || (r->dmq1 == NULL && dmq1 == NULL) -+ || (r->iqmp == NULL && iqmp == NULL)) -+ return 0; -+ -+ if (dmp1 != NULL) { -+ BN_free(r->dmp1); -+ r->dmp1 = dmp1; -+ } -+ if (dmq1 != NULL) { -+ BN_free(r->dmq1); -+ r->dmq1 = dmq1; -+ } -+ if (iqmp != NULL) { -+ BN_free(r->iqmp); -+ r->iqmp = iqmp; -+ } -+ -+ return 1; -+} -+ -+void RSA_get0_key(const RSA *r, -+ const BIGNUM **n, const BIGNUM **e, const BIGNUM **d) -+{ -+ if (n != NULL) -+ *n = r->n; -+ if (e != NULL) -+ *e = r->e; -+ if (d != NULL) -+ *d = r->d; -+} -+ -+void RSA_get0_factors(const RSA *r, const BIGNUM **p, const BIGNUM **q) -+{ -+ if (p != NULL) -+ *p = r->p; -+ if (q != NULL) -+ *q = r->q; -+} -+ -+void RSA_get0_crt_params(const RSA *r, -+ const BIGNUM **dmp1, const BIGNUM **dmq1, -+ const BIGNUM **iqmp) -+{ -+ if (dmp1 != NULL) -+ *dmp1 = r->dmp1; -+ if (dmq1 != NULL) -+ *dmq1 = r->dmq1; -+ if (iqmp != NULL) -+ *iqmp = r->iqmp; -+} -+ -+void DSA_get0_pqg(const DSA *d, -+ const BIGNUM **p, const BIGNUM **q, const BIGNUM **g) -+{ -+ if (p != NULL) -+ *p = d->p; -+ if (q != NULL) -+ *q = d->q; -+ if (g != NULL) -+ *g = d->g; -+} -+ -+int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g) -+{ -+ /* If the fields p, q and g in d are NULL, the corresponding input -+ * parameters MUST be non-NULL. -+ */ -+ if ((d->p == NULL && p == NULL) -+ || (d->q == NULL && q == NULL) -+ || (d->g == NULL && g == NULL)) -+ return 0; -+ -+ if (p != NULL) { -+ BN_free(d->p); -+ d->p = p; -+ } -+ if (q != NULL) { -+ BN_free(d->q); -+ d->q = q; -+ } -+ if (g != NULL) { -+ BN_free(d->g); -+ d->g = g; -+ } -+ -+ return 1; -+} -+ -+void DSA_get0_key(const DSA *d, -+ const BIGNUM **pub_key, const BIGNUM **priv_key) -+{ -+ if (pub_key != NULL) -+ *pub_key = d->pub_key; -+ if (priv_key != NULL) -+ *priv_key = d->priv_key; -+} -+ -+int DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key) -+{ -+ /* If the field pub_key in d is NULL, the corresponding input -+ * parameters MUST be non-NULL. The priv_key field may -+ * be left NULL. -+ */ -+ if (d->pub_key == NULL && pub_key == NULL) -+ return 0; -+ -+ if (pub_key != NULL) { -+ BN_free(d->pub_key); -+ d->pub_key = pub_key; -+ } -+ if (priv_key != NULL) { -+ BN_free(d->priv_key); -+ d->priv_key = priv_key; -+ } -+ -+ return 1; -+} -+ -+void DSA_SIG_get0(const DSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps) -+{ -+ if (pr != NULL) -+ *pr = sig->r; -+ if (ps != NULL) -+ *ps = sig->s; -+} -+ -+int DSA_SIG_set0(DSA_SIG *sig, BIGNUM *r, BIGNUM *s) -+{ -+ if (r == NULL || s == NULL) -+ return 0; -+ BN_clear_free(sig->r); -+ BN_clear_free(sig->s); -+ sig->r = r; -+ sig->s = s; -+ return 1; -+} -+ -+void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps) -+{ -+ if (pr != NULL) -+ *pr = sig->r; -+ if (ps != NULL) -+ *ps = sig->s; -+} -+ -+int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s) -+{ -+ if (r == NULL || s == NULL) -+ return 0; -+ BN_clear_free(sig->r); -+ BN_clear_free(sig->s); -+ sig->r = r; -+ sig->s = s; -+ return 1; -+} -+ -+void DH_get0_pqg(const DH *dh, -+ const BIGNUM **p, const BIGNUM **q, const BIGNUM **g) -+{ -+ if (p != NULL) -+ *p = dh->p; -+ if (q != NULL) -+ *q = dh->q; -+ if (g != NULL) -+ *g = dh->g; -+} -+ -+int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g) -+{ -+ /* If the fields p and g in d are NULL, the corresponding input -+ * parameters MUST be non-NULL. q may remain NULL. -+ */ -+ if ((dh->p == NULL && p == NULL) -+ || (dh->g == NULL && g == NULL)) -+ return 0; -+ -+ if (p != NULL) { -+ BN_free(dh->p); -+ dh->p = p; -+ } -+ if (q != NULL) { -+ BN_free(dh->q); -+ dh->q = q; -+ } -+ if (g != NULL) { -+ BN_free(dh->g); -+ dh->g = g; -+ } -+ -+ if (q != NULL) { -+ dh->length = BN_num_bits(q); -+ } -+ -+ return 1; -+} -+ -+void DH_get0_key(const DH *dh, const BIGNUM **pub_key, const BIGNUM **priv_key) -+{ -+ if (pub_key != NULL) -+ *pub_key = dh->pub_key; -+ if (priv_key != NULL) -+ *priv_key = dh->priv_key; -+} -+ -+int DH_set_length(DH *dh, long length) -+{ -+ dh->length = length; -+ return 1; -+} -+ -+const unsigned char *EVP_CIPHER_CTX_iv(const EVP_CIPHER_CTX *ctx) -+{ -+ return ctx->iv; -+} -+ -+unsigned char *EVP_CIPHER_CTX_iv_noconst(EVP_CIPHER_CTX *ctx) -+{ -+ return ctx->iv; -+} -+ -+EVP_MD_CTX *EVP_MD_CTX_new(void) -+{ -+ return OPENSSL_zalloc(sizeof(EVP_MD_CTX)); -+} -+ -+static void OPENSSL_clear_free(void *str, size_t num) -+{ -+ if (str == NULL) -+ return; -+ if (num) -+ OPENSSL_cleanse(str, num); -+ OPENSSL_free(str); -+} -+ -+/* This call frees resources associated with the context */ -+int EVP_MD_CTX_reset(EVP_MD_CTX *ctx) -+{ -+ if (ctx == NULL) -+ return 1; -+ -+ /* -+ * Don't assume ctx->md_data was cleaned in EVP_Digest_Final, because -+ * sometimes only copies of the context are ever finalised. -+ */ -+ if (ctx->digest && ctx->digest->cleanup -+ && !EVP_MD_CTX_test_flags(ctx, EVP_MD_CTX_FLAG_CLEANED)) -+ ctx->digest->cleanup(ctx); -+ if (ctx->digest && ctx->digest->ctx_size && ctx->md_data -+ && !EVP_MD_CTX_test_flags(ctx, EVP_MD_CTX_FLAG_REUSE)) { -+ OPENSSL_clear_free(ctx->md_data, ctx->digest->ctx_size); -+ } -+ EVP_PKEY_CTX_free(ctx->pctx); -+#ifndef OPENSSL_NO_ENGINE -+ ENGINE_finish(ctx->engine); -+#endif -+ OPENSSL_cleanse(ctx, sizeof(*ctx)); -+ -+ return 1; -+} -+ -+void EVP_MD_CTX_free(EVP_MD_CTX *ctx) -+{ -+ EVP_MD_CTX_reset(ctx); -+ OPENSSL_free(ctx); -+} -+ -+RSA_METHOD *RSA_meth_dup(const RSA_METHOD *meth) -+{ -+ RSA_METHOD *ret; -+ -+ ret = OPENSSL_malloc(sizeof(RSA_METHOD)); -+ -+ if (ret != NULL) { -+ memcpy(ret, meth, sizeof(*meth)); -+ ret->name = OPENSSL_strdup(meth->name); -+ if (ret->name == NULL) { -+ OPENSSL_free(ret); -+ return NULL; -+ } -+ } -+ -+ return ret; -+} -+ -+int RSA_meth_set1_name(RSA_METHOD *meth, const char *name) -+{ -+ char *tmpname; -+ -+ tmpname = OPENSSL_strdup(name); -+ if (tmpname == NULL) { -+ return 0; -+ } -+ -+ OPENSSL_free((char *)meth->name); -+ meth->name = tmpname; -+ -+ return 1; -+} -+ -+int RSA_meth_set_priv_enc(RSA_METHOD *meth, -+ int (*priv_enc) (int flen, const unsigned char *from, -+ unsigned char *to, RSA *rsa, -+ int padding)) -+{ -+ meth->rsa_priv_enc = priv_enc; -+ return 1; -+} -+ -+int RSA_meth_set_priv_dec(RSA_METHOD *meth, -+ int (*priv_dec) (int flen, const unsigned char *from, -+ unsigned char *to, RSA *rsa, -+ int padding)) -+{ -+ meth->rsa_priv_dec = priv_dec; -+ return 1; -+} -+ -+int RSA_meth_set_finish(RSA_METHOD *meth, int (*finish) (RSA *rsa)) -+{ -+ meth->finish = finish; -+ return 1; -+} -+ -+void RSA_meth_free(RSA_METHOD *meth) -+{ -+ if (meth != NULL) { -+ OPENSSL_free((char *)meth->name); -+ OPENSSL_free(meth); -+ } -+} -+ -+int RSA_bits(const RSA *r) -+{ -+ return (BN_num_bits(r->n)); -+} -+ -+int DSA_bits(const DSA *dsa) -+{ -+ return BN_num_bits(dsa->p); -+} -+ -+RSA *EVP_PKEY_get0_RSA(EVP_PKEY *pkey) -+{ -+ if (pkey->type != EVP_PKEY_RSA) { -+ return NULL; -+ } -+ return pkey->pkey.rsa; -+} -+ -+#endif /* OPENSSL_VERSION_NUMBER */ -diff -up openssh/libcrypto-compat.h.openssl openssh/libcrypto-compat.h ---- openssh/libcrypto-compat.h.openssl 2017-09-26 13:19:31.798249703 +0200 -+++ openssh/libcrypto-compat.h 2017-09-26 13:19:31.798249703 +0200 -@@ -0,0 +1,59 @@ -+#ifndef LIBCRYPTO_COMPAT_H -+#define LIBCRYPTO_COMPAT_H -+ -+#if OPENSSL_VERSION_NUMBER < 0x10100000L -+ -+#include -+#include -+#include -+#include -+#include -+ -+int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d); -+int RSA_set0_factors(RSA *r, BIGNUM *p, BIGNUM *q); -+int RSA_set0_crt_params(RSA *r, BIGNUM *dmp1, BIGNUM *dmq1, BIGNUM *iqmp); -+void RSA_get0_key(const RSA *r, const BIGNUM **n, const BIGNUM **e, const BIGNUM **d); -+void RSA_get0_factors(const RSA *r, const BIGNUM **p, const BIGNUM **q); -+void RSA_get0_crt_params(const RSA *r, const BIGNUM **dmp1, const BIGNUM **dmq1, const BIGNUM **iqmp); -+ -+void DSA_get0_pqg(const DSA *d, const BIGNUM **p, const BIGNUM **q, const BIGNUM **g); -+int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g); -+void DSA_get0_key(const DSA *d, const BIGNUM **pub_key, const BIGNUM **priv_key); -+int DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key); -+ -+void DSA_SIG_get0(const DSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps); -+int DSA_SIG_set0(DSA_SIG *sig, BIGNUM *r, BIGNUM *s); -+ -+void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps); -+int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s); -+ -+void DH_get0_pqg(const DH *dh, const BIGNUM **p, const BIGNUM **q, const BIGNUM **g); -+int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g); -+void DH_get0_key(const DH *dh, const BIGNUM **pub_key, const BIGNUM **priv_key); -+int DH_set_length(DH *dh, long length); -+ -+const unsigned char *EVP_CIPHER_CTX_iv(const EVP_CIPHER_CTX *ctx); -+unsigned char *EVP_CIPHER_CTX_iv_noconst(EVP_CIPHER_CTX *ctx); -+int EVP_MD_CTX_reset(EVP_MD_CTX *ctx); -+EVP_MD_CTX *EVP_MD_CTX_new(void); -+void EVP_MD_CTX_free(EVP_MD_CTX *ctx); -+#define EVP_CIPHER_impl_ctx_size(e) e->ctx_size -+#define EVP_CIPHER_CTX_get_cipher_data(ctx) ctx->cipher_data -+ -+RSA_METHOD *RSA_meth_dup(const RSA_METHOD *meth); -+int RSA_meth_set1_name(RSA_METHOD *meth, const char *name); -+#define RSA_meth_get_finish(meth) meth->finish -+int RSA_meth_set_priv_enc(RSA_METHOD *meth, int (*priv_enc) (int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding)); -+int RSA_meth_set_priv_dec(RSA_METHOD *meth, int (*priv_dec) (int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding)); -+int RSA_meth_set_finish(RSA_METHOD *meth, int (*finish) (RSA *rsa)); -+void RSA_meth_free(RSA_METHOD *meth); -+ -+int RSA_bits(const RSA *r); -+int DSA_bits(const DSA *d); -+ -+RSA *EVP_PKEY_get0_RSA(EVP_PKEY *pkey); -+ -+#endif /* OPENSSL_VERSION_NUMBER */ -+ -+#endif /* LIBCRYPTO_COMPAT_H */ -+ -diff -up openssh/Makefile.in.openssl openssh/Makefile.in ---- openssh/Makefile.in.openssl 2017-09-26 13:19:31.784249617 +0200 -+++ openssh/Makefile.in 2017-09-26 13:19:31.798249703 +0200 -@@ -101,7 +101,7 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \ - kexdhc.o kexgexc.o kexecdhc.o kexc25519c.o \ - kexdhs.o kexgexs.o kexecdhs.o kexc25519s.o \ - platform-pledge.o platform-tracing.o platform-misc.o \ -- auditstub.o -+ auditstub.o libcrypto-compat.o - - SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \ - sshconnect.o sshconnect2.o mux.o -diff -up openssh/monitor.c.openssl openssh/monitor.c ---- openssh/monitor.c.openssl 2017-09-26 13:19:31.789249648 +0200 -+++ openssh/monitor.c 2017-09-26 13:19:31.799249709 +0200 -@@ -631,9 +631,12 @@ mm_answer_moduli(int sock, Buffer *m) - return (0); - } else { - /* Send first bignum */ -+ const BIGNUM *p, *g; -+ -+ DH_get0_pqg(dh, &p, NULL, &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) -+ (r = sshbuf_put_bignum2(m, p)) != 0 || -+ (r = sshbuf_put_bignum2(m, g)) != 0) - fatal("%s: buffer error: %s", __func__, ssh_err(r)); - - DH_free(dh); -diff -up openssh/openbsd-compat/openssl-compat.c.openssl openssh/openbsd-compat/openssl-compat.c ---- openssh/openbsd-compat/openssl-compat.c.openssl 2017-09-19 06:26:43.000000000 +0200 -+++ openssh/openbsd-compat/openssl-compat.c 2017-09-26 13:19:31.799249709 +0200 -@@ -70,12 +70,19 @@ ssh_compatible_openssl(long headerver, l - void - ssh_OpenSSL_add_all_algorithms(void) - { -+#if OPENSSL_VERSION_NUMBER < 0x10100000L - OpenSSL_add_all_algorithms(); - - /* Enable use of crypto hardware */ - ENGINE_load_builtin_engines(); -+#if OPENSSL_VERSION_NUMBER < 0x10001000L - ENGINE_register_all_complete(); -+#endif - OPENSSL_config(NULL); -+#else -+ OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_DIGESTS | -+ OPENSSL_INIT_ADD_ALL_DIGESTS | OPENSSL_INIT_LOAD_CONFIG, NULL); -+#endif - } - #endif - -diff -up openssh/regress/unittests/sshkey/test_file.c.openssl openssh/regress/unittests/sshkey/test_file.c ---- openssh/regress/unittests/sshkey/test_file.c.openssl 2017-09-19 06:26:43.000000000 +0200 -+++ openssh/regress/unittests/sshkey/test_file.c 2017-09-26 13:19:31.799249709 +0200 -@@ -46,6 +46,7 @@ sshkey_file_tests(void) - struct sshbuf *buf, *pw; - BIGNUM *a, *b, *c; - char *cp; -+ const BIGNUM *n, *p, *q, *g, *pub_key, *priv_key; - - TEST_START("load passphrase"); - pw = load_text_file("pw"); -@@ -60,9 +61,11 @@ sshkey_file_tests(void) - a = load_bignum("rsa_1.param.n"); - b = load_bignum("rsa_1.param.p"); - c = load_bignum("rsa_1.param.q"); -- ASSERT_BIGNUM_EQ(k1->rsa->n, a); -- ASSERT_BIGNUM_EQ(k1->rsa->p, b); -- ASSERT_BIGNUM_EQ(k1->rsa->q, c); -+ RSA_get0_key(k1->rsa, &n, NULL, NULL); -+ RSA_get0_factors(k1->rsa, &p, &q); -+ ASSERT_BIGNUM_EQ(n, a); -+ ASSERT_BIGNUM_EQ(p, b); -+ ASSERT_BIGNUM_EQ(q, c); - BN_free(a); - BN_free(b); - BN_free(c); -@@ -151,9 +154,11 @@ sshkey_file_tests(void) - a = load_bignum("dsa_1.param.g"); - b = load_bignum("dsa_1.param.priv"); - c = load_bignum("dsa_1.param.pub"); -- ASSERT_BIGNUM_EQ(k1->dsa->g, a); -- ASSERT_BIGNUM_EQ(k1->dsa->priv_key, b); -- ASSERT_BIGNUM_EQ(k1->dsa->pub_key, c); -+ DSA_get0_pqg(k1->dsa, NULL, NULL, &g); -+ DSA_get0_key(k1->dsa, &pub_key, &priv_key); -+ ASSERT_BIGNUM_EQ(g, a); -+ ASSERT_BIGNUM_EQ(priv_key, b); -+ ASSERT_BIGNUM_EQ(pub_key, c); - BN_free(a); - BN_free(b); - BN_free(c); -diff -up openssh/regress/unittests/sshkey/test_sshkey.c.openssl openssh/regress/unittests/sshkey/test_sshkey.c ---- openssh/regress/unittests/sshkey/test_sshkey.c.openssl 2017-09-19 06:26:43.000000000 +0200 -+++ openssh/regress/unittests/sshkey/test_sshkey.c 2017-09-26 13:19:31.800249715 +0200 -@@ -197,9 +197,6 @@ sshkey_tests(void) - k1 = sshkey_new(KEY_RSA); - ASSERT_PTR_NE(k1, NULL); - ASSERT_PTR_NE(k1->rsa, NULL); -- ASSERT_PTR_NE(k1->rsa->n, NULL); -- ASSERT_PTR_NE(k1->rsa->e, NULL); -- ASSERT_PTR_EQ(k1->rsa->p, NULL); - sshkey_free(k1); - TEST_DONE(); - -@@ -207,8 +204,6 @@ sshkey_tests(void) - k1 = sshkey_new(KEY_DSA); - ASSERT_PTR_NE(k1, NULL); - ASSERT_PTR_NE(k1->dsa, NULL); -- ASSERT_PTR_NE(k1->dsa->g, NULL); -- ASSERT_PTR_EQ(k1->dsa->priv_key, NULL); - sshkey_free(k1); - TEST_DONE(); - -@@ -234,9 +229,6 @@ sshkey_tests(void) - k1 = sshkey_new_private(KEY_RSA); - ASSERT_PTR_NE(k1, NULL); - ASSERT_PTR_NE(k1->rsa, NULL); -- ASSERT_PTR_NE(k1->rsa->n, NULL); -- ASSERT_PTR_NE(k1->rsa->e, NULL); -- ASSERT_PTR_NE(k1->rsa->p, NULL); - ASSERT_INT_EQ(sshkey_add_private(k1), 0); - sshkey_free(k1); - TEST_DONE(); -@@ -245,8 +237,6 @@ sshkey_tests(void) - k1 = sshkey_new_private(KEY_DSA); - ASSERT_PTR_NE(k1, NULL); - ASSERT_PTR_NE(k1->dsa, NULL); -- ASSERT_PTR_NE(k1->dsa->g, NULL); -- ASSERT_PTR_NE(k1->dsa->priv_key, NULL); - ASSERT_INT_EQ(sshkey_add_private(k1), 0); - sshkey_free(k1); - TEST_DONE(); -@@ -285,18 +275,13 @@ sshkey_tests(void) - ASSERT_INT_EQ(sshkey_generate(KEY_RSA, 1024, &kr), 0); - ASSERT_PTR_NE(kr, NULL); - ASSERT_PTR_NE(kr->rsa, NULL); -- ASSERT_PTR_NE(kr->rsa->n, NULL); -- ASSERT_PTR_NE(kr->rsa->e, NULL); -- ASSERT_PTR_NE(kr->rsa->p, NULL); -- ASSERT_INT_EQ(BN_num_bits(kr->rsa->n), 1024); -+ ASSERT_INT_EQ(RSA_bits(kr->rsa), 1024); - TEST_DONE(); - - TEST_START("generate KEY_DSA"); - ASSERT_INT_EQ(sshkey_generate(KEY_DSA, 1024, &kd), 0); - ASSERT_PTR_NE(kd, NULL); - ASSERT_PTR_NE(kd->dsa, NULL); -- ASSERT_PTR_NE(kd->dsa->g, NULL); -- ASSERT_PTR_NE(kd->dsa->priv_key, NULL); - TEST_DONE(); - - #ifdef OPENSSL_HAS_ECC -@@ -323,9 +308,6 @@ sshkey_tests(void) - ASSERT_PTR_NE(kr, k1); - ASSERT_INT_EQ(k1->type, KEY_RSA); - ASSERT_PTR_NE(k1->rsa, NULL); -- ASSERT_PTR_NE(k1->rsa->n, NULL); -- ASSERT_PTR_NE(k1->rsa->e, NULL); -- ASSERT_PTR_EQ(k1->rsa->p, NULL); - TEST_DONE(); - - TEST_START("equal KEY_RSA/demoted KEY_RSA"); -@@ -339,8 +321,6 @@ sshkey_tests(void) - ASSERT_PTR_NE(kd, k1); - ASSERT_INT_EQ(k1->type, KEY_DSA); - ASSERT_PTR_NE(k1->dsa, NULL); -- ASSERT_PTR_NE(k1->dsa->g, NULL); -- ASSERT_PTR_EQ(k1->dsa->priv_key, NULL); - TEST_DONE(); - - TEST_START("equal KEY_DSA/demoted KEY_DSA"); -diff -up openssh/ssh.c.openssl openssh/ssh.c ---- openssh/ssh.c.openssl 2017-09-26 13:19:31.786249629 +0200 -+++ openssh/ssh.c 2017-09-26 13:19:31.800249715 +0200 -@@ -530,7 +530,9 @@ main(int ac, char **av) - sanitise_stdfd(); - - __progname = ssh_get_progname(av[0]); -+#if OPENSSL_VERSION_NUMBER < 0x10100000L - SSLeay_add_all_algorithms(); -+#endif - if (access("/etc/system-fips", F_OK) == 0) - if (! FIPSCHECK_verify(NULL, NULL)){ - if (FIPS_mode()) -diff -up openssh/sshd.c.openssl openssh/sshd.c ---- openssh/sshd.c.openssl 2017-09-26 13:19:31.792249666 +0200 -+++ openssh/sshd.c 2017-09-26 13:19:31.801249721 +0200 -@@ -1485,7 +1485,7 @@ main(int ac, char **av) - #endif - __progname = ssh_get_progname(av[0]); - -- SSLeay_add_all_algorithms(); -+ OpenSSL_add_all_algorithms(); - if (access("/etc/system-fips", F_OK) == 0) - if (! FIPSCHECK_verify(NULL, NULL)) { - openlog(__progname, LOG_PID, LOG_AUTHPRIV); -diff -up openssh/ssh-dss.c.openssl openssh/ssh-dss.c ---- openssh/ssh-dss.c.openssl 2017-09-19 06:26:43.000000000 +0200 -+++ openssh/ssh-dss.c 2017-09-26 13:19:31.801249721 +0200 -@@ -55,6 +55,7 @@ ssh_dss_sign(const struct sshkey *key, u - size_t rlen, slen, len, dlen = ssh_digest_bytes(SSH_DIGEST_SHA1); - struct sshbuf *b = NULL; - int ret = SSH_ERR_INVALID_ARGUMENT; -+ const BIGNUM *r, *s; - - if (lenp != NULL) - *lenp = 0; -@@ -76,15 +77,16 @@ ssh_dss_sign(const struct sshkey *key, u - goto out; - } - -- rlen = BN_num_bytes(sig->r); -- slen = BN_num_bytes(sig->s); -+ DSA_SIG_get0(sig, &r, &s); -+ rlen = BN_num_bytes(r); -+ slen = BN_num_bytes(s); - if (rlen > INTBLOB_LEN || slen > INTBLOB_LEN) { - ret = SSH_ERR_INTERNAL_ERROR; - goto out; - } - explicit_bzero(sigblob, SIGBLOB_LEN); -- BN_bn2bin(sig->r, sigblob + SIGBLOB_LEN - INTBLOB_LEN - rlen); -- BN_bn2bin(sig->s, sigblob + SIGBLOB_LEN - slen); -+ BN_bn2bin(r, sigblob + SIGBLOB_LEN - INTBLOB_LEN - rlen); -+ BN_bn2bin(s, sigblob + SIGBLOB_LEN - slen); - - if ((b = sshbuf_new()) == NULL) { - ret = SSH_ERR_ALLOC_FAIL; -@@ -137,6 +139,7 @@ ssh_dss_verify(const struct sshkey *key, - int ret = SSH_ERR_INTERNAL_ERROR; - struct sshbuf *b = NULL; - char *ktype = NULL; -+ BIGNUM *r = NULL, *s = NULL; - - if (key == NULL || key->dsa == NULL || - sshkey_type_plain(key->type) != KEY_DSA || -@@ -177,16 +180,19 @@ ssh_dss_verify(const struct sshkey *key, - - /* parse signature */ - if ((sig = DSA_SIG_new()) == NULL || -- (sig->r = BN_new()) == NULL || -- (sig->s = BN_new()) == NULL) { -+ (r = BN_new()) == NULL || -+ (s = BN_new()) == NULL) { - ret = SSH_ERR_ALLOC_FAIL; - goto out; - } -- if ((BN_bin2bn(sigblob, INTBLOB_LEN, sig->r) == NULL) || -- (BN_bin2bn(sigblob+ INTBLOB_LEN, INTBLOB_LEN, sig->s) == NULL)) { -+ if ((BN_bin2bn(sigblob, INTBLOB_LEN, r) == NULL) || -+ (BN_bin2bn(sigblob+ INTBLOB_LEN, INTBLOB_LEN, s) == NULL) || -+ (DSA_SIG_set0(sig, r, s) == 0)) { - ret = SSH_ERR_LIBCRYPTO_ERROR; - goto out; - } -+ r = NULL; -+ s = NULL; - - /* sha1 the data */ - if ((ret = ssh_digest_memory(SSH_DIGEST_SHA1, data, datalen, -@@ -207,7 +213,9 @@ ssh_dss_verify(const struct sshkey *key, - - out: - explicit_bzero(digest, sizeof(digest)); -+ BN_free(r); -+ BN_free(s); - DSA_SIG_free(sig); - sshbuf_free(b); - free(ktype); - if (sigblob != NULL) { -diff -up openssh/ssh-ecdsa.c.openssl openssh/ssh-ecdsa.c ---- openssh/ssh-ecdsa.c.openssl 2017-09-19 06:26:43.000000000 +0200 -+++ openssh/ssh-ecdsa.c 2017-09-26 13:19:31.801249721 +0200 -@@ -54,6 +54,7 @@ ssh_ecdsa_sign(const struct sshkey *key, - size_t len, dlen; - struct sshbuf *b = NULL, *bb = NULL; - int ret = SSH_ERR_INTERNAL_ERROR; -+ const BIGNUM *r, *s; - - if (lenp != NULL) - *lenp = 0; -@@ -80,8 +81,9 @@ ssh_ecdsa_sign(const struct sshkey *key, - ret = SSH_ERR_ALLOC_FAIL; - goto out; - } -- if ((ret = sshbuf_put_bignum2(bb, sig->r)) != 0 || -- (ret = sshbuf_put_bignum2(bb, sig->s)) != 0) -+ ECDSA_SIG_get0(sig, &r, &s); -+ if ((ret = sshbuf_put_bignum2(bb, r)) != 0 || -+ (ret = sshbuf_put_bignum2(bb, s)) != 0) - goto out; - if ((ret = sshbuf_put_cstring(b, sshkey_ssh_name_plain(key))) != 0 || - (ret = sshbuf_put_stringb(b, bb)) != 0) -@@ -119,6 +121,7 @@ ssh_ecdsa_verify(const struct sshkey *ke - int ret = SSH_ERR_INTERNAL_ERROR; - struct sshbuf *b = NULL, *sigbuf = NULL; - char *ktype = NULL; -+ BIGNUM *r = NULL, *s = NULL; - - if (key == NULL || key->ecdsa == NULL || - sshkey_type_plain(key->type) != KEY_ECDSA || -@@ -147,15 +150,23 @@ ssh_ecdsa_verify(const struct sshkey *ke - } - - /* parse signature */ -- if ((sig = ECDSA_SIG_new()) == NULL) { -+ if ((sig = ECDSA_SIG_new()) == NULL || -+ (r = BN_new()) == NULL || -+ (s = BN_new()) == NULL) { - ret = SSH_ERR_ALLOC_FAIL; - goto out; - } -- if (sshbuf_get_bignum2(sigbuf, sig->r) != 0 || -- sshbuf_get_bignum2(sigbuf, sig->s) != 0) { -+ if (sshbuf_get_bignum2(sigbuf, r) != 0 || -+ sshbuf_get_bignum2(sigbuf, s) != 0) { - ret = SSH_ERR_INVALID_FORMAT; - goto out; - } -+ if (ECDSA_SIG_set0(sig, r, s) == 0) { -+ ret = SSH_ERR_LIBCRYPTO_ERROR; -+ goto out; -+ } -+ r = NULL; -+ s = NULL; - if (sshbuf_len(sigbuf) != 0) { - ret = SSH_ERR_UNEXPECTED_TRAILING_DATA; - goto out; -@@ -180,7 +191,9 @@ ssh_ecdsa_verify(const struct sshkey *ke - explicit_bzero(digest, sizeof(digest)); - sshbuf_free(sigbuf); - sshbuf_free(b); -+ BN_free(r); -+ BN_free(s); - ECDSA_SIG_free(sig); - free(ktype); - return ret; - } -diff -up openssh/sshkey.c.openssl openssh/sshkey.c ---- openssh/sshkey.c.openssl 2017-09-26 13:19:31.786249629 +0200 -+++ openssh/sshkey.c 2017-09-26 13:19:31.802249728 +0200 -@@ -267,10 +267,10 @@ sshkey_size(const struct sshkey *k) - #ifdef WITH_OPENSSL - case KEY_RSA: - case KEY_RSA_CERT: -- return BN_num_bits(k->rsa->n); -+ return RSA_bits(k->rsa); - case KEY_DSA: - case KEY_DSA_CERT: -- return BN_num_bits(k->dsa->p); -+ return DSA_bits(k->dsa); - case KEY_ECDSA: - case KEY_ECDSA_CERT: - return sshkey_curve_nid_to_bits(k->ecdsa_nid); -@@ -302,11 +302,17 @@ sshkey_is_private(const struct sshkey *k - switch (k->type) { - #ifdef WITH_OPENSSL - case KEY_RSA_CERT: -- case KEY_RSA: -- return k->rsa->d != NULL; -+ case KEY_RSA: { -+ const BIGNUM *d; -+ RSA_get0_key(k->rsa, NULL, NULL, &d); -+ return d != NULL; -+ } - case KEY_DSA_CERT: -- case KEY_DSA: -- return k->dsa->priv_key != NULL; -+ case KEY_DSA: { -+ const BIGNUM *priv_key; -+ DSA_get0_key(k->dsa, NULL, &priv_key); -+ return priv_key != NULL; -+ } - #ifdef OPENSSL_HAS_ECC - case KEY_ECDSA_CERT: - case KEY_ECDSA: -@@ -496,10 +501,7 @@ sshkey_new(int type) - #ifdef WITH_OPENSSL - case KEY_RSA: - case KEY_RSA_CERT: -- if ((rsa = RSA_new()) == NULL || -- (rsa->n = BN_new()) == NULL || -- (rsa->e = BN_new()) == NULL) { -- RSA_free(rsa); -+ if ((rsa = RSA_new()) == NULL) { - free(k); - return NULL; - } -@@ -508,12 +509,7 @@ sshkey_new(int type) - break; - case KEY_DSA: - case KEY_DSA_CERT: -- if ((dsa = DSA_new()) == NULL || -- (dsa->p = BN_new()) == NULL || -- (dsa->q = BN_new()) == NULL || -- (dsa->g = BN_new()) == NULL || -- (dsa->pub_key = BN_new()) == NULL) { -- DSA_free(dsa); -+ if ((dsa = DSA_new()) == NULL) { - free(k); - return NULL; - } -@@ -553,21 +548,10 @@ sshkey_add_private(struct sshkey *k) - #ifdef WITH_OPENSSL - case KEY_RSA: - case KEY_RSA_CERT: --#define bn_maybe_alloc_failed(p) (p == NULL && (p = BN_new()) == NULL) -- if (bn_maybe_alloc_failed(k->rsa->d) || -- bn_maybe_alloc_failed(k->rsa->iqmp) || -- bn_maybe_alloc_failed(k->rsa->q) || -- bn_maybe_alloc_failed(k->rsa->p) || -- bn_maybe_alloc_failed(k->rsa->dmq1) || -- bn_maybe_alloc_failed(k->rsa->dmp1)) -- return SSH_ERR_ALLOC_FAIL; - break; - case KEY_DSA: - case KEY_DSA_CERT: -- if (bn_maybe_alloc_failed(k->dsa->priv_key)) -- return SSH_ERR_ALLOC_FAIL; - break; --#undef bn_maybe_alloc_failed - case KEY_ECDSA: - case KEY_ECDSA_CERT: - /* Cannot do anything until we know the group */ -@@ -684,17 +668,31 @@ sshkey_equal_public(const struct sshkey - switch (a->type) { - #ifdef WITH_OPENSSL - case KEY_RSA_CERT: -- case KEY_RSA: -- return a->rsa != NULL && b->rsa != NULL && -- BN_cmp(a->rsa->e, b->rsa->e) == 0 && -- BN_cmp(a->rsa->n, b->rsa->n) == 0; -+ case KEY_RSA: { -+ const BIGNUM *a_e, *a_n, *b_e, *b_n; -+ -+ if (a->rsa == NULL || b->rsa == NULL) -+ return 0; -+ RSA_get0_key(a->rsa, &a_n, &a_e, NULL); -+ RSA_get0_key(b->rsa, &b_n, &b_e, NULL); -+ return BN_cmp(a_e, b_e) == 0 && BN_cmp(a_n, b_n) == 0; -+ } - case KEY_DSA_CERT: -- case KEY_DSA: -- return a->dsa != NULL && b->dsa != NULL && -- BN_cmp(a->dsa->p, b->dsa->p) == 0 && -- BN_cmp(a->dsa->q, b->dsa->q) == 0 && -- BN_cmp(a->dsa->g, b->dsa->g) == 0 && -- BN_cmp(a->dsa->pub_key, b->dsa->pub_key) == 0; -+ case KEY_DSA: { -+ const BIGNUM *a_p, *a_q, *a_g, *a_pub_key; -+ const BIGNUM *b_p, *b_q, *b_g, *b_pub_key; -+ -+ if (a->dsa == NULL || b->dsa == NULL) -+ return 0; -+ DSA_get0_pqg(a->dsa, &a_p, &a_q, &a_g); -+ DSA_get0_key(a->dsa, &a_pub_key, NULL); -+ DSA_get0_pqg(b->dsa, &b_p, &b_q, &b_g); -+ DSA_get0_key(b->dsa, &b_pub_key, NULL); -+ return BN_cmp(a_p, b_p) == 0 && -+ BN_cmp(a_q, b_q) == 0 && -+ BN_cmp(a_g, b_g) == 0 && -+ BN_cmp(a_pub_key, b_pub_key) == 0; -+ } - # ifdef OPENSSL_HAS_ECC - case KEY_ECDSA_CERT: - case KEY_ECDSA: -@@ -769,15 +767,21 @@ to_blob_buf(const struct sshkey *key, st - return ret; - break; - #ifdef WITH_OPENSSL -- case KEY_DSA: -- if (key->dsa == NULL) -- return SSH_ERR_INVALID_ARGUMENT; -- if ((ret = sshbuf_put_cstring(b, typename)) != 0 || -- (ret = sshbuf_put_bignum2(b, key->dsa->p)) != 0 || -- (ret = sshbuf_put_bignum2(b, key->dsa->q)) != 0 || -- (ret = sshbuf_put_bignum2(b, key->dsa->g)) != 0 || -- (ret = sshbuf_put_bignum2(b, key->dsa->pub_key)) != 0) -- return ret; -+ case KEY_DSA: { -+ const BIGNUM *p, *q, *g, *pub_key; -+ -+ if (key->dsa == NULL) -+ return SSH_ERR_INVALID_ARGUMENT; -+ -+ DSA_get0_pqg(key->dsa, &p, &q, &g); -+ DSA_get0_key(key->dsa, &pub_key, NULL); -+ if ((ret = sshbuf_put_cstring(b, typename)) != 0 || -+ (ret = sshbuf_put_bignum2(b, p)) != 0 || -+ (ret = sshbuf_put_bignum2(b, q)) != 0 || -+ (ret = sshbuf_put_bignum2(b, g)) != 0 || -+ (ret = sshbuf_put_bignum2(b, pub_key)) != 0) -+ return ret; -+ } - break; - # ifdef OPENSSL_HAS_ECC - case KEY_ECDSA: -@@ -790,13 +794,18 @@ to_blob_buf(const struct sshkey *key, st - return ret; - break; - # endif -- case KEY_RSA: -- if (key->rsa == NULL) -- return SSH_ERR_INVALID_ARGUMENT; -- if ((ret = sshbuf_put_cstring(b, typename)) != 0 || -- (ret = sshbuf_put_bignum2(b, key->rsa->e)) != 0 || -- (ret = sshbuf_put_bignum2(b, key->rsa->n)) != 0) -- return ret; -+ case KEY_RSA: { -+ const BIGNUM *e, *n; -+ -+ if (key->rsa == NULL) -+ return SSH_ERR_INVALID_ARGUMENT; -+ -+ RSA_get0_key(key->rsa, &n, &e, NULL); -+ if ((ret = sshbuf_put_cstring(b, typename)) != 0 || -+ (ret = sshbuf_put_bignum2(b, e)) != 0 || -+ (ret = sshbuf_put_bignum2(b, n)) != 0) -+ return ret; -+ } - break; - #endif /* WITH_OPENSSL */ - case KEY_ED25519: -@@ -1672,15 +1681,32 @@ sshkey_from_private(const struct sshkey - switch (k->type) { - #ifdef WITH_OPENSSL - case KEY_DSA: -- case KEY_DSA_CERT: -- if ((n = sshkey_new(k->type)) == NULL) -- return SSH_ERR_ALLOC_FAIL; -- if ((BN_copy(n->dsa->p, k->dsa->p) == NULL) || -- (BN_copy(n->dsa->q, k->dsa->q) == NULL) || -- (BN_copy(n->dsa->g, k->dsa->g) == NULL) || -- (BN_copy(n->dsa->pub_key, k->dsa->pub_key) == NULL)) { -- sshkey_free(n); -- return SSH_ERR_ALLOC_FAIL; -+ case KEY_DSA_CERT: { -+ const BIGNUM *k_p, *k_q, *k_g, *k_pub_key; -+ BIGNUM *n_p = NULL, *n_q = NULL, *n_g = NULL, *n_pub_key = NULL; -+ -+ if ((n = sshkey_new(k->type)) == NULL) -+ return SSH_ERR_ALLOC_FAIL; -+ -+ DSA_get0_pqg(k->dsa, &k_p, &k_q, &k_g); -+ DSA_get0_key(k->dsa, &k_pub_key, NULL); -+ -+ if (((n_p = BN_dup(k_p)) == NULL) || -+ ((n_q = BN_dup(k_q)) == NULL) || -+ ((n_g = BN_dup(k_g)) == NULL) || -+ (DSA_set0_pqg(n->dsa, n_p, n_q, n_g) == 0)) { -+ sshkey_free(n); -+ BN_free(n_p); -+ BN_free(n_q); -+ BN_free(n_g); -+ return SSH_ERR_ALLOC_FAIL; -+ } -+ if (((n_pub_key = BN_dup(k_pub_key)) == NULL) || -+ (DSA_set0_key(n->dsa, n_pub_key, NULL) == 0)) { -+ sshkey_free(n); -+ BN_free(n_pub_key); -+ return SSH_ERR_ALLOC_FAIL; -+ } - } - break; - # ifdef OPENSSL_HAS_ECC -@@ -1702,13 +1728,22 @@ sshkey_from_private(const struct sshkey - break; - # endif /* OPENSSL_HAS_ECC */ - case KEY_RSA: -- case KEY_RSA_CERT: -- if ((n = sshkey_new(k->type)) == NULL) -- return SSH_ERR_ALLOC_FAIL; -- if ((BN_copy(n->rsa->n, k->rsa->n) == NULL) || -- (BN_copy(n->rsa->e, k->rsa->e) == NULL)) { -- sshkey_free(n); -- return SSH_ERR_ALLOC_FAIL; -+ case KEY_RSA_CERT: { -+ const BIGNUM *k_n, *k_e; -+ BIGNUM *n_n = NULL, *n_e = NULL; -+ -+ if ((n = sshkey_new(k->type)) == NULL) -+ return SSH_ERR_ALLOC_FAIL; -+ -+ RSA_get0_key(k->rsa, &k_n, &k_e, NULL); -+ if (((n_n = BN_dup(k_n)) == NULL) || -+ ((n_e = BN_dup(k_e)) == NULL) || -+ RSA_set0_key(n->rsa, n_n, n_e, NULL) == 0) { -+ sshkey_free(n); -+ BN_free(n_n); -+ BN_free(n_e); -+ return SSH_ERR_ALLOC_FAIL; -+ } - } - break; - #endif /* WITH_OPENSSL */ -@@ -1907,12 +1942,22 @@ sshkey_from_blob_internal(struct sshbuf - ret = SSH_ERR_ALLOC_FAIL; - goto out; - } -- if (sshbuf_get_bignum2(b, key->rsa->e) != 0 || -- sshbuf_get_bignum2(b, key->rsa->n) != 0) { -- ret = SSH_ERR_INVALID_FORMAT; -- goto out; -+ { -+ BIGNUM *e, *n; -+ -+ e = BN_new(); -+ n = BN_new(); -+ if (e == NULL || n == NULL || -+ sshbuf_get_bignum2(b, e) != 0 || -+ sshbuf_get_bignum2(b, n) != 0 || -+ RSA_set0_key(key->rsa, n, e, NULL) == 0) { -+ BN_free(e); -+ BN_free(n); -+ ret = SSH_ERR_ALLOC_FAIL; -+ goto out; -+ } - } -- if (BN_num_bits(key->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE) { -+ if (RSA_bits(key->rsa) < SSH_RSA_MINIMUM_MODULUS_SIZE) { - ret = SSH_ERR_KEY_LENGTH; - goto out; - } -@@ -1932,12 +1977,34 @@ sshkey_from_blob_internal(struct sshbuf - ret = SSH_ERR_ALLOC_FAIL; - goto out; - } -- if (sshbuf_get_bignum2(b, key->dsa->p) != 0 || -- sshbuf_get_bignum2(b, key->dsa->q) != 0 || -- sshbuf_get_bignum2(b, key->dsa->g) != 0 || -- sshbuf_get_bignum2(b, key->dsa->pub_key) != 0) { -- ret = SSH_ERR_INVALID_FORMAT; -- goto out; -+ { -+ BIGNUM *p, *q, *g, *pub_key; -+ -+ p = BN_new(); -+ q = BN_new(); -+ g = BN_new(); -+ pub_key = BN_new(); -+ -+ if (p == NULL || q == NULL || g == NULL || -+ pub_key == NULL || -+ sshbuf_get_bignum2(b, p) != 0 || -+ sshbuf_get_bignum2(b, q) != 0 || -+ sshbuf_get_bignum2(b, g) != 0 || -+ sshbuf_get_bignum2(b, pub_key) != 0 || -+ DSA_set0_pqg(key->dsa, p, q, g) == 0) { -+ BN_free(p); -+ BN_free(q); -+ BN_free(g); -+ BN_free(pub_key); -+ ret = SSH_ERR_ALLOC_FAIL; -+ goto out; -+ } -+ -+ if (DSA_set0_key(key->dsa, pub_key, NULL) == 0) { -+ BN_free(pub_key); -+ ret = SSH_ERR_LIBCRYPTO_ERROR; -+ goto out; -+ } - } - #ifdef DEBUG_PK - DSA_print_fp(stderr, key->dsa, 8); -@@ -2171,26 +2238,53 @@ sshkey_demote(const struct sshkey *k, st - if ((ret = sshkey_cert_copy(k, pk)) != 0) - goto fail; - /* FALLTHROUGH */ -- case KEY_RSA: -- if ((pk->rsa = RSA_new()) == NULL || -- (pk->rsa->e = BN_dup(k->rsa->e)) == NULL || -- (pk->rsa->n = BN_dup(k->rsa->n)) == NULL) { -- ret = SSH_ERR_ALLOC_FAIL; -- goto fail; -+ case KEY_RSA: { -+ const BIGNUM *k_e, *k_n; -+ BIGNUM *pk_e = NULL, *pk_n = NULL; -+ -+ RSA_get0_key(k->rsa, &k_n, &k_e, NULL); -+ if ((pk->rsa = RSA_new()) == NULL || -+ (pk_e = BN_dup(k_e)) == NULL || -+ (pk_n = BN_dup(k_n)) == NULL || -+ RSA_set0_key(pk->rsa, pk_n, pk_e, NULL) == 0) { -+ BN_free(pk_e); -+ BN_free(pk_n); -+ ret = SSH_ERR_ALLOC_FAIL; -+ goto fail; - } -+ } - break; - case KEY_DSA_CERT: - if ((ret = sshkey_cert_copy(k, pk)) != 0) - goto fail; - /* FALLTHROUGH */ -- case KEY_DSA: -- if ((pk->dsa = DSA_new()) == NULL || -- (pk->dsa->p = BN_dup(k->dsa->p)) == NULL || -- (pk->dsa->q = BN_dup(k->dsa->q)) == NULL || -- (pk->dsa->g = BN_dup(k->dsa->g)) == NULL || -- (pk->dsa->pub_key = BN_dup(k->dsa->pub_key)) == NULL) { -- ret = SSH_ERR_ALLOC_FAIL; -- goto fail; -+ case KEY_DSA: { -+ const BIGNUM *k_p, *k_q, *k_g, *k_pub_key; -+ BIGNUM *pk_p = NULL, *pk_q = NULL, *pk_g = NULL; -+ BIGNUM *pk_pub_key = NULL; -+ -+ DSA_get0_pqg(k->dsa, &k_p, &k_q, &k_g); -+ DSA_get0_key(k->dsa, &k_pub_key, NULL); -+ -+ if ((pk->dsa = DSA_new()) == NULL || -+ (pk_p = BN_dup(k_p)) == NULL || -+ (pk_q = BN_dup(k_q)) == NULL || -+ (pk_g = BN_dup(k_g)) == NULL || -+ (pk_pub_key = BN_dup(k_pub_key)) == NULL || -+ DSA_set0_pqg(pk->dsa, pk_p, pk_q, pk_g) == 0) { -+ BN_free(pk_p); -+ BN_free(pk_q); -+ BN_free(pk_g); -+ BN_free(pk_pub_key); -+ ret = SSH_ERR_ALLOC_FAIL; -+ goto fail; -+ } -+ -+ if (DSA_set0_key(pk->dsa, pk_pub_key, NULL) == 0) { -+ BN_free(pk_pub_key); -+ ret = SSH_ERR_LIBCRYPTO_ERROR; -+ goto fail; -+ } - } - break; - case KEY_ECDSA_CERT: -@@ -2312,12 +2406,17 @@ sshkey_certify_custom(struct sshkey *k, - /* XXX this substantially duplicates to_blob(); refactor */ - switch (k->type) { - #ifdef WITH_OPENSSL -- case KEY_DSA_CERT: -- if ((ret = sshbuf_put_bignum2(cert, k->dsa->p)) != 0 || -- (ret = sshbuf_put_bignum2(cert, k->dsa->q)) != 0 || -- (ret = sshbuf_put_bignum2(cert, k->dsa->g)) != 0 || -- (ret = sshbuf_put_bignum2(cert, k->dsa->pub_key)) != 0) -- goto out; -+ case KEY_DSA_CERT: { -+ const BIGNUM *p, *q, *g, *pub_key; -+ -+ DSA_get0_pqg(k->dsa, &p, &q, &g); -+ DSA_get0_key(k->dsa, &pub_key, NULL); -+ if ((ret = sshbuf_put_bignum2(cert, p)) != 0 || -+ (ret = sshbuf_put_bignum2(cert, q)) != 0 || -+ (ret = sshbuf_put_bignum2(cert, g)) != 0 || -+ (ret = sshbuf_put_bignum2(cert, pub_key)) != 0) -+ goto out; -+ } - break; - # ifdef OPENSSL_HAS_ECC - case KEY_ECDSA_CERT: -@@ -2329,10 +2428,15 @@ sshkey_certify_custom(struct sshkey *k, - goto out; - break; - # endif /* OPENSSL_HAS_ECC */ -- case KEY_RSA_CERT: -- if ((ret = sshbuf_put_bignum2(cert, k->rsa->e)) != 0 || -- (ret = sshbuf_put_bignum2(cert, k->rsa->n)) != 0) -- goto out; -+ case KEY_RSA_CERT: { -+ const BIGNUM *e, *n; -+ -+ RSA_get0_key(k->rsa, &n, &e, NULL); -+ if (e == NULL || n == NULL || -+ (ret = sshbuf_put_bignum2(cert, e)) != 0 || -+ (ret = sshbuf_put_bignum2(cert, n)) != 0) -+ goto out; -+ } - break; - #endif /* WITH_OPENSSL */ - case KEY_ED25519_CERT: -@@ -2505,43 +2609,65 @@ sshkey_private_serialize(const struct ss - goto out; - switch (key->type) { - #ifdef WITH_OPENSSL -- case KEY_RSA: -- if ((r = sshbuf_put_bignum2(b, key->rsa->n)) != 0 || -- (r = sshbuf_put_bignum2(b, key->rsa->e)) != 0 || -- (r = sshbuf_put_bignum2(b, key->rsa->d)) != 0 || -- (r = sshbuf_put_bignum2(b, key->rsa->iqmp)) != 0 || -- (r = sshbuf_put_bignum2(b, key->rsa->p)) != 0 || -- (r = sshbuf_put_bignum2(b, key->rsa->q)) != 0) -- goto out; -+ case KEY_RSA: { -+ const BIGNUM *n, *e, *d, *iqmp, *p, *q; -+ RSA_get0_key(key->rsa, &n, &e, &d); -+ RSA_get0_crt_params(key->rsa, NULL, NULL, &iqmp); -+ RSA_get0_factors(key->rsa, &p, &q); -+ if ((r = sshbuf_put_bignum2(b, n)) != 0 || -+ (r = sshbuf_put_bignum2(b, e)) != 0 || -+ (r = sshbuf_put_bignum2(b, d)) != 0 || -+ (r = sshbuf_put_bignum2(b, iqmp)) != 0 || -+ (r = sshbuf_put_bignum2(b, p)) != 0 || -+ (r = sshbuf_put_bignum2(b, q)) != 0) -+ goto out; -+ } - break; - case KEY_RSA_CERT: - 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 || -- (r = sshbuf_put_bignum2(b, key->rsa->d)) != 0 || -- (r = sshbuf_put_bignum2(b, key->rsa->iqmp)) != 0 || -- (r = sshbuf_put_bignum2(b, key->rsa->p)) != 0 || -- (r = sshbuf_put_bignum2(b, key->rsa->q)) != 0) -- goto out; -+ { -+ const BIGNUM *d, *iqmp, *p, *q; -+ -+ RSA_get0_key(key->rsa, NULL, NULL, &d); -+ RSA_get0_factors(key->rsa, &p, &q); -+ RSA_get0_crt_params(key->rsa, NULL, NULL, &iqmp); -+ if ((r = sshbuf_put_stringb(b, key->cert->certblob)) != 0 || -+ (r = sshbuf_put_bignum2(b, d)) != 0 || -+ (r = sshbuf_put_bignum2(b, iqmp)) != 0 || -+ (r = sshbuf_put_bignum2(b, p)) != 0 || -+ (r = sshbuf_put_bignum2(b, q)) != 0) -+ goto out; -+ } - break; -- case KEY_DSA: -- if ((r = sshbuf_put_bignum2(b, key->dsa->p)) != 0 || -- (r = sshbuf_put_bignum2(b, key->dsa->q)) != 0 || -- (r = sshbuf_put_bignum2(b, key->dsa->g)) != 0 || -- (r = sshbuf_put_bignum2(b, key->dsa->pub_key)) != 0 || -- (r = sshbuf_put_bignum2(b, key->dsa->priv_key)) != 0) -- goto out; -+ case KEY_DSA: { -+ const BIGNUM *p, *q, *g, *pub_key, *priv_key; -+ -+ DSA_get0_pqg(key->dsa, &p, &q, &g); -+ DSA_get0_key(key->dsa, &pub_key, &priv_key); -+ if ((r = sshbuf_put_bignum2(b, p)) != 0 || -+ (r = sshbuf_put_bignum2(b, q)) != 0 || -+ (r = sshbuf_put_bignum2(b, g)) != 0 || -+ (r = sshbuf_put_bignum2(b, pub_key)) != 0 || -+ (r = sshbuf_put_bignum2(b, priv_key)) != 0) -+ goto out; -+ } - break; - case KEY_DSA_CERT: - 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 || -- (r = sshbuf_put_bignum2(b, key->dsa->priv_key)) != 0) -- goto out; -+ { -+ const BIGNUM *priv_key; -+ -+ DSA_get0_key(key->dsa, NULL, &priv_key); -+ if ((r = sshbuf_put_stringb(b, key->cert->certblob)) != 0 || -+ (r = sshbuf_put_bignum2(b, priv_key)) != 0) -+ goto out; -+ } - break; - # ifdef OPENSSL_HAS_ECC - case KEY_ECDSA: -@@ -2617,18 +2743,51 @@ sshkey_private_deserialize(struct sshbuf - r = SSH_ERR_ALLOC_FAIL; - goto out; - } -- if ((r = sshbuf_get_bignum2(buf, k->dsa->p)) != 0 || -- (r = sshbuf_get_bignum2(buf, k->dsa->q)) != 0 || -- (r = sshbuf_get_bignum2(buf, k->dsa->g)) != 0 || -- (r = sshbuf_get_bignum2(buf, k->dsa->pub_key)) != 0 || -- (r = sshbuf_get_bignum2(buf, k->dsa->priv_key)) != 0) -- goto out; -+ { -+ BIGNUM *p, *q, *g, *pub_key, *priv_key; -+ -+ p = BN_new(); -+ q = BN_new(); -+ g = BN_new(); -+ pub_key = BN_new(); -+ priv_key = BN_new(); -+ if (p == NULL || q == NULL || g == NULL || -+ pub_key == NULL || priv_key == NULL || -+ (r = sshbuf_get_bignum2(buf, p)) != 0 || -+ (r = sshbuf_get_bignum2(buf, q)) != 0 || -+ (r = sshbuf_get_bignum2(buf, g)) != 0 || -+ (r = sshbuf_get_bignum2(buf, pub_key)) != 0 || -+ (r = sshbuf_get_bignum2(buf, priv_key)) != 0 || -+ (r = ((DSA_set0_pqg(k->dsa, p, q, g) == 0) -+ ? SSH_ERR_LIBCRYPTO_ERROR : 0)) != 0) { -+ BN_free(p); -+ BN_free(q); -+ BN_free(g); -+ BN_free(pub_key); -+ BN_free(priv_key); -+ goto out; -+ } -+ if (DSA_set0_key(k->dsa, pub_key, priv_key) == 0) { -+ r = SSH_ERR_LIBCRYPTO_ERROR; -+ BN_free(pub_key); -+ BN_free(priv_key); -+ goto out; -+ } -+ } - break; -- case KEY_DSA_CERT: -- if ((r = sshkey_froms(buf, &k)) != 0 || -- (r = sshkey_add_private(k)) != 0 || -- (r = sshbuf_get_bignum2(buf, k->dsa->priv_key)) != 0) -- goto out; -+ case KEY_DSA_CERT: { -+ BIGNUM *priv_key = BN_new(); -+ -+ if (priv_key == NULL || -+ (r = sshkey_froms(buf, &k)) != 0 || -+ (r = sshkey_add_private(k)) != 0 || -+ (r = sshbuf_get_bignum2(buf, priv_key)) != 0 || -+ (r = ((DSA_set0_key(k->dsa, NULL, priv_key) == 0) -+ ? SSH_ERR_LIBCRYPTO_ERROR : 0)) != 0) { -+ BN_free(priv_key); -+ goto out; -+ } -+ } - break; - # ifdef OPENSSL_HAS_ECC - case KEY_ECDSA: -@@ -2687,29 +2846,89 @@ sshkey_private_deserialize(struct sshbuf - r = SSH_ERR_ALLOC_FAIL; - goto out; - } -- if ((r = sshbuf_get_bignum2(buf, k->rsa->n)) != 0 || -- (r = sshbuf_get_bignum2(buf, k->rsa->e)) != 0 || -- (r = sshbuf_get_bignum2(buf, k->rsa->d)) != 0 || -- (r = sshbuf_get_bignum2(buf, k->rsa->iqmp)) != 0 || -- (r = sshbuf_get_bignum2(buf, k->rsa->p)) != 0 || -- (r = sshbuf_get_bignum2(buf, k->rsa->q)) != 0 || -- (r = ssh_rsa_generate_additional_parameters(k)) != 0) -- goto out; -- if (BN_num_bits(k->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE) { -+ { -+ BIGNUM *n, *e, *d, *iqmp, *p, *q; -+ -+ n = BN_new(); -+ e = BN_new(); -+ d = BN_new(); -+ iqmp = BN_new(); -+ p = BN_new(); -+ q = BN_new(); -+ -+ if (n == NULL || e == NULL || d == NULL || -+ iqmp == NULL || p == NULL || q == NULL || -+ (r = sshbuf_get_bignum2(buf, n)) != 0 || -+ (r = sshbuf_get_bignum2(buf, e)) != 0 || -+ (r = sshbuf_get_bignum2(buf, d)) != 0 || -+ (r = sshbuf_get_bignum2(buf, iqmp)) != 0 || -+ (r = sshbuf_get_bignum2(buf, p)) != 0 || -+ (r = sshbuf_get_bignum2(buf, q)) != 0 || -+ (r = ((RSA_set0_key(k->rsa, n, e, d) == 0) -+ ? SSH_ERR_LIBCRYPTO_ERROR : 0)) != 0) { -+ BN_free(n); -+ BN_free(e); -+ BN_free(d); -+ BN_free(iqmp); -+ BN_free(p); -+ BN_free(q); -+ goto out; -+ } -+ if (RSA_set0_factors(k->rsa, p, q) == 0) { -+ r = SSH_ERR_LIBCRYPTO_ERROR; -+ BN_free(iqmp); -+ BN_free(p); -+ BN_free(q); -+ goto out; -+ } -+ if ((r = ssh_rsa_generate_additional_parameters(k, iqmp)) != 0) { -+ BN_free(iqmp); -+ goto out; -+ } -+ } -+ if (RSA_bits(k->rsa) < SSH_RSA_MINIMUM_MODULUS_SIZE) { - r = SSH_ERR_KEY_LENGTH; - goto out; - } - break; -- case KEY_RSA_CERT: -- if ((r = sshkey_froms(buf, &k)) != 0 || -- (r = sshkey_add_private(k)) != 0 || -- (r = sshbuf_get_bignum2(buf, k->rsa->d)) != 0 || -- (r = sshbuf_get_bignum2(buf, k->rsa->iqmp)) != 0 || -- (r = sshbuf_get_bignum2(buf, k->rsa->p)) != 0 || -- (r = sshbuf_get_bignum2(buf, k->rsa->q)) != 0 || -- (r = ssh_rsa_generate_additional_parameters(k)) != 0) -- goto out; -- if (BN_num_bits(k->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE) { -+ case KEY_RSA_CERT: { -+ BIGNUM *d, *iqmp, *p, *q; -+ -+ /* N and E are already set so make sure we will not overwrite them */ -+ d = BN_new(); -+ iqmp = BN_new(); -+ p = BN_new(); -+ q = BN_new(); -+ -+ if (d == NULL || iqmp == NULL || p == NULL || -+ q == NULL || -+ (r = sshkey_froms(buf, &k)) != 0 || -+ (r = sshkey_add_private(k)) != 0 || -+ (r = sshbuf_get_bignum2(buf, d)) != 0 || -+ (r = sshbuf_get_bignum2(buf, iqmp)) != 0 || -+ (r = sshbuf_get_bignum2(buf, p)) != 0 || -+ (r = sshbuf_get_bignum2(buf, q)) != 0 || -+ (r = ((RSA_set0_key(k->rsa, NULL, NULL, d) == 0) -+ ? SSH_ERR_LIBCRYPTO_ERROR : 0)) != 0) { -+ BN_free(d); -+ BN_free(iqmp); -+ BN_free(p); -+ BN_free(q); -+ goto out; -+ } -+ if (RSA_set0_factors(k->rsa, p, q) == 0) { -+ r = SSH_ERR_LIBCRYPTO_ERROR; -+ BN_free(p); -+ BN_free(q); -+ goto out; -+ } -+ if (ssh_rsa_generate_additional_parameters(k, iqmp) != 0) { -+ r = SSH_ERR_LIBCRYPTO_ERROR; -+ free(iqmp); -+ goto out; -+ } -+ } -+ if (RSA_bits(k->rsa) < SSH_RSA_MINIMUM_MODULUS_SIZE) { - r = SSH_ERR_KEY_LENGTH; - goto out; - } -@@ -3427,7 +3646,9 @@ translate_libcrypto_error(unsigned long - switch (pem_reason) { - case EVP_R_BAD_DECRYPT: - return SSH_ERR_KEY_WRONG_PASSPHRASE; -+#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: -@@ -3492,7 +3713,7 @@ sshkey_parse_private_pem_fileblob(struct - r = convert_libcrypto_error(); - goto out; - } -- if (pk->type == EVP_PKEY_RSA && -+ if (EVP_PKEY_id(pk) == EVP_PKEY_RSA && - (type == KEY_UNSPEC || type == KEY_RSA)) { - if ((prv = sshkey_new(KEY_UNSPEC)) == NULL) { - r = SSH_ERR_ALLOC_FAIL; -@@ -3507,11 +3728,11 @@ sshkey_parse_private_pem_fileblob(struct - r = SSH_ERR_LIBCRYPTO_ERROR; - goto out; - } -- if (BN_num_bits(prv->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE) { -+ if (RSA_bits(prv->rsa) < SSH_RSA_MINIMUM_MODULUS_SIZE) { - r = SSH_ERR_KEY_LENGTH; - goto out; - } -- } else if (pk->type == EVP_PKEY_DSA && -+ } else if (EVP_PKEY_id(pk) == EVP_PKEY_DSA && - (type == KEY_UNSPEC || type == KEY_DSA)) { - if ((prv = sshkey_new(KEY_UNSPEC)) == NULL) { - r = SSH_ERR_ALLOC_FAIL; -@@ -3523,7 +3744,7 @@ sshkey_parse_private_pem_fileblob(struct - DSA_print_fp(stderr, prv->dsa, 8); - #endif - #ifdef OPENSSL_HAS_ECC -- } else if (pk->type == EVP_PKEY_EC && -+ } else if (EVP_PKEY_id(pk) == EVP_PKEY_EC && - (type == KEY_UNSPEC || type == KEY_ECDSA)) { - if ((prv = sshkey_new(KEY_UNSPEC)) == NULL) { - r = SSH_ERR_ALLOC_FAIL; -diff -up openssh/ssh-keygen.c.openssl openssh/ssh-keygen.c ---- openssh/ssh-keygen.c.openssl 2017-09-26 13:19:31.787249636 +0200 -+++ openssh/ssh-keygen.c 2017-09-26 13:19:31.802249728 +0200 -@@ -501,40 +501,67 @@ do_convert_private_ssh2_from_blob(u_char - free(type); - - switch (key->type) { -- case KEY_DSA: -- buffer_get_bignum_bits(b, key->dsa->p); -- buffer_get_bignum_bits(b, key->dsa->g); -- buffer_get_bignum_bits(b, key->dsa->q); -- buffer_get_bignum_bits(b, key->dsa->pub_key); -- buffer_get_bignum_bits(b, key->dsa->priv_key); -+ case KEY_DSA: { -+ BIGNUM *p = NULL, *g = NULL, *q = NULL, *pub_key = NULL, *priv_key = NULL; -+ -+ if ((p = BN_new()) == NULL || -+ (g = BN_new()) == NULL || -+ (q = BN_new()) == NULL || -+ (pub_key = BN_new()) == NULL || -+ (priv_key = BN_new()) == NULL) -+ fatal("BN_new() failed"); -+ buffer_get_bignum_bits(b, p); -+ buffer_get_bignum_bits(b, g); -+ buffer_get_bignum_bits(b, q); -+ buffer_get_bignum_bits(b, pub_key); -+ buffer_get_bignum_bits(b, priv_key); -+ if (DSA_set0_pqg(key->dsa, p, q, g) == 0 || -+ DSA_set0_key(key->dsa, pub_key, priv_key) == 0) { -+ fatal("failed to set DSA key"); -+ } -+ } - 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("%s: buffer error: %s", __func__, ssh_err(r)); -- e = e1; -- debug("e %lx", e); -- if (e < 30) { -- e <<= 8; -- e += e2; -- debug("e %lx", e); -- e <<= 8; -- e += e3; -+ case KEY_RSA: { -+ BIGNUM *bn_e = NULL, *bn_d = NULL, *bn_n = NULL, *bn_iqmp = NULL, *bn_p = NULL, *bn_q = NULL; -+ -+ if ((bn_e = BN_new()) == NULL || -+ (bn_d = BN_new()) == NULL || -+ (bn_n = BN_new()) == NULL || -+ (bn_iqmp = BN_new()) == NULL || -+ (bn_p = BN_new()) == NULL || -+ (bn_q = BN_new()) == NULL) -+ fatal("BN_new() failed"); -+ -+ 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("%s: buffer error: %s", __func__, ssh_err(r)); -+ 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 (!BN_set_word(bn_e, e)) { -+ sshbuf_free(b); -+ sshkey_free(key); -+ return NULL; -+ } -+ buffer_get_bignum_bits(b, bn_d); -+ buffer_get_bignum_bits(b, bn_n); -+ buffer_get_bignum_bits(b, bn_iqmp); -+ buffer_get_bignum_bits(b, bn_q); -+ buffer_get_bignum_bits(b, bn_p); -+ if (RSA_set0_key(key->rsa, bn_n, bn_e, bn_d) == 0 || -+ RSA_set0_factors(key->rsa, bn_p, bn_q) == 0) -+ fatal("Failed to set RSA parameters"); -+ if ((r = ssh_rsa_generate_additional_parameters(key, bn_iqmp)) != 0) -+ fatal("generate RSA parameters failed: %s", ssh_err(r)); - } -- if (!BN_set_word(key->rsa->e, e)) { -- sshbuf_free(b); -- sshkey_free(key); -- return NULL; -- } -- buffer_get_bignum_bits(b, key->rsa->d); -- buffer_get_bignum_bits(b, key->rsa->n); -- buffer_get_bignum_bits(b, key->rsa->iqmp); -- buffer_get_bignum_bits(b, key->rsa->q); -- buffer_get_bignum_bits(b, key->rsa->p); -- if ((r = ssh_rsa_generate_additional_parameters(key)) != 0) -- fatal("generate RSA parameters failed: %s", ssh_err(r)); - break; - } - rlen = sshbuf_len(b); -@@ -642,7 +669,7 @@ do_convert_from_pkcs8(struct sshkey **k, - identity_file); - } - fclose(fp); -- switch (EVP_PKEY_type(pubkey->type)) { -+ switch (EVP_PKEY_base_id(pubkey)) { - case EVP_PKEY_RSA: - if ((*k = sshkey_new(KEY_UNSPEC)) == NULL) - fatal("sshkey_new failed"); -@@ -666,7 +693,7 @@ do_convert_from_pkcs8(struct sshkey **k, - #endif - default: - fatal("%s: unsupported pubkey type %d", __func__, -- EVP_PKEY_type(pubkey->type)); -+ EVP_PKEY_base_id(pubkey)); - } - EVP_PKEY_free(pubkey); - return; -@@ -1798,6 +1825,7 @@ do_ca_sign(struct passwd *pw, int argc, - #ifdef ENABLE_PKCS11 - pkcs11_terminate(); - #endif -+ free(ca); - exit(0); - } - -diff -up openssh/sshkey.h.openssl openssh/sshkey.h ---- openssh/sshkey.h.openssl 2017-09-26 13:19:31.780249593 +0200 -+++ openssh/sshkey.h 2017-09-26 13:19:31.803249734 +0200 -@@ -199,7 +199,7 @@ int sshkey_parse_private_fileblob_type(s - const char *passphrase, struct sshkey **keyp, char **commentp); - - /* XXX should be internal, but used by ssh-keygen */ --int ssh_rsa_generate_additional_parameters(struct sshkey *); -+int ssh_rsa_generate_additional_parameters(struct sshkey *, BIGNUM *iqmp); - - /* stateful keys (e.g. XMSS) */ - #ifdef NO_ATTRIBUTE_ON_PROTOTYPE_ARGS -diff -up openssh/ssh-pkcs11-client.c.openssl openssh/ssh-pkcs11-client.c ---- openssh/ssh-pkcs11-client.c.openssl 2017-09-19 06:26:43.000000000 +0200 -+++ openssh/ssh-pkcs11-client.c 2017-09-26 13:19:31.803249734 +0200 -@@ -143,12 +143,16 @@ pkcs11_rsa_private_encrypt(int flen, con - static int - wrap_rsa_key(RSA *rsa) - { -- static RSA_METHOD helper_rsa; -+ static RSA_METHOD *helper_rsa; - -- memcpy(&helper_rsa, RSA_get_default_method(), sizeof(helper_rsa)); -- helper_rsa.name = "ssh-pkcs11-helper"; -- helper_rsa.rsa_priv_enc = pkcs11_rsa_private_encrypt; -- RSA_set_method(rsa, &helper_rsa); -+ if (helper_rsa == NULL) { -+ helper_rsa = RSA_meth_dup(RSA_get_default_method()); -+ if (helper_rsa == NULL) -+ error("RSA_meth_dup failed"); -+ RSA_meth_set1_name(helper_rsa, "ssh-pkcs11-helper"); -+ RSA_meth_set_priv_enc(helper_rsa, pkcs11_rsa_private_encrypt); -+ } -+ RSA_set_method(rsa, helper_rsa); - return (0); - } - -diff -up openssh/ssh-pkcs11.c.openssl openssh/ssh-pkcs11.c ---- openssh/ssh-pkcs11.c.openssl 2017-09-19 06:26:43.000000000 +0200 -+++ openssh/ssh-pkcs11.c 2017-09-26 13:19:31.803249734 +0200 -@@ -67,7 +67,7 @@ struct pkcs11_key { - CK_ULONG slotidx; - CK_ULONG key_type; - int (*orig_finish)(RSA *rsa); -- RSA_METHOD rsa_method; -+ RSA_METHOD *rsa_method; - char *keyid; - int keyid_len; - char *label; -@@ -183,6 +183,7 @@ pkcs11_rsa_finish(RSA *rsa) - pkcs11_provider_unref(k11->provider); - free(k11->keyid); - free(k11->label); -+ RSA_meth_free(k11->rsa_method); - free(k11); - } - return (rv); -@@ -326,13 +326,21 @@ pkcs11_rsa_wrap(struct pkcs11_provider * - memcpy(k11->label, label_attrib->pValue, label_attrib->ulValueLen); - k11->label[label_attrib->ulValueLen] = 0; - } -- k11->orig_finish = def->finish; -- memcpy(&k11->rsa_method, def, sizeof(k11->rsa_method)); -- k11->rsa_method.name = "pkcs11"; -- k11->rsa_method.rsa_priv_enc = pkcs11_rsa_private_encrypt; -- k11->rsa_method.rsa_priv_dec = pkcs11_rsa_private_decrypt; -- k11->rsa_method.finish = pkcs11_rsa_finish; -- RSA_set_method(rsa, &k11->rsa_method); -+ k11->orig_finish = RSA_meth_get_finish(def); -+ if ((k11->rsa_method = RSA_meth_dup(def)) == NULL || -+ RSA_meth_set1_name(k11->rsa_method, "pkcs11") == 0 || -+ RSA_meth_set_priv_enc(k11->rsa_method, pkcs11_rsa_private_encrypt) == 0 || -+ RSA_meth_set_priv_dec(k11->rsa_method, pkcs11_rsa_private_decrypt) == 0 || -+ RSA_meth_set_finish(k11->rsa_method, pkcs11_rsa_finish) == 0) { -+ RSA_meth_free(k11->rsa_method); -+ k11->rsa_method = NULL; -+ pkcs11_provider_unref(k11->provider); -+ free(k11->keyid); -+ free(k11); -+ return (-1); -+ } -+ -+ RSA_set_method(rsa, k11->rsa_method); - RSA_set_app_data(rsa, k11); - return (0); - } -@@ -460,6 +468,7 @@ pkcs11_fetch_keys_filter(struct pkcs11_p - CK_ULONG nfound; - CK_SESSION_HANDLE session; - CK_FUNCTION_LIST *f; -+ const BIGNUM *n, *e; - - f = p->module->function_list; - session = p->module->slotinfo[slotidx].session; -@@ -512,10 +521,16 @@ pkcs11_fetch_keys_filter(struct pkcs11_p - if ((rsa = RSA_new()) == NULL) { - error("RSA_new failed"); - } else { -- rsa->n = BN_bin2bn(attribs[2].pValue, -+ BIGNUM *rsa_n, *rsa_e; -+ -+ rsa_n = BN_bin2bn(attribs[2].pValue, - attribs[2].ulValueLen, NULL); -- rsa->e = BN_bin2bn(attribs[3].pValue, -+ rsa_e = BN_bin2bn(attribs[3].pValue, - attribs[3].ulValueLen, NULL); -+ if (rsa_n == NULL || rsa_e == NULL) -+ error("BN_bin2bn failed"); -+ if (RSA_set0_key(rsa, rsa_n, rsa_e, NULL) == 0) -+ error("RSA_set0_key failed"); - } - #ifdef ENABLE_PKCS11_ECDSA - } else if (attribs[2].type == CKA_EC_PARAMS ) { -@@ -920,19 +936,19 @@ pkcs11_fetch_keys_filter(struct pkcs11_p - } else if ((evp = X509_get_pubkey(x509)) == NULL) { - debug("X509_get_pubkey failed"); - } else { -- switch (evp->type) { -+ switch (EVP_PKEY_id(evp)) { - case EVP_PKEY_RSA: -- if (evp->pkey.rsa == NULL) -+ if (EVP_PKEY_get0_RSA(evp) == NULL) - debug("Missing RSA key"); -- else if ((rsa = RSAPublicKey_dup( -- evp->pkey.rsa)) == NULL) -+ else if ((rsa = RSAPublicKey_dup( -+ EVP_PKEY_get0_RSA(evp))) == NULL) - error("RSAPublicKey_dup failed"); - break; - case EVP_PKEY_EC: -- if (evp->pkey.ecdsa == NULL) -+ if (EVP_PKEY_get0_EC_KEY(evp) == NULL) - debug("Missing ECDSA key"); -- else if ((ecdsa = EC_KEY_dup( -- evp->pkey.ecdsa)) == NULL) -+ else if ((ecdsa = EC_KEY_dup( -+ EVP_PKEY_get0_EC_KEY(evp))) == NULL) - error("EC_KEY_dup failed"); - break; - default: -@@ -538,7 +551,9 @@ pkcs11_fetch_keys_filter(struct pkcs11_p - } - key = NULL; - if (rsa || ecdsa) { -- if (rsa && rsa->n && rsa->e && -+ if (rsa) -+ RSA_get0_key(rsa, &n, &e, NULL); -+ if (rsa && n && e && - pkcs11_rsa_wrap(p, slotidx, &attribs[0], &attribs[1], rsa) == 0) { - if ((key = sshkey_new(KEY_UNSPEC)) == NULL) - fatal("sshkey_new failed"); -diff -up openssh/ssh-rsa.c.openssl openssh/ssh-rsa.c ---- openssh/ssh-rsa.c.openssl 2017-09-19 06:26:43.000000000 +0200 -+++ openssh/ssh-rsa.c 2017-09-26 13:19:31.803249734 +0200 -@@ -78,38 +78,50 @@ rsa_hash_alg_nid(int type) - } - - int --ssh_rsa_generate_additional_parameters(struct sshkey *key) -+ssh_rsa_generate_additional_parameters(struct sshkey *key, BIGNUM *iqmp) - { - BIGNUM *aux = NULL; - BN_CTX *ctx = NULL; -- BIGNUM d; -+ BIGNUM *d = NULL; - int r; -+ const BIGNUM *p, *q, *rsa_d; -+ BIGNUM *dmp1 = NULL, *dmq1 = NULL; - - if (key == NULL || key->rsa == NULL || - sshkey_type_plain(key->type) != KEY_RSA) - return SSH_ERR_INVALID_ARGUMENT; - -- if ((ctx = BN_CTX_new()) == NULL) -- return SSH_ERR_ALLOC_FAIL; -- if ((aux = BN_new()) == NULL) { -+ RSA_get0_factors(key->rsa, &p, &q); -+ RSA_get0_key(key->rsa, NULL, NULL, &rsa_d); -+ -+ if ((ctx = BN_CTX_new()) == NULL || -+ (aux = BN_new()) == NULL || -+ (d = BN_new()) == NULL || -+ (dmp1 = BN_new()) == NULL || -+ (dmq1 = BN_new()) == NULL) { - r = SSH_ERR_ALLOC_FAIL; - goto out; - } - BN_set_flags(aux, BN_FLG_CONSTTIME); - -- BN_init(&d); -- BN_with_flags(&d, key->rsa->d, BN_FLG_CONSTTIME); -+ BN_with_flags(d, rsa_d, BN_FLG_CONSTTIME); - -- if ((BN_sub(aux, key->rsa->q, BN_value_one()) == 0) || -- (BN_mod(key->rsa->dmq1, &d, aux, ctx) == 0) || -- (BN_sub(aux, key->rsa->p, BN_value_one()) == 0) || -- (BN_mod(key->rsa->dmp1, &d, aux, ctx) == 0)) { -+ if ((BN_sub(aux, q, BN_value_one()) == 0) || -+ (BN_mod(dmq1, d, aux, ctx) == 0) || -+ (BN_sub(aux, p, BN_value_one()) == 0) || -+ (BN_mod(dmp1, d, aux, ctx) == 0) || -+ (RSA_set0_crt_params(key->rsa, dmp1, dmq1, iqmp) == 0)) { - r = SSH_ERR_LIBCRYPTO_ERROR; - goto out; - } -+ dmp1 = NULL; -+ dmq1 = NULL; - r = 0; - out: -+ BN_free(d); - BN_clear_free(aux); -+ BN_clear_free(dmp1); -+ BN_clear_free(dmq1); - BN_CTX_free(ctx); - return r; - } -@@ -136,7 +145,7 @@ ssh_rsa_sign(const struct sshkey *key, u - if (key == NULL || key->rsa == NULL || hash_alg == -1 || - sshkey_type_plain(key->type) != KEY_RSA) - return SSH_ERR_INVALID_ARGUMENT; -- if (BN_num_bits(key->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE) -+ if (RSA_bits(key->rsa) < SSH_RSA_MINIMUM_MODULUS_SIZE) - return SSH_ERR_KEY_LENGTH; - slen = RSA_size(key->rsa); - if (slen <= 0 || slen > SSHBUF_MAX_BIGNUM) -@@ -210,7 +219,7 @@ ssh_rsa_verify(const struct sshkey *key, - sshkey_type_plain(key->type) != KEY_RSA || - sig == NULL || siglen == 0) - return SSH_ERR_INVALID_ARGUMENT; -- if (BN_num_bits(key->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE) -+ if (RSA_bits(key->rsa) < SSH_RSA_MINIMUM_MODULUS_SIZE) - return SSH_ERR_KEY_LENGTH; - - if ((b = sshbuf_from(sig, siglen)) == NULL) ---- openssh-7.6p1/configure.ac.openssl 2017-10-04 15:47:48.440672023 +0200 -+++ openssh-7.6p1/configure.ac 2017-10-04 15:47:23.327531495 +0200 -@@ -2730,6 +2730,7 @@ - AC_MSG_ERROR([OpenSSL >= 1.0.1 required (have "$ssl_library_ver")]) - ;; - 100*) ;; # 1.0.x -+ 101*) ;; # 1.1.x is supported by this patch too - 200*) ;; # LibreSSL - *) - AC_MSG_ERROR([OpenSSL >= 1.1.0 is not yet supported (have "$ssl_library_ver")]) diff --git a/openssh-7.5p1-gssapi-kex-with-ec.patch b/openssh-7.5p1-gssapi-kex-with-ec.patch deleted file mode 100644 index 5f2763a..0000000 --- a/openssh-7.5p1-gssapi-kex-with-ec.patch +++ /dev/null @@ -1,1377 +0,0 @@ -From 6ff8f667f792052fd47689c3e421fcd6ddca1cd0 Mon Sep 17 00:00:00 2001 -From: Jakub Jelen -Date: Fri, 25 Aug 2017 19:15:48 +0200 -Subject: [PATCH 1/3] GSSAPI Key exchange methods with DH and SHA2 - ---- - gss-genr.c | 10 ++++++++++ - kex.c | 2 ++ - kex.h | 2 ++ - kexgssc.c | 6 ++++++ - kexgsss.c | 6 ++++++ - monitor.c | 2 ++ - regress/kextype.sh | 4 +++- - regress/rekey.sh | 8 ++++++-- - ssh-gss.h | 2 ++ - ssh_config.5 | 4 +++- - sshconnect2.c | 2 ++ - sshd.c | 2 ++ - sshd_config.5 | 4 +++- - 13 files changed, 49 insertions(+), 5 deletions(-) - -diff --git a/gss-genr.c b/gss-genr.c -index dc63682d..c6eff3d7 100644 ---- a/gss-genr.c -+++ b/gss-genr.c -@@ -183,6 +183,16 @@ ssh_gssapi_id_kex(Gssctxt *ctx, char *name, int kex_type) { - return GSS_C_NO_OID; - name += sizeof(KEX_GSS_GRP14_SHA1_ID) - 1; - break; -+ case KEX_GSS_GRP14_SHA256: -+ if (strlen(name) < sizeof(KEX_GSS_GRP14_SHA256_ID)) -+ return GSS_C_NO_OID; -+ name += sizeof(KEX_GSS_GRP14_SHA256_ID) - 1; -+ break; -+ case KEX_GSS_GRP16_SHA512: -+ if (strlen(name) < sizeof(KEX_GSS_GRP16_SHA512_ID)) -+ return GSS_C_NO_OID; -+ name += sizeof(KEX_GSS_GRP16_SHA512_ID) - 1; -+ break; - case KEX_GSS_GEX_SHA1: - if (strlen(name) < sizeof(KEX_GSS_GEX_SHA1_ID)) - return GSS_C_NO_OID; -diff --git a/kex.c b/kex.c -index 63e028fa..e798fecb 100644 ---- a/kex.c -+++ b/kex.c -@@ -112,6 +112,8 @@ static const struct kexalg kexalgs[] = { - { KEX_GSS_GEX_SHA1_ID, KEX_GSS_GEX_SHA1, 0, SSH_DIGEST_SHA1 }, - { KEX_GSS_GRP1_SHA1_ID, KEX_GSS_GRP1_SHA1, 0, SSH_DIGEST_SHA1 }, - { KEX_GSS_GRP14_SHA1_ID, KEX_GSS_GRP14_SHA1, 0, SSH_DIGEST_SHA1 }, -+ { KEX_GSS_GRP14_SHA256_ID, KEX_GSS_GRP14_SHA256, 0, SSH_DIGEST_SHA256 }, -+ { KEX_GSS_GRP16_SHA512_ID, KEX_GSS_GRP16_SHA512, 0, SSH_DIGEST_SHA512 }, - #endif - { NULL, -1, -1, -1}, - }; -diff --git a/kex.h b/kex.h -index 8a2b37c5..f27958ae 100644 ---- a/kex.h -+++ b/kex.h -@@ -102,6 +102,8 @@ enum kex_exchange { - #ifdef GSSAPI - KEX_GSS_GRP1_SHA1, - KEX_GSS_GRP14_SHA1, -+ KEX_GSS_GRP14_SHA256, -+ KEX_GSS_GRP16_SHA512, - KEX_GSS_GEX_SHA1, - #endif - KEX_MAX -diff --git a/kexgssc.c b/kexgssc.c -index 132df8b5..ed23f06d 100644 ---- a/kexgssc.c -+++ b/kexgssc.c -@@ -88,8 +88,12 @@ kexgss_client(struct ssh *ssh) { - dh = dh_new_group1(); - break; - case KEX_GSS_GRP14_SHA1: -+ case KEX_GSS_GRP14_SHA256: - dh = dh_new_group14(); - break; -+ case KEX_GSS_GRP16_SHA512: -+ dh = dh_new_group16(); -+ break; - case KEX_GSS_GEX_SHA1: - debug("Doing group exchange\n"); - nbits = dh_estimate(ssh->kex->we_need * 8); -@@ -272,6 +276,8 @@ kexgss_client(struct ssh *ssh) { - switch (ssh->kex->kex_type) { - case KEX_GSS_GRP1_SHA1: - case KEX_GSS_GRP14_SHA1: -+ case KEX_GSS_GRP14_SHA256: -+ case KEX_GSS_GRP16_SHA512: - kex_dh_hash(ssh->kex->hash_alg, ssh->kex->client_version_string, - ssh->kex->server_version_string, - sshbuf_ptr(ssh->kex->my), sshbuf_len(ssh->kex->my), -diff --git a/kexgsss.c b/kexgsss.c -index 82a715cc..b7da8823 100644 ---- a/kexgsss.c -+++ b/kexgsss.c -@@ -104,8 +104,12 @@ kexgss_server(struct ssh *ssh) - dh = dh_new_group1(); - break; - case KEX_GSS_GRP14_SHA1: -+ case KEX_GSS_GRP14_SHA256: - dh = dh_new_group14(); - break; -+ case KEX_GSS_GRP16_SHA512: -+ dh = dh_new_group16(); -+ break; - case KEX_GSS_GEX_SHA1: - debug("Doing group exchange"); - packet_read_expect(SSH2_MSG_KEXGSS_GROUPREQ); -@@ -223,6 +227,8 @@ kexgss_server(struct ssh *ssh) - switch (ssh->kex->kex_type) { - case KEX_GSS_GRP1_SHA1: - case KEX_GSS_GRP14_SHA1: -+ case KEX_GSS_GRP14_SHA256: -+ case KEX_GSS_GRP16_SHA512: - kex_dh_hash(ssh->kex->hash_alg, - ssh->kex->client_version_string, ssh->kex->server_version_string, - sshbuf_ptr(ssh->kex->peer), sshbuf_len(ssh->kex->peer), -diff --git a/monitor.c b/monitor.c -index 17046936..d6bc7ac7 100644 ---- a/monitor.c -+++ b/monitor.c -@@ -1648,6 +1648,8 @@ monitor_apply_keystate(struct monitor *pmonitor) - if (options.gss_keyex) { - kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server; - kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server; -+ kex->kex[KEX_GSS_GRP14_SHA256] = kexgss_server; -+ kex->kex[KEX_GSS_GRP16_SHA512] = kexgss_server; - kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server; - } - #endif -diff --git a/regress/kextype.sh b/regress/kextype.sh -index 780362ca..45f4f16d 100644 ---- a/regress/kextype.sh -+++ b/regress/kextype.sh -@@ -14,7 +14,9 @@ echo "KexAlgorithms=$KEXOPT" >> $OBJ/sshd_proxy - - tries="1 2 3 4" - for k in `${SSH} -Q kex`; do -- if [ $k = "gss-gex-sha1-" -o $k = "gss-group1-sha1-" -o $k = "gss-group14-sha1-" ]; then -+ if [ $k = "gss-gex-sha1-" -o $k = "gss-group1-sha1-" -o \ -+ $k = "gss-group14-sha1-" -o $k = "gss-group14-sha256-" -o \ -+ $k = "gss-group16-sha512-" ]; then - continue - fi - verbose "kex $k" -diff --git a/regress/rekey.sh b/regress/rekey.sh -index 9fbe9b38..a2921bef 100644 ---- a/regress/rekey.sh -+++ b/regress/rekey.sh -@@ -38,7 +38,9 @@ increase_datafile_size 300 - - opts="" - for i in `${SSH} -Q kex`; do -- if [ $i = "gss-gex-sha1-" -o $i = "gss-group1-sha1-" -o $i = "gss-group14-sha1-" ]; then -+ if [ $i = "gss-gex-sha1-" -o $i = "gss-group1-sha1-" -o \ -+ $i = "gss-group14-sha1-" -o $i = "gss-group14-sha256-" -o \ -+ $i = "gss-group16-sha512-" ]; then - continue - fi - opts="$opts KexAlgorithms=$i" -@@ -59,7 +61,9 @@ done - if ${SSH} -Q cipher-auth | grep '^.*$' >/dev/null 2>&1 ; then - for c in `${SSH} -Q cipher-auth`; do - for kex in `${SSH} -Q kex`; do -- if [ $kex = "gss-gex-sha1-" -o $kex = "gss-group1-sha1-" -o $kex = "gss-group14-sha1-" ]; then -+ if [ $kex = "gss-gex-sha1-" -o $kex = "gss-group1-sha1-" -o \ -+ $kex = "gss-group14-sha1-" -o $kex = "gss-group14-sha256-" -o \ -+ $kex = "gss-group16-sha512-" ]; then - continue - fi - verbose "client rekey $c $kex" -diff --git a/ssh-gss.h b/ssh-gss.h -index 6b6adb2b..7bf8d75e 100644 ---- a/ssh-gss.h -+++ b/ssh-gss.h -@@ -70,6 +70,8 @@ - #define SSH2_MSG_KEXGSS_GROUP 41 - #define KEX_GSS_GRP1_SHA1_ID "gss-group1-sha1-" - #define KEX_GSS_GRP14_SHA1_ID "gss-group14-sha1-" -+#define KEX_GSS_GRP14_SHA256_ID "gss-group14-sha256-" -+#define KEX_GSS_GRP16_SHA512_ID "gss-group16-sha512-" - #define KEX_GSS_GEX_SHA1_ID "gss-gex-sha1-" - - #define GSS_KEX_DEFAULT_KEX \ -diff --git a/ssh_config.5 b/ssh_config.5 -index 6b24649e..3d6da510 100644 ---- a/ssh_config.5 -+++ b/ssh_config.5 -@@ -760,7 +760,9 @@ key exchange. Possible values are - .Bd -literal -offset 3n - gss-gex-sha1-, - gss-group1-sha1-, --gss-group14-sha1- -+gss-group14-sha1-, -+gss-group14-sha256-, -+gss-group16-sha512- - .Ed - .Pp - The default is -diff --git a/sshconnect2.c b/sshconnect2.c -index 8db98293..5d6b8be0 100644 ---- a/sshconnect2.c -+++ b/sshconnect2.c -@@ -253,6 +253,8 @@ ssh_kex2(char *host, struct sockaddr *hostaddr, u_short port) - if (options.gss_keyex) { - kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_client; - kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_client; -+ kex->kex[KEX_GSS_GRP14_SHA256] = kexgss_client; -+ kex->kex[KEX_GSS_GRP16_SHA512] = kexgss_client; - kex->kex[KEX_GSS_GEX_SHA1] = kexgss_client; - } - #endif -diff --git a/sshd.c b/sshd.c -index 895df26f..e4c879a2 100644 ---- a/sshd.c -+++ b/sshd.c -@@ -2244,6 +2244,8 @@ do_ssh2_kex(void) - if (options.gss_keyex) { - kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server; - kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server; -+ kex->kex[KEX_GSS_GRP14_SHA256] = kexgss_server; -+ kex->kex[KEX_GSS_GRP16_SHA512] = kexgss_server; - kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server; - } - #endif -diff --git a/sshd_config.5 b/sshd_config.5 -index bf81f6af..0793418b 100644 ---- a/sshd_config.5 -+++ b/sshd_config.5 -@@ -675,7 +675,9 @@ key exchange. Possible values are - .Bd -literal -offset 3n - gss-gex-sha1-, - gss-group1-sha1-, --gss-group14-sha1- -+gss-group14-sha1-, -+gss-group14-sha256-, -+gss-group16-sha512- - .Ed - .Pp - The default is --- -2.13.5 - - -From 7d56144903fc625c33da7fabf103f4f6bba4d43a Mon Sep 17 00:00:00 2001 -From: Jakub Jelen -Date: Tue, 29 Aug 2017 15:32:14 +0200 -Subject: [PATCH 2/3] GSSAPI Key exchange using ECDH and SHA2 - ---- - gss-genr.c | 10 ++ - kex.c | 3 + - kex.h | 4 + - kexgssc.c | 392 ++++++++++++++++++++++++++++++++++++++++++++++++++++- - kexgsss.c | 333 +++++++++++++++++++++++++++++++++++++++++++++ - monitor.c | 5 +- - regress/kextype.sh | 1 + - regress/rekey.sh | 2 + - ssh-gss.h | 2 + - ssh_config.5 | 4 +- - sshconnect2.c | 2 + - sshd.c | 2 + - sshd_config.5 | 4 +- - 13 files changed, 754 insertions(+), 10 deletions(-) - -diff --git a/gss-genr.c b/gss-genr.c -index c6eff3d7..22040244 100644 ---- a/gss-genr.c -+++ b/gss-genr.c -@@ -198,6 +198,16 @@ ssh_gssapi_id_kex(Gssctxt *ctx, char *name, int kex_type) { - return GSS_C_NO_OID; - name += sizeof(KEX_GSS_GEX_SHA1_ID) - 1; - break; -+ case KEX_GSS_NISTP256_SHA256: -+ if (strlen(name) < sizeof(KEX_GSS_NISTP256_SHA256_ID)) -+ return GSS_C_NO_OID; -+ name += sizeof(KEX_GSS_NISTP256_SHA256_ID) - 1; -+ break; -+ case KEX_GSS_C25519_SHA256: -+ if (strlen(name) < sizeof(KEX_GSS_C25519_SHA256_ID)) -+ return GSS_C_NO_OID; -+ name += sizeof(KEX_GSS_C25519_SHA256_ID) - 1; -+ break; - default: - return GSS_C_NO_OID; - } -diff --git a/kex.c b/kex.c -index e798fecb..bdeeada9 100644 ---- a/kex.c -+++ b/kex.c -@@ -114,6 +114,9 @@ static const struct kexalg kexalgs[] = { - { KEX_GSS_GRP14_SHA1_ID, KEX_GSS_GRP14_SHA1, 0, SSH_DIGEST_SHA1 }, - { KEX_GSS_GRP14_SHA256_ID, KEX_GSS_GRP14_SHA256, 0, SSH_DIGEST_SHA256 }, - { KEX_GSS_GRP16_SHA512_ID, KEX_GSS_GRP16_SHA512, 0, SSH_DIGEST_SHA512 }, -+ { KEX_GSS_NISTP256_SHA256_ID, KEX_GSS_NISTP256_SHA256, -+ NID_X9_62_prime256v1, SSH_DIGEST_SHA256 }, -+ { KEX_GSS_C25519_SHA256_ID, KEX_GSS_C25519_SHA256, 0, SSH_DIGEST_SHA256 }, - #endif - { NULL, -1, -1, -1}, - }; -diff --git a/kex.h b/kex.h -index f27958ae..7def8561 100644 ---- a/kex.h -+++ b/kex.h -@@ -105,6 +105,8 @@ enum kex_exchange { - KEX_GSS_GRP14_SHA256, - KEX_GSS_GRP16_SHA512, - KEX_GSS_GEX_SHA1, -+ KEX_GSS_NISTP256_SHA256, -+ KEX_GSS_C25519_SHA256, - #endif - KEX_MAX - }; -@@ -211,6 +213,8 @@ int kexecdh_server(struct ssh *); - int kexc25519_client(struct ssh *); - int kexc25519_server(struct ssh *); - #ifdef GSSAPI -+int kexecgss_client(struct ssh *); -+int kexecgss_server(struct ssh *); - int kexgss_client(struct ssh *); - int kexgss_server(struct ssh *); - #endif -diff --git a/kexgssc.c b/kexgssc.c -index ed23f06d..bdb3109a 100644 ---- a/kexgssc.c -+++ b/kexgssc.c -@@ -43,6 +43,7 @@ - #include "packet.h" - #include "dh.h" - #include "digest.h" -+#include "ssherr.h" - - #include "ssh-gss.h" - -@@ -52,7 +53,7 @@ kexgss_client(struct ssh *ssh) { - gss_buffer_desc recv_tok, gssbuf, msg_tok, *token_ptr; - Gssctxt *ctxt; - OM_uint32 maj_status, min_status, ret_flags; -- u_int klen, kout, slen = 0, strlen; -+ u_int klen, kout, slen = 0, packet_len; - DH *dh; - BIGNUM *dh_server_pub = NULL; - BIGNUM *shared_secret = NULL; -@@ -201,20 +202,20 @@ kexgss_client(struct ssh *ssh) { - debug("Received GSSAPI_CONTINUE"); - if (maj_status == GSS_S_COMPLETE) - fatal("GSSAPI Continue received from server when complete"); -- recv_tok.value = packet_get_string(&strlen); -- recv_tok.length = strlen; -+ recv_tok.value = packet_get_string(&packet_len); -+ recv_tok.length = packet_len; - break; - case SSH2_MSG_KEXGSS_COMPLETE: - debug("Received GSSAPI_COMPLETE"); - packet_get_bignum2(dh_server_pub); -- msg_tok.value = packet_get_string(&strlen); -- msg_tok.length = strlen; -+ msg_tok.value = packet_get_string(&packet_len); -+ msg_tok.length = packet_len; - - /* Is there a token included? */ - if (packet_get_char()) { - recv_tok.value= -- packet_get_string(&strlen); -- recv_tok.length = strlen; -+ packet_get_string(&packet_len); -+ recv_tok.length = packet_len; - /* If we're already complete - protocol error */ - if (maj_status == GSS_S_COMPLETE) - packet_disconnect("Protocol error: received token when complete"); -@@ -344,4 +345,382 @@ kexgss_client(struct ssh *ssh) { - return kex_send_newkeys(ssh); - } - -+int -+kexecgss_client(struct ssh *ssh) { -+ gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; -+ gss_buffer_desc recv_tok, gssbuf, msg_tok, *token_ptr; -+ Gssctxt *ctxt; -+ OM_uint32 maj_status, min_status, ret_flags; -+ u_int klen = 0, slen = 0, packet_len; -+ u_char *server_pub = NULL; -+ u_int server_pub_len = 0; -+ BIGNUM *shared_secret = NULL; -+ u_char *kbuf = NULL; -+ u_char *serverhostkey = NULL; -+ u_char *empty = ""; -+ char *msg; -+ char *lang; -+ int type = 0; -+ int first = 1; -+ u_char hash[SSH_DIGEST_MAX_LENGTH]; -+ size_t hashlen; -+ const EC_GROUP *group = NULL; -+ const EC_POINT *public_key; -+ struct sshbuf *Q_C = NULL; -+ struct kex *kex = ssh->kex; -+ EC_POINT *server_public = NULL; -+ struct sshbuf *c25519_shared_secret = NULL; -+ int r; -+ -+ /* Initialise our GSSAPI world */ -+ ssh_gssapi_build_ctx(&ctxt); -+ if (ssh_gssapi_id_kex(ctxt, kex->name, kex->kex_type) -+ == GSS_C_NO_OID) -+ fatal("Couldn't identify host exchange"); -+ -+ if (ssh_gssapi_import_name(ctxt, kex->gss_host)) -+ fatal("Couldn't import hostname"); -+ -+ if (kex->gss_client && -+ ssh_gssapi_client_identity(ctxt, kex->gss_client)) -+ fatal("Couldn't acquire client credentials"); -+ -+ if ((Q_C = sshbuf_new()) == NULL) { -+ r = SSH_ERR_ALLOC_FAIL; -+ goto out; -+ } -+ -+ switch (kex->kex_type) { -+ case KEX_GSS_NISTP256_SHA256: -+ if ((kex->ec_client_key = EC_KEY_new_by_curve_name(kex->ec_nid)) == NULL) { -+ r = SSH_ERR_ALLOC_FAIL; -+ goto out; -+ } -+ if (EC_KEY_generate_key(kex->ec_client_key) != 1) { -+ r = SSH_ERR_LIBCRYPTO_ERROR; -+ goto out; -+ } -+ group = EC_KEY_get0_group(kex->ec_client_key); -+ public_key = EC_KEY_get0_public_key(kex->ec_client_key); -+#ifdef DEBUG_KEXECDH -+ fputs("client private key:\n", stderr); -+ sshkey_dump_ec_key(kex->ec_client_key); -+#endif -+ -+ sshbuf_put_ec(Q_C, public_key, group); -+ break; -+ case KEX_GSS_C25519_SHA256: -+ kexc25519_keygen(kex->c25519_client_key, kex->c25519_client_pubkey); -+#ifdef DEBUG_KEXECDH -+ dump_digest("client private key:", kex->c25519_client_key, -+ sizeof(kex->c25519_client_key)); -+#endif -+ -+ if ((r = sshbuf_put_string(Q_C, kex->c25519_client_pubkey, -+ sizeof(kex->c25519_client_pubkey))) != 0) -+ fatal("%s: buffer error: %s", __func__, ssh_err(r)); -+ break; -+ default: -+ fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); -+ } -+ -+ token_ptr = GSS_C_NO_BUFFER; -+ -+ do { -+ /* Step 2 - call GSS_Init_sec_context() */ -+ debug("Calling gss_init_sec_context"); -+ -+ maj_status = ssh_gssapi_init_ctx(ctxt, -+ kex->gss_deleg_creds, token_ptr, &send_tok, -+ &ret_flags); -+ -+ if (GSS_ERROR(maj_status)) { -+ if (send_tok.length != 0) { -+ packet_start(SSH2_MSG_KEXGSS_CONTINUE); -+ packet_put_string(send_tok.value, -+ send_tok.length); -+ } -+ fatal("gss_init_context failed"); -+ } -+ -+ /* If we've got an old receive buffer get rid of it */ -+ if (token_ptr != GSS_C_NO_BUFFER) -+ free(recv_tok.value); -+ -+ if (maj_status == GSS_S_COMPLETE) { -+ /* If mutual state flag is not true, kex fails */ -+ if (!(ret_flags & GSS_C_MUTUAL_FLAG)) -+ fatal("Mutual authentication failed"); -+ -+ /* If integ avail flag is not true kex fails */ -+ if (!(ret_flags & GSS_C_INTEG_FLAG)) -+ fatal("Integrity check failed"); -+ } -+ -+ /* -+ * If we have data to send, then the last message that we -+ * received cannot have been a 'complete'. -+ */ -+ if (send_tok.length != 0) { -+ if (first) { -+ const u_char * ptr; -+ size_t len; -+ -+ packet_start(SSH2_MSG_KEXGSS_INIT); -+ packet_put_string(send_tok.value, -+ send_tok.length); -+ sshbuf_get_string_direct(Q_C, &ptr, &len); -+ packet_put_string(ptr, len); -+ first = 0; -+ } else { -+ packet_start(SSH2_MSG_KEXGSS_CONTINUE); -+ packet_put_string(send_tok.value, -+ send_tok.length); -+ } -+ packet_send(); -+ gss_release_buffer(&min_status, &send_tok); -+ -+ /* If we've sent them data, they should reply */ -+ do { -+ type = packet_read(); -+ if (type == SSH2_MSG_KEXGSS_HOSTKEY) { -+ debug("Received KEXGSS_HOSTKEY"); -+ if (serverhostkey) -+ fatal("Server host key received more than once"); -+ serverhostkey = -+ packet_get_string(&slen); -+ } -+ } while (type == SSH2_MSG_KEXGSS_HOSTKEY); -+ -+ switch (type) { -+ case SSH2_MSG_KEXGSS_CONTINUE: -+ debug("Received GSSAPI_CONTINUE"); -+ if (maj_status == GSS_S_COMPLETE) -+ fatal("GSSAPI Continue received from server when complete"); -+ recv_tok.value = packet_get_string(&packet_len); -+ recv_tok.length = packet_len; -+ break; -+ case SSH2_MSG_KEXGSS_COMPLETE: -+ debug("Received GSSAPI_COMPLETE"); -+ server_pub = packet_get_string(&server_pub_len); -+ msg_tok.value = packet_get_string(&packet_len); -+ msg_tok.length = packet_len; -+ -+ /* Is there a token included? */ -+ if (packet_get_char()) { -+ recv_tok.value= -+ packet_get_string(&packet_len); -+ recv_tok.length = packet_len; -+ /* If we're already complete - protocol error */ -+ if (maj_status == GSS_S_COMPLETE) -+ packet_disconnect("Protocol error: received token when complete"); -+ } else { -+ /* No token included */ -+ if (maj_status != GSS_S_COMPLETE) -+ packet_disconnect("Protocol error: did not receive final token"); -+ } -+ break; -+ case SSH2_MSG_KEXGSS_ERROR: -+ debug("Received Error"); -+ maj_status = packet_get_int(); -+ min_status = packet_get_int(); -+ msg = packet_get_string(NULL); -+ lang = packet_get_string(NULL); -+ fatal("GSSAPI Error: \n%.400s",msg); -+ default: -+ packet_disconnect("Protocol error: didn't expect packet type %d", -+ type); -+ } -+ token_ptr = &recv_tok; -+ } else { -+ /* No data, and not complete */ -+ if (maj_status != GSS_S_COMPLETE) -+ fatal("Not complete, and no token output"); -+ } -+ } while (maj_status & GSS_S_CONTINUE_NEEDED); -+ -+ /* -+ * We _must_ have received a COMPLETE message in reply from the -+ * server, which will have set dh_server_pub and msg_tok -+ */ -+ -+ if (type != SSH2_MSG_KEXGSS_COMPLETE) -+ fatal("Didn't receive a SSH2_MSG_KEXGSS_COMPLETE when I expected it"); -+ -+ /* 7. C verifies that the key Q_S is valid */ -+ /* 8. C computes shared secret */ -+ switch (kex->kex_type) { -+ case KEX_GSS_NISTP256_SHA256: -+ if (server_pub_len != 65) -+ fatal("The received NIST-P256 key did not match" -+ "expected length (expected 65, got %d)", server_pub_len); -+ -+ if (server_pub[0] != POINT_CONVERSION_UNCOMPRESSED) -+ fatal("The received NIST-P256 key does not have first octet 0x04"); -+ -+ if ((server_public = EC_POINT_new(group)) == NULL) { -+ r = SSH_ERR_ALLOC_FAIL; -+ goto out; -+ } -+ -+ if (!EC_POINT_oct2point(group, server_public, server_pub, -+ server_pub_len, NULL)) -+ fatal("Can not decode received NIST-P256 client key"); -+#ifdef DEBUG_KEXECDH -+ fputs("server public key:\n", stderr); -+ sshkey_dump_ec_point(group, server_public); -+#endif -+ -+ if (sshkey_ec_validate_public(group, server_public) != 0) { -+ sshpkt_disconnect(ssh, "invalid client public key"); -+ r = SSH_ERR_MESSAGE_INCOMPLETE; -+ goto out; -+ } -+ -+ if (!EC_POINT_is_on_curve(group, server_public, NULL)) -+ fatal("Received NIST-P256 client key is not on curve"); -+ -+ /* Calculate shared_secret */ -+ klen = (EC_GROUP_get_degree(group) + 7) / 8; -+ if ((kbuf = malloc(klen)) == NULL || -+ (shared_secret = BN_new()) == NULL) { -+ r = SSH_ERR_ALLOC_FAIL; -+ goto out; -+ } -+ if (ECDH_compute_key(kbuf, klen, server_public, -+ kex->ec_client_key, NULL) != (int)klen || -+ BN_bin2bn(kbuf, klen, shared_secret) == NULL) { -+ r = SSH_ERR_LIBCRYPTO_ERROR; -+ goto out; -+ } -+#ifdef DEBUG_KEXECDH -+ dump_digest("shared secret", kbuf, klen); -+#endif -+ break; -+ case KEX_GSS_C25519_SHA256: -+ if (server_pub_len != 32) -+ fatal("The received curve25519 key did not match" -+ "expected length (expected 32, got %d)", server_pub_len); -+ -+ if (server_pub[server_pub_len-1] & 0x80) -+ fatal("The received key has MSB of last octet set!"); -+#ifdef DEBUG_KEXECDH -+ dump_digest("server public key:", server_pub, CURVE25519_SIZE); -+#endif -+ -+ /* generate shared secret */ -+ if ((c25519_shared_secret = sshbuf_new()) == NULL) { -+ r = SSH_ERR_ALLOC_FAIL; -+ goto out; -+ } -+ if ((r = kexc25519_shared_key(kex->c25519_client_key, -+ server_pub, c25519_shared_secret)) < 0) -+ goto out; -+ -+ /* if all octets of the shared secret are zero octets, -+ * is already checked in kexc25519_shared_key() */ -+ break; -+ default: -+ fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); -+ } -+ -+ hashlen = sizeof(hash); -+ switch (kex->kex_type) { -+ case KEX_GSS_NISTP256_SHA256: -+ kex_ecdh_hash( -+ kex->hash_alg, -+ group, -+ kex->client_version_string, -+ kex->server_version_string, -+ sshbuf_ptr(kex->my), sshbuf_len(kex->my), -+ sshbuf_ptr(kex->peer), sshbuf_len(kex->peer), -+ (serverhostkey ? serverhostkey : empty), slen, -+ EC_KEY_get0_public_key(kex->ec_client_key), -+ server_public, -+ shared_secret, -+ hash, &hashlen -+ ); -+ break; -+ case KEX_GSS_C25519_SHA256: -+ kex_c25519_hash( -+ kex->hash_alg, -+ kex->client_version_string, kex->server_version_string, -+ sshbuf_ptr(kex->my), sshbuf_len(kex->my), -+ sshbuf_ptr(kex->peer), sshbuf_len(kex->peer), -+ (serverhostkey ? serverhostkey : empty), slen, -+ kex->c25519_client_pubkey, server_pub, -+ sshbuf_ptr(c25519_shared_secret), sshbuf_len(c25519_shared_secret), -+ hash, &hashlen -+ ); -+ break; -+ default: -+ fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); -+ } -+ -+ gssbuf.value = hash; -+ gssbuf.length = hashlen; -+ -+ /* Verify that the hash matches the MIC we just got. */ -+ if (GSS_ERROR(ssh_gssapi_checkmic(ctxt, &gssbuf, &msg_tok))) -+ packet_disconnect("Hash's MIC didn't verify"); -+ -+ free(msg_tok.value); -+ -+ /* save session id */ -+ if (kex->session_id == NULL) { -+ kex->session_id_len = hashlen; -+ kex->session_id = xmalloc(kex->session_id_len); -+ memcpy(kex->session_id, hash, kex->session_id_len); -+ } -+ -+ if (kex->gss_deleg_creds) -+ ssh_gssapi_credentials_updated(ctxt); -+ -+ if (gss_kex_context == NULL) -+ gss_kex_context = ctxt; -+ else -+ ssh_gssapi_delete_ctx(&ctxt); -+ -+ /* Finally derive the keys and send them */ -+ switch (kex->kex_type) { -+ case KEX_GSS_NISTP256_SHA256: -+ if ((r = kex_derive_keys_bn(ssh, hash, hashlen, shared_secret)) != 0) -+ goto out; -+ break; -+ case KEX_GSS_C25519_SHA256: -+ if ((r = kex_derive_keys(ssh, hash, hashlen, c25519_shared_secret)) != 0) -+ goto out; -+ break; -+ default: -+ fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); -+ } -+ r = kex_send_newkeys(ssh); -+out: -+ free(serverhostkey); -+ explicit_bzero(hash, sizeof(hash)); -+ sshbuf_free(Q_C); -+ if (server_pub) -+ free(server_pub); -+ switch (kex->kex_type) { -+ case KEX_GSS_NISTP256_SHA256: -+ if (kex->ec_client_key) { -+ EC_KEY_free(kex->ec_client_key); -+ kex->ec_client_key = NULL; -+ } -+ if (server_public) -+ EC_POINT_clear_free(server_public); -+ if (kbuf) { -+ explicit_bzero(kbuf, klen); -+ free(kbuf); -+ } -+ if (shared_secret) -+ BN_clear_free(shared_secret); -+ break; -+ case KEX_GSS_C25519_SHA256: -+ explicit_bzero(kex->c25519_client_key, sizeof(kex->c25519_client_key)); -+ sshbuf_free(c25519_shared_secret); -+ break; -+ } -+ return r; -+} - #endif /* GSSAPI */ -diff --git a/kexgsss.c b/kexgsss.c -index b7da8823..a7c42803 100644 ---- a/kexgsss.c -+++ b/kexgsss.c -@@ -46,6 +46,7 @@ - #include "servconf.h" - #include "ssh-gss.h" - #include "digest.h" -+#include "ssherr.h" - - extern ServerOptions options; - -@@ -303,4 +304,338 @@ kexgss_server(struct ssh *ssh) - ssh_gssapi_rekey_creds(); - return 0; - } -+ -+int -+kexecgss_server(struct ssh *ssh) -+{ -+ OM_uint32 maj_status, min_status; -+ -+ /* -+ * Some GSSAPI implementations use the input value of ret_flags (an -+ * output variable) as a means of triggering mechanism specific -+ * features. Initializing it to zero avoids inadvertently -+ * activating this non-standard behaviour. -+ */ -+ -+ OM_uint32 ret_flags = 0; -+ gss_buffer_desc gssbuf, recv_tok, msg_tok; -+ gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; -+ Gssctxt *ctxt = NULL; -+ u_int slen, klen = 0; -+ u_char *kbuf = NULL; -+ BIGNUM *shared_secret = NULL; -+ int type = 0; -+ gss_OID oid; -+ char *mechs; -+ u_char hash[SSH_DIGEST_MAX_LENGTH]; -+ size_t hashlen; -+ u_char *client_pub = NULL; -+ u_int client_pub_len = 0; -+ const EC_GROUP *group = NULL; -+ EC_POINT *client_public = NULL; -+ EC_KEY *server_key = NULL; -+ const EC_POINT *public_key; -+ u_char c25519_server_key[CURVE25519_SIZE]; -+ u_char c25519_server_pubkey[CURVE25519_SIZE]; -+ struct sshbuf *c25519_shared_secret = NULL; -+ struct sshbuf *Q_S; -+ struct kex *kex = ssh->kex; -+ int r; -+ -+ /* Initialise GSSAPI */ -+ -+ /* If we're rekeying, privsep means that some of the private structures -+ * in the GSSAPI code are no longer available. This kludges them back -+ * into life -+ */ -+ if (!ssh_gssapi_oid_table_ok()) -+ if ((mechs = ssh_gssapi_server_mechanisms())) -+ free(mechs); -+ -+ debug2("%s: Identifying %s", __func__, kex->name); -+ oid = ssh_gssapi_id_kex(NULL, kex->name, kex->kex_type); -+ if (oid == GSS_C_NO_OID) -+ fatal("Unknown gssapi mechanism"); -+ -+ debug2("%s: Acquiring credentials", __func__); -+ -+ if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, oid)))) -+ fatal("Unable to acquire credentials for the server"); -+ -+ if ((Q_S = sshbuf_new()) == NULL) { -+ r = SSH_ERR_ALLOC_FAIL; -+ goto out; -+ } -+ -+ /* 5. S generates an ephemeral key pair (do the allocations early) */ -+ switch (kex->kex_type) { -+ case KEX_GSS_NISTP256_SHA256: -+ if ((server_key = EC_KEY_new_by_curve_name(kex->ec_nid)) == NULL) { -+ r = SSH_ERR_ALLOC_FAIL; -+ goto out; -+ } -+ if (EC_KEY_generate_key(server_key) != 1) { -+ r = SSH_ERR_LIBCRYPTO_ERROR; -+ goto out; -+ } -+ group = EC_KEY_get0_group(server_key); -+ public_key = EC_KEY_get0_public_key(server_key); -+ -+ sshbuf_put_ec(Q_S, public_key, group); -+ break; -+ case KEX_GSS_C25519_SHA256: -+ kexc25519_keygen(c25519_server_key, c25519_server_pubkey); -+#ifdef DEBUG_KEXECDH -+ dump_digest("server private key:", c25519_server_key, -+ sizeof(c25519_server_key)); -+#endif -+ if ((r = sshbuf_put_string(Q_S, c25519_server_pubkey, -+ sizeof(c25519_server_pubkey))) != 0) -+ fatal("%s: buffer error: %s", __func__, ssh_err(r)); -+ break; -+ default: -+ fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); -+ } -+ -+ do { -+ debug("Wait SSH2_MSG_GSSAPI_INIT"); -+ type = packet_read(); -+ switch(type) { -+ case SSH2_MSG_KEXGSS_INIT: -+ if (client_pub != NULL) -+ fatal("Received KEXGSS_INIT after initialising"); -+ recv_tok.value = packet_get_string(&slen); -+ recv_tok.length = slen; -+ -+ client_pub = packet_get_string(&client_pub_len); -+ -+ /* Send SSH_MSG_KEXGSS_HOSTKEY here, if we want */ -+ break; -+ case SSH2_MSG_KEXGSS_CONTINUE: -+ recv_tok.value = packet_get_string(&slen); -+ recv_tok.length = slen; -+ break; -+ default: -+ packet_disconnect( -+ "Protocol error: didn't expect packet type %d", -+ type); -+ } -+ -+ maj_status = PRIVSEP(ssh_gssapi_accept_ctx(ctxt, &recv_tok, -+ &send_tok, &ret_flags)); -+ -+ free(recv_tok.value); -+ -+ if (maj_status != GSS_S_COMPLETE && send_tok.length == 0) -+ fatal("Zero length token output when incomplete"); -+ -+ if (client_pub == NULL) -+ fatal("No client public key"); -+ -+ if (maj_status & GSS_S_CONTINUE_NEEDED) { -+ debug("Sending GSSAPI_CONTINUE"); -+ packet_start(SSH2_MSG_KEXGSS_CONTINUE); -+ packet_put_string(send_tok.value, send_tok.length); -+ packet_send(); -+ gss_release_buffer(&min_status, &send_tok); -+ } -+ } while (maj_status & GSS_S_CONTINUE_NEEDED); -+ -+ if (GSS_ERROR(maj_status)) { -+ if (send_tok.length > 0) { -+ packet_start(SSH2_MSG_KEXGSS_CONTINUE); -+ packet_put_string(send_tok.value, send_tok.length); -+ packet_send(); -+ } -+ fatal("accept_ctx died"); -+ } -+ -+ if (!(ret_flags & GSS_C_MUTUAL_FLAG)) -+ fatal("Mutual Authentication flag wasn't set"); -+ -+ if (!(ret_flags & GSS_C_INTEG_FLAG)) -+ fatal("Integrity flag wasn't set"); -+ -+ /* 3. S verifies that the (client) key is valid */ -+ /* calculate shared secret */ -+ switch (kex->kex_type) { -+ case KEX_GSS_NISTP256_SHA256: -+ if (client_pub_len != 65) -+ fatal("The received NIST-P256 key did not match" -+ "expected length (expected 65, got %d)", client_pub_len); -+ -+ if (client_pub[0] != POINT_CONVERSION_UNCOMPRESSED) -+ fatal("The received NIST-P256 key does not have first octet 0x04"); -+ -+ if ((client_public = EC_POINT_new(group)) == NULL) { -+ r = SSH_ERR_ALLOC_FAIL; -+ goto out; -+ } -+ -+ if (!EC_POINT_oct2point(group, client_public, client_pub, -+ client_pub_len, NULL)) -+ fatal("Can not decode received NIST-P256 client key"); -+ -+ if (sshkey_ec_validate_public(group, client_public) != 0) { -+ sshpkt_disconnect(ssh, "invalid client public key"); -+ r = SSH_ERR_MESSAGE_INCOMPLETE; -+ goto out; -+ } -+ -+ if (!EC_POINT_is_on_curve(group, client_public, NULL)) -+ fatal("Received NIST-P256 client key is not on curve"); -+ -+ /* Calculate shared_secret */ -+ klen = (EC_GROUP_get_degree(group) + 7) / 8; -+ if ((kbuf = malloc(klen)) == NULL || -+ (shared_secret = BN_new()) == NULL) { -+ r = SSH_ERR_ALLOC_FAIL; -+ goto out; -+ } -+ if (ECDH_compute_key(kbuf, klen, client_public, -+ server_key, NULL) != (int)klen || -+ BN_bin2bn(kbuf, klen, shared_secret) == NULL) { -+ r = SSH_ERR_LIBCRYPTO_ERROR; -+ goto out; -+ } -+ break; -+ case KEX_GSS_C25519_SHA256: -+ if (client_pub_len != 32) -+ fatal("The received curve25519 key did not match" -+ "expected length (expected 32, got %d)", client_pub_len); -+ -+ if (client_pub[client_pub_len-1] & 0x80) -+ fatal("The received key has MSB of last octet set!"); -+ -+ /* generate shared secret */ -+ if ((c25519_shared_secret = sshbuf_new()) == NULL) { -+ r = SSH_ERR_ALLOC_FAIL; -+ goto out; -+ } -+ if ((r = kexc25519_shared_key(c25519_server_key, -+ client_pub, c25519_shared_secret)) < 0) -+ goto out; -+ -+ /* if all octets of the shared secret are zero octets, -+ * is already checked in kexc25519_shared_key() */ -+ break; -+ default: -+ fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); -+ } -+ -+ hashlen = sizeof(hash); -+ switch (kex->kex_type) { -+ case KEX_GSS_NISTP256_SHA256: -+ kex_ecdh_hash( -+ kex->hash_alg, -+ group, -+ kex->client_version_string, -+ kex->server_version_string, -+ sshbuf_ptr(kex->peer), sshbuf_len(kex->peer), -+ sshbuf_ptr(kex->my), sshbuf_len(kex->my), -+ NULL, 0, -+ client_public, -+ EC_KEY_get0_public_key(server_key), -+ shared_secret, -+ hash, &hashlen -+ ); -+ break; -+ case KEX_GSS_C25519_SHA256: -+ kex_c25519_hash( -+ kex->hash_alg, -+ kex->client_version_string, kex->server_version_string, -+ sshbuf_ptr(kex->peer), sshbuf_len(kex->peer), -+ sshbuf_ptr(kex->my), sshbuf_len(kex->my), -+ NULL, 0, -+ client_pub, c25519_server_pubkey, -+ sshbuf_ptr(c25519_shared_secret), sshbuf_len(c25519_shared_secret), -+ hash, &hashlen -+ ); -+ break; -+ default: -+ fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); -+ } -+ -+ if (kex->session_id == NULL) { -+ kex->session_id_len = hashlen; -+ kex->session_id = xmalloc(kex->session_id_len); -+ memcpy(kex->session_id, hash, kex->session_id_len); -+ } -+ -+ gssbuf.value = hash; -+ gssbuf.length = hashlen; -+ -+ if (GSS_ERROR(PRIVSEP(ssh_gssapi_sign(ctxt,&gssbuf,&msg_tok)))) -+ fatal("Couldn't get MIC"); -+ -+ packet_start(SSH2_MSG_KEXGSS_COMPLETE); -+ { -+ const u_char *ptr; -+ size_t len; -+ if ((r = sshbuf_get_string_direct(Q_S, &ptr, &len)) != 0) -+ fatal("%s: buffer error: %s", __func__, ssh_err(r)); -+ packet_put_string(ptr, len); -+ } -+ packet_put_string(msg_tok.value, msg_tok.length); -+ -+ if (send_tok.length != 0) { -+ packet_put_char(1); /* true */ -+ packet_put_string(send_tok.value, send_tok.length); -+ } else { -+ packet_put_char(0); /* false */ -+ } -+ packet_send(); -+ -+ gss_release_buffer(&min_status, &send_tok); -+ gss_release_buffer(&min_status, &msg_tok); -+ -+ if (gss_kex_context == NULL) -+ gss_kex_context = ctxt; -+ else -+ ssh_gssapi_delete_ctx(&ctxt); -+ -+ /* Finally derive the keys and send them */ -+ switch (kex->kex_type) { -+ case KEX_GSS_NISTP256_SHA256: -+ if ((r = kex_derive_keys_bn(ssh, hash, hashlen, shared_secret)) != 0) -+ goto out; -+ break; -+ case KEX_GSS_C25519_SHA256: -+ if ((r = kex_derive_keys(ssh, hash, hashlen, c25519_shared_secret)) != 0) -+ goto out; -+ break; -+ default: -+ fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); -+ } -+ if ((r = kex_send_newkeys(ssh)) != 0) -+ goto out; -+ -+ /* If this was a rekey, then save out any delegated credentials we -+ * just exchanged. */ -+ if (options.gss_store_rekey) -+ ssh_gssapi_rekey_creds(); -+out: -+ explicit_bzero(hash, sizeof(hash)); -+ if (Q_S) -+ sshbuf_free(Q_S); -+ if (client_pub) -+ free(client_pub); -+ switch (kex->kex_type) { -+ case KEX_GSS_NISTP256_SHA256: -+ if (server_key) -+ EC_KEY_free(server_key); -+ if (kbuf) { -+ explicit_bzero(kbuf, klen); -+ free(kbuf); -+ } -+ if (shared_secret) -+ BN_clear_free(shared_secret); -+ break; -+ case KEX_GSS_C25519_SHA256: -+ explicit_bzero(c25519_server_key, sizeof(c25519_server_key)); -+ sshbuf_free(c25519_shared_secret); -+ break; -+ } -+ return r; -+} - #endif /* GSSAPI */ -diff --git a/monitor.c b/monitor.c -index d6bc7ac7..b11616c8 100644 ---- a/monitor.c -+++ b/monitor.c -@@ -1651,6 +1651,8 @@ monitor_apply_keystate(struct monitor *pmonitor) - kex->kex[KEX_GSS_GRP14_SHA256] = kexgss_server; - kex->kex[KEX_GSS_GRP16_SHA512] = kexgss_server; - kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server; -+ kex->kex[KEX_GSS_NISTP256_SHA256] = kexecgss_server; -+ kex->kex[KEX_GSS_C25519_SHA256] = kexecgss_server; - } - #endif - kex->load_host_public_key=&get_hostkey_public_by_type; -@@ -1867,7 +1869,8 @@ mm_answer_gss_sign(int socket, Buffer *m) - - if ((r = sshbuf_get_string(m, (u_char **)&data.value, &data.length)) != 0) - fatal("%s: buffer error: %s", __func__, ssh_err(r)); -- if (data.length != 20) -+ /* Lengths of SHA-1, SHA-256 and SHA-512 hashes that are used */ -+ if (data.length != 20 && data.length != 32 && data.length != 64) - fatal("%s: data length incorrect: %d", __func__, - (int) data.length); - -diff --git a/regress/kextype.sh b/regress/kextype.sh -index 45f4f16d..d5b4a713 100644 ---- a/regress/kextype.sh -+++ b/regress/kextype.sh -@@ -15,6 +15,7 @@ echo "KexAlgorithms=$KEXOPT" >> $OBJ/sshd_proxy - tries="1 2 3 4" - for k in `${SSH} -Q kex`; do - if [ $k = "gss-gex-sha1-" -o $k = "gss-group1-sha1-" -o \ -+ $k = "gss-nistp256-sha256-" -o $k = "gss-curve25519-sha256-" -o \ - $k = "gss-group14-sha1-" -o $k = "gss-group14-sha256-" -o \ - $k = "gss-group16-sha512-" ]; then - continue -diff --git a/regress/rekey.sh b/regress/rekey.sh -index a2921bef..b118c6c8 100644 ---- a/regress/rekey.sh -+++ b/regress/rekey.sh -@@ -39,6 +39,7 @@ increase_datafile_size 300 - opts="" - for i in `${SSH} -Q kex`; do - if [ $i = "gss-gex-sha1-" -o $i = "gss-group1-sha1-" -o \ -+ $i = "gss-nistp256-sha256-" -o $i = "gss-curve25519-sha256-" -o \ - $i = "gss-group14-sha1-" -o $i = "gss-group14-sha256-" -o \ - $i = "gss-group16-sha512-" ]; then - continue -@@ -62,6 +63,7 @@ if ${SSH} -Q cipher-auth | grep '^.*$' >/dev/null 2>&1 ; then - for c in `${SSH} -Q cipher-auth`; do - for kex in `${SSH} -Q kex`; do - if [ $kex = "gss-gex-sha1-" -o $kex = "gss-group1-sha1-" -o \ -+ $kex = "gss-nistp256-sha256-" -o $kex = "gss-curve25519-sha256-" -o \ - $kex = "gss-group14-sha1-" -o $kex = "gss-group14-sha256-" -o \ - $kex = "gss-group16-sha512-" ]; then - continue -diff --git a/ssh-gss.h b/ssh-gss.h -index 7bf8d75e..1f73721d 100644 ---- a/ssh-gss.h -+++ b/ssh-gss.h -@@ -73,6 +73,8 @@ - #define KEX_GSS_GRP14_SHA256_ID "gss-group14-sha256-" - #define KEX_GSS_GRP16_SHA512_ID "gss-group16-sha512-" - #define KEX_GSS_GEX_SHA1_ID "gss-gex-sha1-" -+#define KEX_GSS_NISTP256_SHA256_ID "gss-nistp256-sha256-" -+#define KEX_GSS_C25519_SHA256_ID "gss-curve25519-sha256-" - - #define GSS_KEX_DEFAULT_KEX \ - KEX_GSS_GEX_SHA1_ID "," \ -diff --git a/ssh_config.5 b/ssh_config.5 -index 3d6da510..1dc29bf1 100644 ---- a/ssh_config.5 -+++ b/ssh_config.5 -@@ -762,7 +762,9 @@ gss-gex-sha1-, - gss-group1-sha1-, - gss-group14-sha1-, - gss-group14-sha256-, --gss-group16-sha512- -+gss-group16-sha512-, -+gss-nistp256-sha256-, -+gss-curve25519-sha256- - .Ed - .Pp - The default is -diff --git a/sshconnect2.c b/sshconnect2.c -index 5d6b8be0..280ae5a6 100644 ---- a/sshconnect2.c -+++ b/sshconnect2.c -@@ -256,6 +256,8 @@ ssh_kex2(char *host, struct sockaddr *hostaddr, u_short port) - kex->kex[KEX_GSS_GRP14_SHA256] = kexgss_client; - kex->kex[KEX_GSS_GRP16_SHA512] = kexgss_client; - kex->kex[KEX_GSS_GEX_SHA1] = kexgss_client; -+ kex->kex[KEX_GSS_NISTP256_SHA256] = kexecgss_client; -+ kex->kex[KEX_GSS_C25519_SHA256] = kexecgss_client; - } - #endif - kex->kex[KEX_C25519_SHA256] = kexc25519_client; -diff --git a/sshd.c b/sshd.c -index e4c879a2..a35735d8 100644 ---- a/sshd.c -+++ b/sshd.c -@@ -2247,6 +2247,8 @@ do_ssh2_kex(void) - kex->kex[KEX_GSS_GRP14_SHA256] = kexgss_server; - kex->kex[KEX_GSS_GRP16_SHA512] = kexgss_server; - kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server; -+ kex->kex[KEX_GSS_NISTP256_SHA256] = kexecgss_server; -+ kex->kex[KEX_GSS_C25519_SHA256] = kexecgss_server; - } - #endif - kex->server = 1; -diff --git a/sshd_config.5 b/sshd_config.5 -index 0793418b..888316bf 100644 ---- a/sshd_config.5 -+++ b/sshd_config.5 -@@ -677,7 +677,9 @@ gss-gex-sha1-, - gss-group1-sha1-, - gss-group14-sha1-, - gss-group14-sha256-, --gss-group16-sha512- -+gss-group16-sha512-, -+gss-nistp256-sha256-, -+gss-curve25519-sha256- - .Ed - .Pp - The default is --- -2.13.5 - - -From 0431695660d5eb1dd1169d42a1624c75a92aa5d2 Mon Sep 17 00:00:00 2001 -From: Jakub Jelen -Date: Wed, 30 Aug 2017 15:30:51 +0200 -Subject: [PATCH 3/3] Simplify rough edges of GSSAPI Kex - ---- - gss-genr.c | 53 +++++++++++++++++------------------------------------ - regress/kextype.sh | 10 ++++------ - regress/rekey.sh | 20 ++++++++------------ - 3 files changed, 29 insertions(+), 54 deletions(-) - -diff --git a/gss-genr.c b/gss-genr.c -index 22040244..c671be31 100644 ---- a/gss-genr.c -+++ b/gss-genr.c -@@ -171,47 +171,28 @@ ssh_gssapi_kex_mechs(gss_OID_set gss_supported, ssh_gssapi_check_fn *check, - gss_OID - ssh_gssapi_id_kex(Gssctxt *ctx, char *name, int kex_type) { - int i = 0; -- -- switch (kex_type) { -- case KEX_GSS_GRP1_SHA1: -- if (strlen(name) < sizeof(KEX_GSS_GRP1_SHA1_ID)) -- return GSS_C_NO_OID; -- name += sizeof(KEX_GSS_GRP1_SHA1_ID) - 1; -- break; -- case KEX_GSS_GRP14_SHA1: -- if (strlen(name) < sizeof(KEX_GSS_GRP14_SHA1_ID)) -- return GSS_C_NO_OID; -- name += sizeof(KEX_GSS_GRP14_SHA1_ID) - 1; -- break; -- case KEX_GSS_GRP14_SHA256: -- if (strlen(name) < sizeof(KEX_GSS_GRP14_SHA256_ID)) -- return GSS_C_NO_OID; -- name += sizeof(KEX_GSS_GRP14_SHA256_ID) - 1; -- break; -- case KEX_GSS_GRP16_SHA512: -- if (strlen(name) < sizeof(KEX_GSS_GRP16_SHA512_ID)) -- return GSS_C_NO_OID; -- name += sizeof(KEX_GSS_GRP16_SHA512_ID) - 1; -- break; -- case KEX_GSS_GEX_SHA1: -- if (strlen(name) < sizeof(KEX_GSS_GEX_SHA1_ID)) -- return GSS_C_NO_OID; -- name += sizeof(KEX_GSS_GEX_SHA1_ID) - 1; -- break; -- case KEX_GSS_NISTP256_SHA256: -- if (strlen(name) < sizeof(KEX_GSS_NISTP256_SHA256_ID)) -- return GSS_C_NO_OID; -- name += sizeof(KEX_GSS_NISTP256_SHA256_ID) - 1; -- break; -- case KEX_GSS_C25519_SHA256: -- if (strlen(name) < sizeof(KEX_GSS_C25519_SHA256_ID)) -- return GSS_C_NO_OID; -- name += sizeof(KEX_GSS_C25519_SHA256_ID) - 1; -+ -+#define SKIP_KEX_NAME(type) \ -+ case type: \ -+ if (strlen(name) < sizeof(type##_ID)) \ -+ return GSS_C_NO_OID; \ -+ name += sizeof(type##_ID) - 1; \ - break; -+ -+ switch (kex_type) { -+ SKIP_KEX_NAME(KEX_GSS_GRP1_SHA1) -+ SKIP_KEX_NAME(KEX_GSS_GRP14_SHA1) -+ SKIP_KEX_NAME(KEX_GSS_GRP14_SHA256) -+ SKIP_KEX_NAME(KEX_GSS_GRP16_SHA512) -+ SKIP_KEX_NAME(KEX_GSS_GEX_SHA1) -+ SKIP_KEX_NAME(KEX_GSS_NISTP256_SHA256) -+ SKIP_KEX_NAME(KEX_GSS_C25519_SHA256) - default: - return GSS_C_NO_OID; - } - -+#undef SKIP_KEX_NAME -+ - while (gss_enc2oid[i].encoded != NULL && - strcmp(name, gss_enc2oid[i].encoded) != 0) - i++; -diff --git a/regress/kextype.sh b/regress/kextype.sh -index d5b4a713..6b4af28a 100644 ---- a/regress/kextype.sh -+++ b/regress/kextype.sh -@@ -14,12 +14,10 @@ echo "KexAlgorithms=$KEXOPT" >> $OBJ/sshd_proxy - - tries="1 2 3 4" - for k in `${SSH} -Q kex`; do -- if [ $k = "gss-gex-sha1-" -o $k = "gss-group1-sha1-" -o \ -- $k = "gss-nistp256-sha256-" -o $k = "gss-curve25519-sha256-" -o \ -- $k = "gss-group14-sha1-" -o $k = "gss-group14-sha256-" -o \ -- $k = "gss-group16-sha512-" ]; then -- continue -- fi -+ # ignore GSSAPI key exchange mechanisms (all of them start with gss-) -+ case $k in -+ gss-* ) continue ;; -+ esac - verbose "kex $k" - for i in $tries; do - ${SSH} -F $OBJ/ssh_proxy -o KexAlgorithms=$k x true -diff --git a/regress/rekey.sh b/regress/rekey.sh -index b118c6c8..d6a8742f 100644 ---- a/regress/rekey.sh -+++ b/regress/rekey.sh -@@ -38,12 +38,10 @@ increase_datafile_size 300 - - opts="" - for i in `${SSH} -Q kex`; do -- if [ $i = "gss-gex-sha1-" -o $i = "gss-group1-sha1-" -o \ -- $i = "gss-nistp256-sha256-" -o $i = "gss-curve25519-sha256-" -o \ -- $i = "gss-group14-sha1-" -o $i = "gss-group14-sha256-" -o \ -- $i = "gss-group16-sha512-" ]; then -- continue -- fi -+ # ignore GSSAPI key exchange mechanisms (all of them start with gss-) -+ case $i in -+ gss-* ) continue ;; -+ esac - opts="$opts KexAlgorithms=$i" - done - for i in `${SSH} -Q cipher`; do -@@ -62,12 +60,10 @@ done - if ${SSH} -Q cipher-auth | grep '^.*$' >/dev/null 2>&1 ; then - for c in `${SSH} -Q cipher-auth`; do - for kex in `${SSH} -Q kex`; do -- if [ $kex = "gss-gex-sha1-" -o $kex = "gss-group1-sha1-" -o \ -- $kex = "gss-nistp256-sha256-" -o $kex = "gss-curve25519-sha256-" -o \ -- $kex = "gss-group14-sha1-" -o $kex = "gss-group14-sha256-" -o \ -- $kex = "gss-group16-sha512-" ]; then -- continue -- fi -+ # ignore GSSAPI key exchange mechanisms (all of them start with gss-) -+ case $kex in -+ gss-* ) continue ;; -+ esac - verbose "client rekey $c $kex" - ssh_data_rekeying "KexAlgorithms=$kex" -oRekeyLimit=256k -oCiphers=$c - done --- -2.13.5 - diff --git a/openssh-7.5p1-sandbox.patch b/openssh-7.5p1-sandbox.patch index b761962..7190813 100644 --- a/openssh-7.5p1-sandbox.patch +++ b/openssh-7.5p1-sandbox.patch @@ -20,8 +20,8 @@ index ca75cc7..6e7de31 100644 +#if defined(__NR_flock) && defined(__s390__) + SC_ALLOW(__NR_flock), +#endif - #ifdef __NR_geteuid - SC_ALLOW(__NR_geteuid), + #ifdef __NR_futex + SC_ALLOW(__NR_futex), #endif @@ -178,6 +181,9 @@ static const struct sock_filter preauth_insns[] = { #ifdef __NR_gettimeofday @@ -106,3 +106,41 @@ diff -up openssh-7.6p1/sandbox-seccomp-filter.c.sandbox openssh-7.6p1/sandbox-se #ifdef __NR_getrandom SC_ALLOW(__NR_getrandom), #endif + + +From ef34ea4521b042dd8a9c4c7455f5d1a8f8ee5bb2 Mon Sep 17 00:00:00 2001 +From: Harald Freudenberger +Date: Fri, 24 May 2019 10:11:15 +0200 +Subject: [PATCH] allow s390 specific ioctl for ecc hardware support + +Adding another s390 specific ioctl to be able to support ECC hardware acceleration +to the sandbox seccomp filter rules. + +Now the ibmca openssl engine provides elliptic curve cryptography support with the +help of libica and CCA crypto cards. This is done via jet another ioctl call to the zcrypt +device driver and so there is a need to enable this on the openssl sandbox. + +Code is s390 specific and has been tested, verified and reviewed. + +Please note that I am also the originator of the previous changes in that area. +I posted these changes to Eduardo and he forwarded the patches to the openssl +community. + +Signed-off-by: Harald Freudenberger +Reviewed-by: Joerg Schmidbauer +--- + sandbox-seccomp-filter.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/sandbox-seccomp-filter.c b/sandbox-seccomp-filter.c +index 5edbc6946..56eb9317f 100644 +--- a/sandbox-seccomp-filter.c ++++ b/sandbox-seccomp-filter.c +@@ -252,6 +252,7 @@ static const struct sock_filter preauth_insns[] = { + SC_ALLOW_ARG(__NR_ioctl, 1, ICARSACRT), + /* Allow ioctls for EP11 crypto card on s390 */ + SC_ALLOW_ARG(__NR_ioctl, 1, ZSENDEP11CPRB), ++ SC_ALLOW_ARG(__NR_ioctl, 1, ZSECSENDCPRB), + #endif + #if defined(__x86_64__) && defined(__ILP32__) && defined(__X32_SYSCALL_BIT) + /* diff --git a/openssh-7.6p1-audit.patch b/openssh-7.6p1-audit.patch index e184894..01e509e 100644 --- a/openssh-7.6p1-audit.patch +++ b/openssh-7.6p1-audit.patch @@ -1,83 +1,80 @@ -diff -up openssh-7.6p1/audit-bsm.c.audit openssh-7.6p1/audit-bsm.c ---- openssh-7.6p1/audit-bsm.c.audit 2017-10-02 21:34:26.000000000 +0200 -+++ openssh-7.6p1/audit-bsm.c 2017-10-04 17:18:32.834505048 +0200 -@@ -373,10 +373,23 @@ audit_connection_from(const char *host, +diff -up openssh/audit-bsm.c.audit openssh/audit-bsm.c +--- openssh/audit-bsm.c.audit 2019-03-27 23:26:14.000000000 +0100 ++++ openssh/audit-bsm.c 2019-04-03 17:02:20.713886041 +0200 +@@ -372,13 +372,26 @@ audit_connection_from(const char *host, #endif } --void +int - audit_run_command(const char *command) - { - /* not implemented */ ++audit_run_command(struct ssh *ssh, const char *command) ++{ ++ /* not implemented */ + return 0; +} + -+void -+audit_end_command(int handle, const char *command) -+{ -+ /* not implemented */ -+} -+ -+void -+audit_count_session_open(void) -+{ -+ /* not necessary */ + void +-audit_run_command(const char *command) ++audit_end_command(struct ssh *ssh, int handle, const char *command) + { + /* not implemented */ } void -@@ -391,6 +404,12 @@ audit_session_close(struct logininfo *li ++audit_count_session_open(void) ++{ ++ /* not necessary */ ++} ++ ++void + audit_session_open(struct logininfo *li) + { + /* not implemented */ +@@ -390,6 +403,12 @@ audit_session_close(struct logininfo *li /* not implemented */ } +int -+audit_keyusage(int host_user, char *fp, int rv) ++audit_keyusage(struct ssh *ssh, int host_user, char *fp, int rv) +{ + /* not implemented */ +} + void - audit_event(ssh_audit_event_t event) + audit_event(struct ssh *ssh, ssh_audit_event_t event) { -@@ -452,4 +471,34 @@ audit_event(ssh_audit_event_t event) +@@ -451,4 +470,28 @@ audit_event(struct ssh *ssh, ssh_audit_e debug("%s: unhandled event %d", __func__, event); } } + +void -+audit_unsupported_body(int what) ++audit_unsupported_body(struct ssh *ssh, int what) +{ + /* not implemented */ +} + +void -+audit_kex_body(int ctos, char *enc, char *mac, char *compress, char *pfs, pid_t pid, uid_t uid) ++audit_kex_body(struct ssh *ssh, int ctos, char *enc, char *mac, char *compress, char *pfs, pid_t pid, uid_t uid) +{ + /* not implemented */ +} + +void -+audit_session_key_free_body(int ctos, pid_t pid, uid_t uid) ++audit_session_key_free_body(struct ssh * ssh, int ctos, pid_t pid, uid_t uid) +{ + /* not implemented */ +} + +void -+audit_destroy_sensitive_data(const char *fp) -+{ -+ /* not implemented */ -+} -+ -+void -+audit_destroy_sensitive_data(const char *fp, pid_t pid, uid_t uid) ++audit_destroy_sensitive_data(struct ssh *ssh, const char *fp, pid_t pid, uid_t uid) +{ + /* not implemented */ +} #endif /* BSM */ -diff -up openssh-7.6p1/audit.c.audit openssh-7.6p1/audit.c ---- openssh-7.6p1/audit.c.audit 2017-10-02 21:34:26.000000000 +0200 -+++ openssh-7.6p1/audit.c 2017-10-04 17:18:32.834505048 +0200 -@@ -34,6 +35,12 @@ +diff -up openssh/audit.c.audit openssh/audit.c +--- openssh/audit.c.audit 2019-03-27 23:26:14.000000000 +0100 ++++ openssh/audit.c 2019-04-03 17:02:20.713886041 +0200 +@@ -34,6 +34,12 @@ #include "log.h" #include "hostfile.h" #include "auth.h" @@ -119,38 +116,38 @@ diff -up openssh-7.6p1/audit.c.audit openssh-7.6p1/audit.c } +void -+audit_key(int host_user, int *rv, const struct sshkey *key) ++audit_key(struct ssh *ssh, int host_user, int *rv, const struct sshkey *key) +{ + char *fp; + + fp = sshkey_fingerprint(key, options.fingerprint_hash, SSH_FP_HEX); -+ if (audit_keyusage(host_user, fp, (*rv == 0)) == 0) ++ if (audit_keyusage(ssh, host_user, fp, (*rv == 0)) == 0) + *rv = -SSH_ERR_INTERNAL_ERROR; + free(fp); +} + +void -+audit_unsupported(int what) ++audit_unsupported(struct ssh *ssh, int what) +{ -+ PRIVSEP(audit_unsupported_body(what)); ++ PRIVSEP(audit_unsupported_body(ssh, what)); +} + +void -+audit_kex(int ctos, char *enc, char *mac, char *comp, char *pfs) ++audit_kex(struct ssh *ssh, int ctos, char *enc, char *mac, char *comp, char *pfs) +{ -+ PRIVSEP(audit_kex_body(ctos, enc, mac, comp, pfs, getpid(), getuid())); ++ PRIVSEP(audit_kex_body(ssh, ctos, enc, mac, comp, pfs, getpid(), getuid())); +} + +void -+audit_session_key_free(int ctos) ++audit_session_key_free(struct ssh *ssh, int ctos) +{ -+ PRIVSEP(audit_session_key_free_body(ctos, getpid(), getuid())); ++ PRIVSEP(audit_session_key_free_body(ssh, ctos, getpid(), getuid())); +} + # ifndef CUSTOM_SSH_AUDIT_EVENTS /* * Null implementations of audit functions. -@@ -138,6 +173,17 @@ audit_event(ssh_audit_event_t event) +@@ -138,6 +171,17 @@ audit_event(struct ssh *ssh, ssh_audit_e } /* @@ -168,7 +165,7 @@ diff -up openssh-7.6p1/audit.c.audit openssh-7.6p1/audit.c * Called when a user session is started. Argument is the tty allocated to * the session, or NULL if no tty was allocated. * -@@ -172,13 +218,82 @@ audit_session_close(struct logininfo *li +@@ -172,13 +216,82 @@ audit_session_close(struct logininfo *li /* * This will be called when a user runs a non-interactive command. Note that * it may be called multiple times for a single connection since SSH2 allows @@ -177,8 +174,9 @@ diff -up openssh-7.6p1/audit.c.audit openssh-7.6p1/audit.c + * audit_end_command. */ -void +-audit_run_command(const char *command) +int - audit_run_command(const char *command) ++audit_run_command(struct ssh *ssh, const char *command) { debug("audit run command euid %d user %s command '%.200s'", geteuid(), audit_username(), command); @@ -192,7 +190,7 @@ diff -up openssh-7.6p1/audit.c.audit openssh-7.6p1/audit.c + * the corresponding audit_run_command. + */ +void -+audit_end_command(int handle, const char *command) ++audit_end_command(struct ssh *ssh, int handle, const char *command) +{ + debug("audit end nopty exec euid %d user %s command '%.200s'", geteuid(), + audit_username(), command); @@ -204,7 +202,7 @@ diff -up openssh-7.6p1/audit.c.audit openssh-7.6p1/audit.c + * Type is the key type, len is the key length(byte) and fp is the fingerprint of the key. + */ +int -+audit_keyusage(int host_user, char *fp, int rv) ++audit_keyusage(struct ssh *ssh, int host_user, char *fp, int rv) +{ + debug("audit %s key usage euid %d user %s fingerprint %s, result %d", + host_user ? "pubkey" : "hostbased", geteuid(), audit_username(), @@ -215,7 +213,7 @@ diff -up openssh-7.6p1/audit.c.audit openssh-7.6p1/audit.c + * This will be called when the protocol negotiation fails. + */ +void -+audit_unsupported_body(int what) ++audit_unsupported_body(struct ssh *ssh, int what) +{ + debug("audit unsupported protocol euid %d type %d", geteuid(), what); +} @@ -224,7 +222,7 @@ diff -up openssh-7.6p1/audit.c.audit openssh-7.6p1/audit.c + * This will be called on succesfull protocol negotiation. + */ +void -+audit_kex_body(int ctos, char *enc, char *mac, char *compress, char *pfs, pid_t pid, ++audit_kex_body(struct ssh *ssh, int ctos, char *enc, char *mac, char *compress, char *pfs, pid_t pid, + uid_t uid) +{ + debug("audit protocol negotiation euid %d direction %d cipher %s mac %s compresion %s pfs %s from pid %ld uid %u", @@ -236,7 +234,7 @@ diff -up openssh-7.6p1/audit.c.audit openssh-7.6p1/audit.c + * This will be called on succesfull session key discard + */ +void -+audit_session_key_free_body(int ctos, pid_t pid, uid_t uid) ++audit_session_key_free_body(struct ssh *, int ctos, pid_t pid, uid_t uid) +{ + debug("audit session key discard euid %u direction %d from pid %ld uid %u", + (unsigned)geteuid(), ctos, (long)pid, (unsigned)uid); @@ -246,25 +244,25 @@ diff -up openssh-7.6p1/audit.c.audit openssh-7.6p1/audit.c + * This will be called on destroy private part of the server key + */ +void -+audit_destroy_sensitive_data(const char *fp, pid_t pid, uid_t uid) ++audit_destroy_sensitive_data(struct ssh *ssh, const char *fp, pid_t pid, uid_t uid) +{ + debug("audit destroy sensitive data euid %d fingerprint %s from pid %ld uid %u", + geteuid(), fp, (long)pid, (unsigned)uid); } # endif /* !defined CUSTOM_SSH_AUDIT_EVENTS */ #endif /* SSH_AUDIT_EVENTS */ -diff -up openssh-7.6p1/audit.h.audit openssh-7.6p1/audit.h ---- openssh-7.6p1/audit.h.audit 2017-10-02 21:34:26.000000000 +0200 -+++ openssh-7.6p1/audit.h 2017-10-04 17:18:32.834505048 +0200 +diff -up openssh/audit.h.audit openssh/audit.h +--- openssh/audit.h.audit 2019-03-27 23:26:14.000000000 +0100 ++++ openssh/audit.h 2019-04-03 17:02:20.713886041 +0200 @@ -26,6 +26,7 @@ # define _SSH_AUDIT_H #include "loginrec.h" +#include "sshkey.h" - enum ssh_audit_event_type { - SSH_LOGIN_EXCEED_MAXTRIES, -@@ -43,13 +44,32 @@ enum ssh_audit_event_type { + struct ssh; + +@@ -45,13 +46,32 @@ enum ssh_audit_event_type { SSH_CONNECTION_ABANDON, /* closed without completing auth */ SSH_AUDIT_UNKNOWN }; @@ -279,28 +277,28 @@ diff -up openssh-7.6p1/audit.h.audit openssh-7.6p1/audit.h +int listening_for_clients(void); + void audit_connection_from(const char *, int); - void audit_event(ssh_audit_event_t); + void audit_event(struct ssh *, ssh_audit_event_t); +void audit_count_session_open(void); void audit_session_open(struct logininfo *); void audit_session_close(struct logininfo *); -void audit_run_command(const char *); -+int audit_run_command(const char *); -+void audit_end_command(int, const char *); ++int audit_run_command(struct ssh *, const char *); ++void audit_end_command(struct ssh *, int, const char *); ssh_audit_event_t audit_classify_auth(const char *); -+int audit_keyusage(int, char *, int); -+void audit_key(int, int *, const struct sshkey *); -+void audit_unsupported(int); -+void audit_kex(int, char *, char *, char *, char *); -+void audit_unsupported_body(int); -+void audit_kex_body(int, char *, char *, char *, char *, pid_t, uid_t); -+void audit_session_key_free(int ctos); -+void audit_session_key_free_body(int ctos, pid_t, uid_t); -+void audit_destroy_sensitive_data(const char *, pid_t, uid_t); ++int audit_keyusage(struct ssh *, int, char *, int); ++void audit_key(struct ssh *, int, int *, const struct sshkey *); ++void audit_unsupported(struct ssh *, int); ++void audit_kex(struct ssh *, int, char *, char *, char *, char *); ++void audit_unsupported_body(struct ssh *, int); ++void audit_kex_body(struct ssh *, int, char *, char *, char *, char *, pid_t, uid_t); ++void audit_session_key_free(struct ssh *, int ctos); ++void audit_session_key_free_body(struct ssh *, int ctos, pid_t, uid_t); ++void audit_destroy_sensitive_data(struct ssh *, const char *, pid_t, uid_t); #endif /* _SSH_AUDIT_H */ -diff -up openssh-7.6p1/audit-linux.c.audit openssh-7.6p1/audit-linux.c ---- openssh-7.6p1/audit-linux.c.audit 2017-10-02 21:34:26.000000000 +0200 -+++ openssh-7.6p1/audit-linux.c 2017-10-04 17:18:32.835505053 +0200 +diff -up openssh/audit-linux.c.audit openssh/audit-linux.c +--- openssh/audit-linux.c.audit 2019-03-27 23:26:14.000000000 +0100 ++++ openssh/audit-linux.c 2019-04-03 17:02:20.713886041 +0200 @@ -33,27 +33,40 @@ #include "log.h" @@ -414,7 +412,7 @@ diff -up openssh-7.6p1/audit-linux.c.audit openssh-7.6p1/audit-linux.c +} + +int -+audit_keyusage(int host_user, char *fp, int rv) ++audit_keyusage(struct ssh *ssh, int host_user, char *fp, int rv) +{ + char buf[AUDIT_LOG_SIZE]; + int audit_fd, rc, saved_errno; @@ -429,12 +427,12 @@ diff -up openssh-7.6p1/audit-linux.c.audit openssh-7.6p1/audit-linux.c + } + snprintf(buf, sizeof(buf), "%s_auth grantors=auth-key", host_user ? "pubkey" : "hostbased"); + rc = audit_log_acct_message(audit_fd, AUDIT_USER_AUTH, NULL, -+ buf, audit_username(), -1, NULL, ssh_remote_ipaddr(active_state), NULL, rv); ++ buf, audit_username(), -1, NULL, ssh_remote_ipaddr(ssh), NULL, rv); + if ((rc < 0) && ((rc != -1) || (getuid() == 0))) + goto out; + snprintf(buf, sizeof(buf), "op=negotiate kind=auth-key fp=%s", fp); + rc = audit_log_user_message(audit_fd, AUDIT_CRYPTO_KEY_USER, buf, NULL, -+ ssh_remote_ipaddr(active_state), NULL, rv); ++ ssh_remote_ipaddr(ssh), NULL, rv); +out: + saved_errno = errno; + audit_close(audit_fd); @@ -448,34 +446,34 @@ diff -up openssh-7.6p1/audit-linux.c.audit openssh-7.6p1/audit-linux.c /* Below is the sshd audit API code */ void -@@ -76,24 +177,55 @@ audit_connection_from(const char *host, +@@ -76,49 +176,210 @@ audit_connection_from(const char *host, /* not implemented */ } --void +int - audit_run_command(const char *command) - { -- /* not implemented */ ++audit_run_command(struct ssh *ssh, const char *command) ++{ + if (!user_login_count++) + linux_audit_user_logxxx(the_authctxt->pw->pw_uid, NULL, -+ ssh_remote_ipaddr(active_state), ++ ssh_remote_ipaddr(ssh), + "ssh", 1, AUDIT_USER_LOGIN); + linux_audit_user_logxxx(the_authctxt->pw->pw_uid, NULL, -+ ssh_remote_ipaddr(active_state), ++ ssh_remote_ipaddr(ssh), + "ssh", 1, AUDIT_USER_START); + return 0; +} + -+void -+audit_end_command(int handle, const char *command) -+{ + void +-audit_run_command(const char *command) ++audit_end_command(struct ssh *ssh, int handle, const char *command) + { +- /* not implemented */ + linux_audit_user_logxxx(the_authctxt->pw->pw_uid, NULL, -+ ssh_remote_ipaddr(active_state), ++ ssh_remote_ipaddr(ssh), + "ssh", 1, AUDIT_USER_END); + if (user_login_count && !--user_login_count) + linux_audit_user_logxxx(the_authctxt->pw->pw_uid, NULL, -+ ssh_remote_ipaddr(active_state), ++ ssh_remote_ipaddr(ssh), + "ssh", 1, AUDIT_USER_LOGOUT); +} + @@ -510,9 +508,8 @@ diff -up openssh-7.6p1/audit-linux.c.audit openssh-7.6p1/audit-linux.c } void -@@ -102,25 +231,155 @@ audit_event(ssh_audit_event_t event) - struct ssh *ssh = active_state; /* XXX */ - + audit_event(struct ssh *ssh, ssh_audit_event_t event) + { switch(event) { - case SSH_AUTH_SUCCESS: - case SSH_CONNECTION_CLOSE: @@ -563,7 +560,7 @@ diff -up openssh-7.6p1/audit-linux.c.audit openssh-7.6p1/audit-linux.c } + +void -+audit_unsupported_body(int what) ++audit_unsupported_body(struct ssh *ssh, int what) +{ +#ifdef AUDIT_CRYPTO_SESSION + char buf[AUDIT_LOG_SIZE]; @@ -572,15 +569,15 @@ diff -up openssh-7.6p1/audit-linux.c.audit openssh-7.6p1/audit-linux.c + int audit_fd; + + snprintf(buf, sizeof(buf), "op=unsupported-%s direction=? cipher=? ksize=? rport=%d laddr=%s lport=%d ", -+ name[what], ssh_remote_port(active_state), (s = get_local_ipaddr(packet_get_connection_in())), -+ ssh_local_port(active_state)); ++ name[what], ssh_remote_port(ssh), (s = get_local_ipaddr(ssh_packet_get_connection_in(ssh))), ++ ssh_local_port(ssh)); + free(s); + audit_fd = audit_open(); + if (audit_fd < 0) + /* no problem, the next instruction will be fatal() */ + return; + audit_log_user_message(audit_fd, AUDIT_CRYPTO_SESSION, -+ buf, NULL, ssh_remote_ipaddr(active_state), NULL, 0); ++ buf, NULL, ssh_remote_ipaddr(ssh), NULL, 0); + audit_close(audit_fd); +#endif +} @@ -588,7 +585,7 @@ diff -up openssh-7.6p1/audit-linux.c.audit openssh-7.6p1/audit-linux.c +const static char *direction[] = { "from-server", "from-client", "both" }; + +void -+audit_kex_body(int ctos, char *enc, char *mac, char *compress, ++audit_kex_body(struct ssh *ssh, int ctos, char *enc, char *mac, char *compress, + char *pfs, pid_t pid, uid_t uid) +{ +#ifdef AUDIT_CRYPTO_SESSION @@ -600,7 +597,7 @@ diff -up openssh-7.6p1/audit-linux.c.audit openssh-7.6p1/audit-linux.c + snprintf(buf, sizeof(buf), "op=start direction=%s cipher=%s ksize=%d mac=%s pfs=%s spid=%jd suid=%jd rport=%d laddr=%s lport=%d ", + direction[ctos], enc, cipher ? 8 * cipher->key_len : 0, mac, pfs, + (intmax_t)pid, (intmax_t)uid, -+ ssh_remote_port(active_state), (s = get_local_ipaddr(packet_get_connection_in())), ssh_local_port(active_state)); ++ ssh_remote_port(ssh), (s = get_local_ipaddr(ssh_packet_get_connection_in(ssh))), ssh_local_port(ssh)); + free(s); + audit_fd = audit_open(); + if (audit_fd < 0) { @@ -611,7 +608,7 @@ diff -up openssh-7.6p1/audit-linux.c.audit openssh-7.6p1/audit-linux.c + fatal("cannot open audit"); /* Must prevent login */ + } + audit_ok = audit_log_user_message(audit_fd, AUDIT_CRYPTO_SESSION, -+ buf, NULL, ssh_remote_ipaddr(active_state), NULL, 1); ++ buf, NULL, ssh_remote_ipaddr(ssh), NULL, 1); + audit_close(audit_fd); + /* do not abort if the error is EPERM and sshd is run as non root user */ + if ((audit_ok < 0) && ((audit_ok != -1) || (getuid() == 0))) @@ -620,7 +617,7 @@ diff -up openssh-7.6p1/audit-linux.c.audit openssh-7.6p1/audit-linux.c +} + +void -+audit_session_key_free_body(int ctos, pid_t pid, uid_t uid) ++audit_session_key_free_body(struct ssh *ssh, int ctos, pid_t pid, uid_t uid) +{ + char buf[AUDIT_LOG_SIZE]; + int audit_fd, audit_ok; @@ -628,9 +625,9 @@ diff -up openssh-7.6p1/audit-linux.c.audit openssh-7.6p1/audit-linux.c + + snprintf(buf, sizeof(buf), "op=destroy kind=session fp=? direction=%s spid=%jd suid=%jd rport=%d laddr=%s lport=%d ", + direction[ctos], (intmax_t)pid, (intmax_t)uid, -+ ssh_remote_port(active_state), -+ (s = get_local_ipaddr(packet_get_connection_in())), -+ ssh_local_port(active_state)); ++ ssh_remote_port(ssh), ++ (s = get_local_ipaddr(ssh_packet_get_connection_in(ssh))), ++ ssh_local_port(ssh)); + free(s); + audit_fd = audit_open(); + if (audit_fd < 0) { @@ -640,7 +637,7 @@ diff -up openssh-7.6p1/audit-linux.c.audit openssh-7.6p1/audit-linux.c + return; + } + audit_ok = audit_log_user_message(audit_fd, AUDIT_CRYPTO_KEY_USER, -+ buf, NULL, ssh_remote_ipaddr(active_state), NULL, 1); ++ buf, NULL, ssh_remote_ipaddr(ssh), NULL, 1); + audit_close(audit_fd); + /* do not abort if the error is EPERM and sshd is run as non root user */ + if ((audit_ok < 0) && ((audit_ok != -1) || (getuid() == 0))) @@ -648,7 +645,7 @@ diff -up openssh-7.6p1/audit-linux.c.audit openssh-7.6p1/audit-linux.c +} + +void -+audit_destroy_sensitive_data(const char *fp, pid_t pid, uid_t uid) ++audit_destroy_sensitive_data(struct ssh *ssh, const char *fp, pid_t pid, uid_t uid) +{ + char buf[AUDIT_LOG_SIZE]; + int audit_fd, audit_ok; @@ -664,7 +661,7 @@ diff -up openssh-7.6p1/audit-linux.c.audit openssh-7.6p1/audit-linux.c + } + audit_ok = audit_log_user_message(audit_fd, AUDIT_CRYPTO_KEY_USER, + buf, NULL, -+ listening_for_clients() ? NULL : ssh_remote_ipaddr(active_state), ++ listening_for_clients() ? NULL : ssh_remote_ipaddr(ssh), + NULL, 1); + audit_close(audit_fd); + /* do not abort if the error is EPERM and sshd is run as non root user */ @@ -672,10 +669,10 @@ diff -up openssh-7.6p1/audit-linux.c.audit openssh-7.6p1/audit-linux.c + error("cannot write into audit"); +} #endif /* USE_LINUX_AUDIT */ -diff -up openssh-7.6p1/auditstub.c.audit openssh-7.6p1/auditstub.c ---- openssh-7.6p1/auditstub.c.audit 2017-10-04 17:18:32.835505053 +0200 -+++ openssh-7.6p1/auditstub.c 2017-10-04 17:18:32.835505053 +0200 -@@ -0,0 +1,50 @@ +diff -up openssh/auditstub.c.audit openssh/auditstub.c +--- openssh/auditstub.c.audit 2019-04-03 17:02:20.714886050 +0200 ++++ openssh/auditstub.c 2019-04-03 17:02:20.714886050 +0200 +@@ -0,0 +1,52 @@ +/* $Id: auditstub.c,v 1.1 jfch Exp $ */ + +/* @@ -707,95 +704,97 @@ diff -up openssh-7.6p1/auditstub.c.audit openssh-7.6p1/auditstub.c + +#include + ++struct ssh; ++ +void -+audit_unsupported(int n) ++audit_unsupported(struct ssh *ssh, int n) +{ +} + +void -+audit_kex(int ctos, char *enc, char *mac, char *comp, char *pfs) ++audit_kex(struct ssh *ssh, int ctos, char *enc, char *mac, char *comp, char *pfs) +{ +} + +void -+audit_session_key_free(int ctos) ++audit_session_key_free(struct ssh *ssh, int ctos) +{ +} + +void -+audit_session_key_free_body(int ctos, pid_t pid, uid_t uid) ++audit_session_key_free_body(struct ssh *ssh, int ctos, pid_t pid, uid_t uid) +{ +} -diff -up openssh-7.6p1/auth2.c.audit openssh-7.6p1/auth2.c ---- openssh-7.6p1/auth2.c.audit 2017-10-04 17:18:32.746504598 +0200 -+++ openssh-7.6p1/auth2.c 2017-10-04 17:18:32.835505053 +0200 -@@ -255,9 +255,6 @@ input_userauth_request(int type, u_int32 +diff -up openssh/auth2.c.audit openssh/auth2.c +--- openssh/auth2.c.audit 2019-04-03 17:02:20.651885453 +0200 ++++ openssh/auth2.c 2019-04-03 17:02:20.714886050 +0200 +@@ -303,9 +303,6 @@ input_userauth_request(int type, u_int32 } else { /* Invalid user, fake password information */ authctxt->pw = fakepw(); -#ifdef SSH_AUDIT_EVENTS -- PRIVSEP(audit_event(SSH_INVALID_USER)); +- PRIVSEP(audit_event(ssh, SSH_INVALID_USER)); -#endif } #ifdef USE_PAM if (options.use_pam) -diff -up openssh-7.6p1/auth2-hostbased.c.audit openssh-7.6p1/auth2-hostbased.c ---- openssh-7.6p1/auth2-hostbased.c.audit 2017-10-04 17:18:32.683504276 +0200 -+++ openssh-7.6p1/auth2-hostbased.c 2017-10-04 17:18:32.835505053 +0200 -@@ -152,7 +152,7 @@ userauth_hostbased(struct ssh *ssh) - /* test for allowed key and correct signature */ +diff -up openssh/auth2-hostbased.c.audit openssh/auth2-hostbased.c +--- openssh/auth2-hostbased.c.audit 2019-04-03 17:02:20.612885083 +0200 ++++ openssh/auth2-hostbased.c 2019-04-03 17:02:20.714886050 +0200 +@@ -158,7 +158,7 @@ userauth_hostbased(struct ssh *ssh) authenticated = 0; - if (PRIVSEP(hostbased_key_allowed(authctxt->pw, cuser, chost, key)) && + if (PRIVSEP(hostbased_key_allowed(ssh, authctxt->pw, cuser, + chost, key)) && - PRIVSEP(sshkey_verify(key, sig, slen, -+ PRIVSEP(hostbased_key_verify(key, sig, slen, ++ PRIVSEP(hostbased_key_verify(ssh, key, sig, slen, sshbuf_ptr(b), sshbuf_len(b), pkalg, ssh->compat)) == 0) authenticated = 1; -@@ -169,6 +169,19 @@ done: +@@ -175,6 +175,19 @@ done: return authenticated; } +int -+hostbased_key_verify(const struct sshkey *key, const u_char *sig, size_t slen, -+ const u_char *data, size_t datalen, const char *pkalg, u_int compat) ++hostbased_key_verify(struct ssh *ssh, const struct sshkey *key, const u_char *sig, ++ size_t slen, const u_char *data, size_t datalen, const char *pkalg, u_int compat) +{ + int rv; + + rv = sshkey_verify(key, sig, slen, data, datalen, pkalg, compat); +#ifdef SSH_AUDIT_EVENTS -+ audit_key(0, &rv, key); ++ audit_key(ssh, 0, &rv, key); +#endif + return rv; +} + /* return 1 if given hostkey is allowed */ int - hostbased_key_allowed(struct passwd *pw, const char *cuser, char *chost, -diff -up openssh-7.6p1/auth2-pubkey.c.audit openssh-7.6p1/auth2-pubkey.c ---- openssh-7.6p1/auth2-pubkey.c.audit 2017-10-04 17:18:32.828505018 +0200 -+++ openssh-7.6p1/auth2-pubkey.c 2017-10-04 17:18:32.835505053 +0200 -@@ -206,7 +206,7 @@ userauth_pubkey(struct ssh *ssh) + hostbased_key_allowed(struct ssh *ssh, struct passwd *pw, +diff -up openssh/auth2-pubkey.c.audit openssh/auth2-pubkey.c +--- openssh/auth2-pubkey.c.audit 2019-04-03 17:02:20.691885832 +0200 ++++ openssh/auth2-pubkey.c 2019-04-03 17:02:20.714886050 +0200 +@@ -219,7 +219,7 @@ userauth_pubkey(struct ssh *ssh) /* test for correct signature */ authenticated = 0; if (PRIVSEP(user_key_allowed(ssh, pw, key, 1, &authopts)) && - PRIVSEP(sshkey_verify(key, sig, slen, -+ PRIVSEP(user_key_verify(key, sig, slen, ++ PRIVSEP(user_key_verify(ssh, key, sig, slen, sshbuf_ptr(b), sshbuf_len(b), (ssh->compat & SSH_BUG_SIGTYPE) == 0 ? pkalg : NULL, ssh->compat)) == 0) { -@@ -250,6 +250,19 @@ done: +@@ -278,6 +278,19 @@ done: return authenticated; } +int -+user_key_verify(const struct sshkey *key, const u_char *sig, size_t slen, -+ const u_char *data, size_t datalen, const char *pkalg, u_int compat) ++user_key_verify(struct ssh *ssh, const struct sshkey *key, const u_char *sig, ++ size_t slen, const u_char *data, size_t datalen, const char *pkalg, u_int compat) +{ + int rv; + + rv = sshkey_verify(key, sig, slen, data, datalen, pkalg, compat); +#ifdef SSH_AUDIT_EVENTS -+ audit_key(1, &rv, key); ++ audit_key(ssh, 1, &rv, key); +#endif + return rv; +} @@ -803,52 +802,52 @@ diff -up openssh-7.6p1/auth2-pubkey.c.audit openssh-7.6p1/auth2-pubkey.c static int match_principals_option(const char *principal_list, struct sshkey_cert *cert) { -diff -up openssh-7.6p1/auth.c.audit openssh-7.6p1/auth.c ---- openssh-7.6p1/auth.c.audit 2017-10-04 17:18:32.746504598 +0200 -+++ openssh-7.6p1/auth.c 2017-10-04 17:18:32.835505053 +0200 -@@ -360,7 +360,7 @@ auth_log(Authctxt *authctxt, int authent +diff -up openssh/auth.c.audit openssh/auth.c +--- openssh/auth.c.audit 2019-04-03 17:02:20.691885832 +0200 ++++ openssh/auth.c 2019-04-03 17:02:20.714886050 +0200 +@@ -366,7 +366,7 @@ auth_log(struct ssh *ssh, int authentica # endif #endif #ifdef SSH_AUDIT_EVENTS - if (authenticated == 0 && !authctxt->postponed) + if (authenticated == 0 && !authctxt->postponed && !partial) - audit_event(audit_classify_auth(method)); + audit_event(ssh, audit_classify_auth(method)); #endif } -@@ -599,9 +599,6 @@ getpwnamallow(const char *user) - record_failed_login(user, +@@ -592,9 +592,6 @@ getpwnamallow(struct ssh *ssh, const cha + record_failed_login(ssh, user, auth_get_canonical_hostname(ssh, options.use_dns), "ssh"); #endif -#ifdef SSH_AUDIT_EVENTS -- audit_event(SSH_INVALID_USER); +- audit_event(ssh, SSH_INVALID_USER); -#endif /* SSH_AUDIT_EVENTS */ return (NULL); } - if (!allowed_user(pw)) -diff -up openssh-7.6p1/auth.h.audit openssh-7.6p1/auth.h ---- openssh-7.6p1/auth.h.audit 2017-10-04 17:18:32.768504711 +0200 -+++ openssh-7.6p1/auth.h 2017-10-04 17:18:32.836505059 +0200 -@@ -198,6 +198,8 @@ struct passwd * getpwnamallow(const char + if (!allowed_user(ssh, pw)) +diff -up openssh/auth.h.audit openssh/auth.h +--- openssh/auth.h.audit 2019-04-03 17:02:20.692885842 +0200 ++++ openssh/auth.h 2019-04-03 17:02:20.714886050 +0200 +@@ -195,6 +195,8 @@ struct passwd * getpwnamallow(struct ssh char *expand_authorized_keys(const char *, struct passwd *pw); char *authorized_principals_file(struct passwd *); -+int user_key_verify(const struct sshkey *, const u_char *, size_t, ++int user_key_verify(struct ssh *, const struct sshkey *, const u_char *, size_t, + const u_char *, size_t, const char *, u_int); FILE *auth_openkeyfile(const char *, struct passwd *, int); FILE *auth_openprincipals(const char *, struct passwd *, int); -@@ -217,6 +218,8 @@ struct sshkey *get_hostkey_private_by_ty +@@ -214,6 +216,8 @@ struct sshkey *get_hostkey_private_by_ty int get_hostkey_index(struct sshkey *, int, struct ssh *); - int sshd_hostkey_sign(struct sshkey *, struct sshkey *, u_char **, - size_t *, const u_char *, size_t, const char *, u_int); -+int hostbased_key_verify(const struct sshkey *, const u_char *, size_t, + int sshd_hostkey_sign(struct ssh *, struct sshkey *, struct sshkey *, + u_char **, size_t *, const u_char *, size_t, const char *); ++int hostbased_key_verify(struct ssh *, const struct sshkey *, const u_char *, size_t, + const u_char *, size_t, const char *, u_int); /* Key / cert options linkage to auth layer */ const struct sshauthopt *auth_options(struct ssh *); -diff -up openssh-7.6p1/cipher.c.audit openssh-7.6p1/cipher.c ---- openssh-7.6p1/cipher.c.audit 2017-10-02 21:34:26.000000000 +0200 -+++ openssh-7.6p1/cipher.c 2017-10-04 17:18:32.836505059 +0200 +diff -up openssh/cipher.c.audit openssh/cipher.c +--- openssh/cipher.c.audit 2019-03-27 23:26:14.000000000 +0100 ++++ openssh/cipher.c 2019-04-03 17:02:20.714886050 +0200 @@ -61,25 +61,6 @@ struct sshcipher_ctx { const struct sshcipher *cipher; }; @@ -875,7 +874,7 @@ diff -up openssh-7.6p1/cipher.c.audit openssh-7.6p1/cipher.c static const struct sshcipher ciphers[] = { #ifdef WITH_OPENSSL #ifndef OPENSSL_NO_DES -@@ -409,7 +409,7 @@ cipher_get_length(struct sshcipher_ctx * +@@ -410,7 +391,7 @@ cipher_get_length(struct sshcipher_ctx * void cipher_free(struct sshcipher_ctx *cc) { @@ -884,9 +883,9 @@ diff -up openssh-7.6p1/cipher.c.audit openssh-7.6p1/cipher.c return; if ((cc->cipher->flags & CFLAG_CHACHAPOLY) != 0) explicit_bzero(&cc->cp_ctx, sizeof(cc->cp_ctx)); -diff -up openssh-7.6p1/cipher.h.audit openssh-7.6p1/cipher.h ---- openssh-7.6p1/cipher.h.audit 2017-10-02 21:34:26.000000000 +0200 -+++ openssh-7.6p1/cipher.h 2017-10-04 17:18:32.836505059 +0200 +diff -up openssh/cipher.h.audit openssh/cipher.h +--- openssh/cipher.h.audit 2019-03-27 23:26:14.000000000 +0100 ++++ openssh/cipher.h 2019-04-03 17:02:20.714886050 +0200 @@ -45,7 +45,25 @@ #define CIPHER_ENCRYPT 1 #define CIPHER_DECRYPT 0 @@ -914,10 +913,10 @@ diff -up openssh-7.6p1/cipher.h.audit openssh-7.6p1/cipher.h struct sshcipher_ctx; const struct sshcipher *cipher_by_name(const char *); -diff -up openssh-7.6p1/kex.c.audit openssh-7.6p1/kex.c ---- openssh-7.6p1/kex.c.audit 2017-10-04 17:18:32.822504987 +0200 -+++ openssh-7.6p1/kex.c 2017-10-04 17:18:32.836505059 +0200 -@@ -54,6 +54,7 @@ +diff -up openssh/kex.c.audit openssh/kex.c +--- openssh/kex.c.audit 2019-04-03 17:02:20.652885462 +0200 ++++ openssh/kex.c 2019-04-03 17:02:20.715886060 +0200 +@@ -60,6 +60,7 @@ #include "ssherr.h" #include "sshbuf.h" #include "digest.h" @@ -925,64 +924,91 @@ diff -up openssh-7.6p1/kex.c.audit openssh-7.6p1/kex.c #ifdef GSSAPI #include "ssh-gss.h" -@@ -692,8 +693,12 @@ choose_enc(struct sshenc *enc, char *cli +@@ -758,12 +759,16 @@ kex_start_rekex(struct ssh *ssh) + } + + static int +-choose_enc(struct sshenc *enc, char *client, char *server) ++choose_enc(struct ssh *ssh, struct sshenc *enc, char *client, char *server) { char *name = match_list(client, server, NULL); - if (name == NULL) + if (name == NULL) { +#ifdef SSH_AUDIT_EVENTS -+ audit_unsupported(SSH_AUDIT_UNSUPPORTED_CIPHER); ++ audit_unsupported(ssh, SSH_AUDIT_UNSUPPORTED_CIPHER); +#endif return SSH_ERR_NO_CIPHER_ALG_MATCH; + } if ((enc->cipher = cipher_by_name(name)) == NULL) { free(name); return SSH_ERR_INTERNAL_ERROR; -@@ -713,8 +718,12 @@ choose_mac(struct ssh *ssh, struct sshma +@@ -783,8 +788,12 @@ choose_mac(struct ssh *ssh, struct sshma { char *name = match_list(client, server, NULL); - if (name == NULL) + if (name == NULL) { +#ifdef SSH_AUDIT_EVENTS -+ audit_unsupported(SSH_AUDIT_UNSUPPORTED_MAC); ++ audit_unsupported(ssh, SSH_AUDIT_UNSUPPORTED_MAC); +#endif return SSH_ERR_NO_MAC_ALG_MATCH; + } if (mac_setup(mac, name) < 0) { free(name); return SSH_ERR_INTERNAL_ERROR; -@@ -733,8 +742,12 @@ choose_comp(struct sshcomp *comp, char * +@@ -796,12 +805,16 @@ choose_mac(struct ssh *ssh, struct sshma + } + + static int +-choose_comp(struct sshcomp *comp, char *client, char *server) ++choose_comp(struct ssh *ssh, struct sshcomp *comp, char *client, char *server) { char *name = match_list(client, server, NULL); - if (name == NULL) + if (name == NULL) { +#ifdef SSH_AUDIT_EVENTS -+ audit_unsupported(SSH_AUDIT_UNSUPPORTED_COMPRESSION); ++ audit_unsupported(ssh, SSH_AUDIT_UNSUPPORTED_COMPRESSION); +#endif return SSH_ERR_NO_COMPRESS_ALG_MATCH; + } if (strcmp(name, "zlib@openssh.com") == 0) { comp->type = COMP_DELAYED; } else if (strcmp(name, "zlib") == 0) { -@@ -904,6 +917,10 @@ kex_choose_conf(struct ssh *ssh) +@@ -933,7 +946,7 @@ kex_choose_conf(struct ssh *ssh) + 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], ++ if ((r = choose_enc(ssh, &newkeys->enc, cprop[nenc], + sprop[nenc])) != 0) { + kex->failed_choice = peer[nenc]; + peer[nenc] = NULL; +@@ -948,7 +961,7 @@ kex_choose_conf(struct ssh *ssh) + peer[nmac] = NULL; + goto out; + } +- if ((r = choose_comp(&newkeys->comp, cprop[ncomp], ++ if ((r = choose_comp(ssh, &newkeys->comp, cprop[ncomp], + sprop[ncomp])) != 0) { + kex->failed_choice = peer[ncomp]; + peer[ncomp] = NULL; +@@ -971,6 +984,10 @@ kex_choose_conf(struct ssh *ssh) 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); + debug("kex: %s need=%d dh_need=%d", kex->name, need, dh_need); +#ifdef SSH_AUDIT_EVENTS -+ audit_kex(mode, newkeys->enc.name, newkeys->mac.name, newkeys->comp.name, kex->name); ++ audit_kex(ssh, mode, newkeys->enc.name, newkeys->mac.name, newkeys->comp.name, kex->name); +#endif } /* XXX need runden? */ kex->we_need = need; -@@ -1037,3 +1054,33 @@ dump_digest(char *msg, u_char *digest, i - sshbuf_dump_data(digest, len, stderr); +@@ -1129,6 +1146,36 @@ dump_digest(const char *msg, const u_cha } #endif -+ + +static void +enc_destroy(struct sshenc *enc) +{ @@ -1012,22 +1038,26 @@ diff -up openssh-7.6p1/kex.c.audit openssh-7.6p1/kex.c + mac_destroy(&newkeys->mac); + memset(&newkeys->comp, 0, sizeof(newkeys->comp)); +} -diff -up openssh-7.6p1/kex.h.audit openssh-7.6p1/kex.h ---- openssh-7.6p1/kex.h.audit 2017-10-04 17:18:32.822504987 +0200 -+++ openssh-7.6p1/kex.h 2017-10-04 17:18:32.836505059 +0200 -@@ -219,6 +219,8 @@ int kexgss_client(struct ssh *); ++ + /* + * Send a plaintext error message to the peer, suffixed by \r\n. + * Only used during banner exchange, and there only for the server. +diff -up openssh/kex.h.audit openssh/kex.h +--- openssh/kex.h.audit 2019-04-03 17:02:20.652885462 +0200 ++++ openssh/kex.h 2019-04-03 17:02:20.715886060 +0200 +@@ -226,6 +226,8 @@ int kexgss_client(struct ssh *); int kexgss_server(struct ssh *); #endif +void newkeys_destroy(struct newkeys *newkeys); + - int kex_dh_hash(int, const char *, const char *, - const u_char *, size_t, const u_char *, size_t, const u_char *, size_t, - const BIGNUM *, const BIGNUM *, const BIGNUM *, u_char *, size_t *); -diff -up openssh-7.6p1/mac.c.audit openssh-7.6p1/mac.c ---- openssh-7.6p1/mac.c.audit 2017-10-02 21:34:26.000000000 +0200 -+++ openssh-7.6p1/mac.c 2017-10-04 17:18:32.836505059 +0200 -@@ -242,6 +242,20 @@ mac_clear(struct sshmac *mac) + int kex_dh_keypair(struct kex *); + int kex_dh_enc(struct kex *, const struct sshbuf *, struct sshbuf **, + struct sshbuf **); +diff -up openssh/mac.c.audit openssh/mac.c +--- openssh/mac.c.audit 2019-04-03 17:02:20.652885462 +0200 ++++ openssh/mac.c 2019-04-03 17:02:20.715886060 +0200 +@@ -243,6 +243,20 @@ mac_clear(struct sshmac *mac) mac->umac_ctx = NULL; } @@ -1048,9 +1078,9 @@ diff -up openssh-7.6p1/mac.c.audit openssh-7.6p1/mac.c /* XXX copied from ciphers_valid */ #define MAC_SEP "," int -diff -up openssh-7.6p1/mac.h.audit openssh-7.6p1/mac.h ---- openssh-7.6p1/mac.h.audit 2017-10-02 21:34:26.000000000 +0200 -+++ openssh-7.6p1/mac.h 2017-10-04 17:18:32.837505064 +0200 +diff -up openssh/mac.h.audit openssh/mac.h +--- openssh/mac.h.audit 2019-03-27 23:26:14.000000000 +0100 ++++ openssh/mac.h 2019-04-03 17:02:20.715886060 +0200 @@ -49,5 +49,6 @@ int mac_compute(struct sshmac *, u_int3 int mac_check(struct sshmac *, u_int32_t, const u_char *, size_t, const u_char *, size_t); @@ -1058,23 +1088,23 @@ diff -up openssh-7.6p1/mac.h.audit openssh-7.6p1/mac.h +void mac_destroy(struct sshmac *); #endif /* SSHMAC_H */ -diff -up openssh-7.6p1/Makefile.in.audit openssh-7.6p1/Makefile.in ---- openssh-7.6p1/Makefile.in.audit 2017-10-04 17:18:32.749504614 +0200 -+++ openssh-7.6p1/Makefile.in 2017-10-04 17:18:32.837505064 +0200 -@@ -100,7 +100,8 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \ - kex.o kexdh.o kexgex.o kexecdh.o kexc25519.o \ - kexdhc.o kexgexc.o kexecdhc.o kexc25519c.o \ - kexdhs.o kexgexs.o kexecdhs.o kexc25519s.o \ +diff -up openssh/Makefile.in.audit openssh/Makefile.in +--- openssh/Makefile.in.audit 2019-04-03 17:02:20.705885965 +0200 ++++ openssh/Makefile.in 2019-04-03 17:02:20.715886060 +0200 +@@ -109,7 +109,8 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \ + kexgexc.o kexgexs.o \ + sntrup4591761.o kexsntrup4591761x25519.o kexgen.o \ + kexgssc.o \ - platform-pledge.o platform-tracing.o platform-misc.o + platform-pledge.o platform-tracing.o platform-misc.o \ + auditstub.o + SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \ - sshconnect.o sshconnect2.o mux.o -diff -up openssh-7.6p1/monitor.c.audit openssh-7.6p1/monitor.c ---- openssh-7.6p1/monitor.c.audit 2017-10-04 17:18:32.824504997 +0200 -+++ openssh-7.6p1/monitor.c 2017-10-04 17:18:32.837505064 +0200 -@@ -102,6 +102,7 @@ +diff -up openssh/monitor.c.audit openssh/monitor.c +--- openssh/monitor.c.audit 2019-04-03 17:02:20.674885671 +0200 ++++ openssh/monitor.c 2019-04-03 17:03:17.201421405 +0200 +@@ -93,6 +93,7 @@ #include "compat.h" #include "ssh2.h" #include "authfd.h" @@ -1082,28 +1112,28 @@ diff -up openssh-7.6p1/monitor.c.audit openssh-7.6p1/monitor.c #include "match.h" #include "ssherr.h" -@@ -117,6 +118,8 @@ extern Buffer auth_debug; +@@ -107,6 +108,8 @@ extern u_char session_id[]; extern struct sshbuf *loginmsg; extern struct sshauthopt *auth_opts; /* XXX move to permanent ssh->authctxt? */ -+extern void destroy_sensitive_data(int); ++extern void destroy_sensitive_data(struct ssh *, int); + /* State exported from the child */ static struct sshbuf *child_state; -@@ -167,6 +170,11 @@ int mm_answer_gss_updatecreds(int, Buffe +@@ -157,6 +160,11 @@ int mm_answer_gss_updatecreds(struct ssh #ifdef SSH_AUDIT_EVENTS - int mm_answer_audit_event(int, struct sshbuf *); - int mm_answer_audit_command(int, struct sshbuf *); -+int mm_answer_audit_end_command(int, struct sshbuf *); -+int mm_answer_audit_unsupported_body(int, struct sshbuf *); -+int mm_answer_audit_kex_body(int, struct sshbuf *); -+int mm_answer_audit_session_key_free_body(int, struct sshbuf *); -+int mm_answer_audit_server_key_free(int, struct sshbuf *); + int mm_answer_audit_event(struct ssh *, int, struct sshbuf *); + int mm_answer_audit_command(struct ssh *, int, struct sshbuf *); ++int mm_answer_audit_end_command(struct ssh *, int, struct sshbuf *); ++int mm_answer_audit_unsupported_body(struct ssh *, int, struct sshbuf *); ++int mm_answer_audit_kex_body(struct ssh *, int, struct sshbuf *); ++int mm_answer_audit_session_key_free_body(struct ssh *, int, struct sshbuf *); ++int mm_answer_audit_server_key_free(struct ssh *, int, struct sshbuf *); #endif - static int monitor_read_log(struct monitor *); -@@ -222,6 +230,10 @@ struct mon_table mon_dispatch_proto20[] + static Authctxt *authctxt; +@@ -215,6 +223,10 @@ struct mon_table mon_dispatch_proto20[] #endif #ifdef SSH_AUDIT_EVENTS {MONITOR_REQ_AUDIT_EVENT, MON_PERMIT, mm_answer_audit_event}, @@ -1114,7 +1144,7 @@ diff -up openssh-7.6p1/monitor.c.audit openssh-7.6p1/monitor.c #endif #ifdef BSD_AUTH {MONITOR_REQ_BSDAUTHQUERY, MON_ISAUTH, mm_answer_bsdauthquery}, -@@ -260,6 +272,11 @@ struct mon_table mon_dispatch_postauth20 +@@ -249,6 +261,11 @@ struct mon_table mon_dispatch_postauth20 #ifdef SSH_AUDIT_EVENTS {MONITOR_REQ_AUDIT_EVENT, MON_PERMIT, mm_answer_audit_event}, {MONITOR_REQ_AUDIT_COMMAND, MON_PERMIT, mm_answer_audit_command}, @@ -1126,7 +1156,7 @@ diff -up openssh-7.6p1/monitor.c.audit openssh-7.6p1/monitor.c #endif {0, 0, NULL} }; -@@ -1396,8 +1413,10 @@ mm_answer_keyverify(int sock, struct ssh +@@ -1445,8 +1462,10 @@ mm_answer_keyverify(struct ssh *ssh, int char *sigalg; size_t signaturelen, datalen, bloblen; int r, ret, valid_data = 0, encoded_ret; @@ -1138,7 +1168,7 @@ diff -up openssh-7.6p1/monitor.c.audit openssh-7.6p1/monitor.c (r = sshbuf_get_string(m, &signature, &signaturelen)) != 0 || (r = sshbuf_get_string(m, &data, &datalen)) != 0 || (r = sshbuf_get_cstring(m, &sigalg, NULL)) != 0) -@@ -1405,6 +1424,8 @@ mm_answer_keyverify(int sock, struct ssh +@@ -1455,6 +1474,8 @@ mm_answer_keyverify(struct ssh *ssh, int if (hostbased_cuser == NULL || hostbased_chost == NULL || !monitor_allowed_key(blob, bloblen)) fatal("%s: bad key, not previously allowed", __func__); @@ -1147,18 +1177,18 @@ diff -up openssh-7.6p1/monitor.c.audit openssh-7.6p1/monitor.c /* Empty signature algorithm means NULL. */ if (*sigalg == '\0') { -@@ -1414,21 +1435,24 @@ mm_answer_keyverify(int sock, struct ssh +@@ -1470,21 +1491,24 @@ mm_answer_keyverify(struct ssh *ssh, int case MM_USERKEY: valid_data = monitor_valid_userblob(data, datalen); auth_method = "publickey"; -+ ret = user_key_verify(key, signature, signaturelen, data, -+ datalen, sigalg, active_state->compat); ++ ret = user_key_verify(ssh, key, signature, signaturelen, data, ++ datalen, sigalg, ssh->compat); break; case MM_HOSTKEY: valid_data = monitor_valid_hostbasedblob(data, datalen, hostbased_cuser, hostbased_chost); -+ ret = hostbased_key_verify(key, signature, signaturelen, data, -+ datalen, sigalg, active_state->compat); ++ ret = hostbased_key_verify(ssh, key, signature, signaturelen, data, ++ datalen, sigalg, ssh->compat); auth_method = "hostbased"; break; default: @@ -1170,33 +1200,59 @@ diff -up openssh-7.6p1/monitor.c.audit openssh-7.6p1/monitor.c fatal("%s: bad signature data blob", __func__); - ret = sshkey_verify(key, signature, signaturelen, data, datalen, -- sigalg, active_state->compat); +- sigalg, ssh->compat); debug3("%s: %s %p signature %s", __func__, auth_method, key, (ret == 0) ? "verified" : "unverified"); auth2_record_key(authctxt, ret == 0, key); -@@ -1485,6 +1509,12 @@ mm_session_close(Session *s) +@@ -1536,13 +1560,19 @@ mm_record_login(struct ssh *ssh, Session + } + + static void +-mm_session_close(Session *s) ++mm_session_close(struct ssh *ssh, Session *s) + { + debug3("%s: session %d pid %ld", __func__, s->self, (long)s->pid); + if (s->ttyfd != -1) { debug3("%s: tty %s ptyfd %d", __func__, s->tty, s->ptyfd); session_pty_cleanup2(s); } +#ifdef SSH_AUDIT_EVENTS + if (s->command != NULL) { + debug3("%s: command %d", __func__, s->command_handle); -+ session_end_command2(s); ++ session_end_command2(ssh, s); + } +#endif session_unused(s->self); } -@@ -1588,6 +1618,8 @@ mm_answer_term(int sock, Buffer *req) +@@ -1609,7 +1639,7 @@ mm_answer_pty(struct ssh *ssh, int sock, + + error: + if (s != NULL) +- mm_session_close(s); ++ mm_session_close(ssh, s); + if ((r = sshbuf_put_u32(m, 0)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + mm_request_send(sock, MONITOR_ANS_PTY, m); +@@ -1628,7 +1658,7 @@ mm_answer_pty_cleanup(struct ssh *ssh, i + if ((r = sshbuf_get_cstring(m, &tty, NULL)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + if ((s = session_by_tty(tty)) != NULL) +- mm_session_close(s); ++ mm_session_close(ssh, s); + sshbuf_reset(m); + free(tty); + return (0); +@@ -1650,6 +1680,8 @@ mm_answer_term(struct ssh *ssh, int sock sshpam_cleanup(); #endif -+ destroy_sensitive_data(0); ++ destroy_sensitive_data(ssh, 0); + while (waitpid(pmonitor->m_pid, &status, 0) == -1) if (errno != EINTR) exit(1); -@@ -1630,12 +1662,47 @@ mm_answer_audit_command(int socket, Buff +@@ -1696,12 +1728,47 @@ mm_answer_audit_command(struct ssh *ssh, { char *cmd; int r; @@ -1213,7 +1269,7 @@ diff -up openssh-7.6p1/monitor.c.audit openssh-7.6p1/monitor.c + fatal("%s: error allocating a session", __func__); + s->command = cmd; +#ifdef SSH_AUDIT_EVENTS -+ s->command_handle = audit_run_command(cmd); ++ s->command_handle = audit_run_command(ssh, cmd); +#endif + + sshbuf_reset(m); @@ -1225,7 +1281,7 @@ diff -up openssh-7.6p1/monitor.c.audit openssh-7.6p1/monitor.c +} + +int -+mm_answer_audit_end_command(int socket, struct sshbuf *m) ++mm_answer_audit_end_command(struct ssh *ssh, int socket, struct sshbuf *m) +{ + int handle, r; + size_t len; @@ -1241,19 +1297,19 @@ diff -up openssh-7.6p1/monitor.c.audit openssh-7.6p1/monitor.c + if (s == NULL || s->ttyfd != -1 || s->command == NULL || + strcmp(s->command, cmd) != 0) + fatal("%s: invalid handle", __func__); -+ mm_session_close(s); ++ mm_session_close(ssh, s); free(cmd); return (0); } -@@ -1702,6 +1768,7 @@ monitor_apply_keystate(struct monitor *p +@@ -1767,6 +1834,7 @@ monitor_apply_keystate(struct ssh *ssh, void - mm_get_keystate(struct monitor *pmonitor) + mm_get_keystate(struct ssh *ssh, struct monitor *pmonitor) { + struct sshbuf *m; debug3("%s: Waiting for new keys", __func__); if ((child_state = sshbuf_new()) == NULL) -@@ -1709,6 +1776,19 @@ mm_get_keystate(struct monitor *pmonitor +@@ -1774,6 +1842,19 @@ mm_get_keystate(struct ssh *ssh, struct mm_request_receive_expect(pmonitor->m_sendfd, MONITOR_REQ_KEYEXPORT, child_state); debug3("%s: GOT new keys", __func__); @@ -1262,7 +1318,7 @@ diff -up openssh-7.6p1/monitor.c.audit openssh-7.6p1/monitor.c + m = sshbuf_new(); + mm_request_receive_expect(pmonitor->m_sendfd, + MONITOR_REQ_AUDIT_SESSION_KEY_FREE, m); -+ mm_answer_audit_session_key_free_body(pmonitor->m_sendfd, m); ++ mm_answer_audit_session_key_free_body(ssh, pmonitor->m_sendfd, m); + sshbuf_free(m); +#endif + @@ -1273,20 +1329,20 @@ diff -up openssh-7.6p1/monitor.c.audit openssh-7.6p1/monitor.c } -@@ -1976,3 +2056,102 @@ mm_answer_gss_updatecreds(int socket, Bu +@@ -2066,3 +2147,102 @@ mm_answer_gss_updatecreds(struct ssh *ss #endif /* GSSAPI */ +#ifdef SSH_AUDIT_EVENTS +int -+mm_answer_audit_unsupported_body(int sock, struct sshbuf *m) ++mm_answer_audit_unsupported_body(struct ssh *ssh, int sock, struct sshbuf *m) +{ + int what, r; + + if ((r = sshbuf_get_u32(m, &what)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + -+ audit_unsupported_body(what); ++ audit_unsupported_body(ssh, what); + + sshbuf_reset(m); + @@ -1295,7 +1351,7 @@ diff -up openssh-7.6p1/monitor.c.audit openssh-7.6p1/monitor.c +} + +int -+mm_answer_audit_kex_body(int sock, struct sshbuf *m) ++mm_answer_audit_kex_body(struct ssh *ssh, int sock, struct sshbuf *m) +{ + int ctos, r; + char *cipher, *mac, *compress, *pfs; @@ -1315,7 +1371,7 @@ diff -up openssh-7.6p1/monitor.c.audit openssh-7.6p1/monitor.c + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + uid = (pid_t) tmp; + -+ audit_kex_body(ctos, cipher, mac, compress, pfs, pid, uid); ++ audit_kex_body(ssh, ctos, cipher, mac, compress, pfs, pid, uid); + + free(cipher); + free(mac); @@ -1328,7 +1384,7 @@ diff -up openssh-7.6p1/monitor.c.audit openssh-7.6p1/monitor.c +} + +int -+mm_answer_audit_session_key_free_body(int sock, struct sshbuf *m) ++mm_answer_audit_session_key_free_body(struct ssh *ssh, int sock, struct sshbuf *m) +{ + int ctos, r; + u_int64_t tmp; @@ -1343,7 +1399,7 @@ diff -up openssh-7.6p1/monitor.c.audit openssh-7.6p1/monitor.c + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + uid = (uid_t) tmp; + -+ audit_session_key_free_body(ctos, pid, uid); ++ audit_session_key_free_body(ssh, ctos, pid, uid); + + sshbuf_reset(m); + @@ -1352,7 +1408,7 @@ diff -up openssh-7.6p1/monitor.c.audit openssh-7.6p1/monitor.c +} + +int -+mm_answer_audit_server_key_free(int sock, struct sshbuf *m) ++mm_answer_audit_server_key_free(struct ssh *ssh, int sock, struct sshbuf *m) +{ + size_t len, r; + char *fp; @@ -1368,7 +1424,7 @@ diff -up openssh-7.6p1/monitor.c.audit openssh-7.6p1/monitor.c + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + uid = (uid_t) tmp; + -+ audit_destroy_sensitive_data(fp, pid, uid); ++ audit_destroy_sensitive_data(ssh, fp, pid, uid); + + free(fp); + sshbuf_reset(m); @@ -1376,10 +1432,10 @@ diff -up openssh-7.6p1/monitor.c.audit openssh-7.6p1/monitor.c + return 0; +} +#endif /* SSH_AUDIT_EVENTS */ -diff -up openssh-7.6p1/monitor.h.audit openssh-7.6p1/monitor.h ---- openssh-7.6p1/monitor.h.audit 2017-10-04 17:18:32.781504777 +0200 -+++ openssh-7.6p1/monitor.h 2017-10-04 17:18:32.837505064 +0200 -@@ -69,7 +69,13 @@ enum monitor_reqtype { +diff -up openssh/monitor.h.audit openssh/monitor.h +--- openssh/monitor.h.audit 2019-04-03 17:02:20.674885671 +0200 ++++ openssh/monitor.h 2019-04-03 17:02:20.715886060 +0200 +@@ -65,7 +65,13 @@ enum monitor_reqtype { MONITOR_REQ_PAM_QUERY = 106, MONITOR_ANS_PAM_QUERY = 107, MONITOR_REQ_PAM_RESPOND = 108, MONITOR_ANS_PAM_RESPOND = 109, MONITOR_REQ_PAM_FREE_CTX = 110, MONITOR_ANS_PAM_FREE_CTX = 111, @@ -1390,14 +1446,14 @@ diff -up openssh-7.6p1/monitor.h.audit openssh-7.6p1/monitor.h + MONITOR_REQ_AUDIT_UNSUPPORTED = 118, MONITOR_ANS_AUDIT_UNSUPPORTED = 119, + MONITOR_REQ_AUDIT_KEX = 120, MONITOR_ANS_AUDIT_KEX = 121, + MONITOR_REQ_AUDIT_SESSION_KEY_FREE = 122, MONITOR_ANS_AUDIT_SESSION_KEY_FREE = 123, -+ MONITOR_REQ_AUDIT_SERVER_KEY_FREE = 124 ++ MONITOR_REQ_AUDIT_SERVER_KEY_FREE = 124, - }; - -diff -up openssh-7.6p1/monitor_wrap.c.audit openssh-7.6p1/monitor_wrap.c ---- openssh-7.6p1/monitor_wrap.c.audit 2017-10-04 17:18:32.750504619 +0200 -+++ openssh-7.6p1/monitor_wrap.c 2017-10-04 17:18:32.838505069 +0200 -@@ -463,7 +463,7 @@ mm_key_allowed(enum mm_keytype type, con + MONITOR_REQ_GSSSIGN = 150, MONITOR_ANS_GSSSIGN = 151, + MONITOR_REQ_GSSUPCREDS = 152, MONITOR_ANS_GSSUPCREDS = 153, +diff -up openssh/monitor_wrap.c.audit openssh/monitor_wrap.c +--- openssh/monitor_wrap.c.audit 2019-04-03 17:02:20.653885472 +0200 ++++ openssh/monitor_wrap.c 2019-04-03 17:02:20.716886069 +0200 +@@ -513,7 +513,7 @@ mm_key_allowed(enum mm_keytype type, con */ int @@ -1406,7 +1462,7 @@ diff -up openssh-7.6p1/monitor_wrap.c.audit openssh-7.6p1/monitor_wrap.c const u_char *data, size_t datalen, const char *sigalg, u_int compat) { struct sshbuf *m; -@@ -478,7 +478,8 @@ mm_sshkey_verify(const struct sshkey *ke +@@ -525,7 +525,8 @@ mm_sshkey_verify(const struct sshkey *ke if ((m = sshbuf_new()) == NULL) fatal("%s: sshbuf_new failed", __func__); @@ -1416,34 +1472,35 @@ diff -up openssh-7.6p1/monitor_wrap.c.audit openssh-7.6p1/monitor_wrap.c (r = sshbuf_put_string(m, sig, siglen)) != 0 || (r = sshbuf_put_string(m, data, datalen)) != 0 || (r = sshbuf_put_cstring(m, sigalg == NULL ? "" : sigalg)) != 0) -@@ -497,6 +498,20 @@ mm_sshkey_verify(const struct sshkey *ke +@@ -547,6 +548,20 @@ mm_sshkey_verify(const struct sshkey *ke return 0; } +int -+mm_hostbased_key_verify(const struct sshkey *key, const u_char *sig, size_t siglen, ++mm_hostbased_key_verify(struct ssh *ssh, const struct sshkey *key, const u_char *sig, size_t siglen, + const u_char *data, size_t datalen, const char *pkalg, u_int compat) +{ + return mm_sshkey_verify(MM_HOSTKEY, key, sig, siglen, data, datalen, pkalg, compat); +} + +int -+mm_user_key_verify(const struct sshkey *key, const u_char *sig, size_t siglen, ++mm_user_key_verify(struct ssh *ssh, const struct sshkey *key, const u_char *sig, size_t siglen, + const u_char *data, size_t datalen, const char *pkalg, u_int compat) +{ + return mm_sshkey_verify(MM_USERKEY, key, sig, siglen, data, datalen, pkalg, compat); +} + void - mm_send_keystate(struct monitor *monitor) + mm_send_keystate(struct ssh *ssh, struct monitor *monitor) { -@@ -874,11 +889,12 @@ mm_audit_event(ssh_audit_event_t event) +@@ -900,11 +915,12 @@ mm_audit_event(struct ssh *ssh, ssh_audi sshbuf_free(m); } -void +-mm_audit_run_command(const char *command) +int - mm_audit_run_command(const char *command) ++mm_audit_run_command(struct ssh *ssh, const char *command) { struct sshbuf *m; int r; @@ -1451,7 +1508,7 @@ diff -up openssh-7.6p1/monitor_wrap.c.audit openssh-7.6p1/monitor_wrap.c debug3("%s entering command %s", __func__, command); -@@ -885,6 +901,30 @@ mm_audit_run_command(const char *command +@@ -914,6 +930,30 @@ mm_audit_run_command(const char *command fatal("%s: buffer error: %s", __func__, ssh_err(r)); mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUDIT_COMMAND, m); @@ -1465,7 +1522,7 @@ diff -up openssh-7.6p1/monitor_wrap.c.audit openssh-7.6p1/monitor_wrap.c +} + +void -+mm_audit_end_command(int handle, const char *command) ++mm_audit_end_command(struct ssh *ssh, int handle, const char *command) +{ + int r; + struct sshbuf *m; @@ -1482,13 +1539,13 @@ diff -up openssh-7.6p1/monitor_wrap.c.audit openssh-7.6p1/monitor_wrap.c sshbuf_free(m); } #endif /* SSH_AUDIT_EVENTS */ -@@ -1020,3 +1056,83 @@ mm_ssh_gssapi_update_creds(ssh_gssapi_cc - return (ok); +@@ -1074,3 +1114,83 @@ mm_ssh_gssapi_update_creds(ssh_gssapi_cc } + #endif /* GSSAPI */ +#ifdef SSH_AUDIT_EVENTS +void -+mm_audit_unsupported_body(int what) ++mm_audit_unsupported_body(struct ssh *ssh, int what) +{ + int r; + struct sshbuf *m; @@ -1506,7 +1563,7 @@ diff -up openssh-7.6p1/monitor_wrap.c.audit openssh-7.6p1/monitor_wrap.c +} + +void -+mm_audit_kex_body(int ctos, char *cipher, char *mac, char *compress, char *fps, pid_t pid, ++mm_audit_kex_body(struct ssh *ssh, int ctos, char *cipher, char *mac, char *compress, char *fps, pid_t pid, + uid_t uid) +{ + int r; @@ -1531,7 +1588,7 @@ diff -up openssh-7.6p1/monitor_wrap.c.audit openssh-7.6p1/monitor_wrap.c +} + +void -+mm_audit_session_key_free_body(int ctos, pid_t pid, uid_t uid) ++mm_audit_session_key_free_body(struct ssh *ssh, int ctos, pid_t pid, uid_t uid) +{ + int r; + struct sshbuf *m; @@ -1550,7 +1607,7 @@ diff -up openssh-7.6p1/monitor_wrap.c.audit openssh-7.6p1/monitor_wrap.c +} + +void -+mm_audit_destroy_sensitive_data(const char *fp, pid_t pid, uid_t uid) ++mm_audit_destroy_sensitive_data(struct ssh *ssh, const char *fp, pid_t pid, uid_t uid) +{ + int r; + struct sshbuf *m; @@ -1566,38 +1623,38 @@ diff -up openssh-7.6p1/monitor_wrap.c.audit openssh-7.6p1/monitor_wrap.c + sshbuf_free(m); +} +#endif /* SSH_AUDIT_EVENTS */ -diff -up openssh-7.6p1/monitor_wrap.h.audit openssh-7.6p1/monitor_wrap.h ---- openssh-7.6p1/monitor_wrap.h.audit 2017-10-04 17:18:32.750504619 +0200 -+++ openssh-7.6p1/monitor_wrap.h 2017-10-04 17:18:32.838505069 +0200 -@@ -53,7 +53,9 @@ int mm_key_allowed(enum mm_keytype, cons +diff -up openssh/monitor_wrap.h.audit openssh/monitor_wrap.h +--- openssh/monitor_wrap.h.audit 2019-04-03 17:02:20.653885472 +0200 ++++ openssh/monitor_wrap.h 2019-04-03 17:02:20.716886069 +0200 +@@ -57,7 +57,9 @@ int mm_user_key_allowed(struct ssh *, st struct sshauthopt **); - int mm_hostbased_key_allowed(struct passwd *, const char *, + int mm_hostbased_key_allowed(struct ssh *, struct passwd *, const char *, const char *, struct sshkey *); -int mm_sshkey_verify(const struct sshkey *, const u_char *, size_t, -+int mm_hostbased_key_verify(const struct sshkey *, const u_char *, size_t, ++int mm_hostbased_key_verify(struct ssh *, const struct sshkey *, const u_char *, size_t, + const u_char *, size_t, const char *, u_int); -+int mm_user_key_verify(const struct sshkey *, const u_char *, size_t, ++int mm_user_key_verify(struct ssh*, const struct sshkey *, const u_char *, size_t, const u_char *, size_t, const char *, u_int); #ifdef GSSAPI -@@ -78,7 +80,12 @@ void mm_sshpam_free_ctx(void *); +@@ -82,7 +84,12 @@ void mm_sshpam_free_ctx(void *); #ifdef SSH_AUDIT_EVENTS #include "audit.h" - void mm_audit_event(ssh_audit_event_t); + void mm_audit_event(struct ssh *, ssh_audit_event_t); -void mm_audit_run_command(const char *); -+int mm_audit_run_command(const char *); -+void mm_audit_end_command(int, const char *); -+void mm_audit_unsupported_body(int); -+void mm_audit_kex_body(int, char *, char *, char *, char *, pid_t, uid_t); -+void mm_audit_session_key_free_body(int, pid_t, uid_t); -+void mm_audit_destroy_sensitive_data(const char *, pid_t, uid_t); ++int mm_audit_run_command(struct ssh *ssh, const char *); ++void mm_audit_end_command(struct ssh *ssh, int, const char *); ++void mm_audit_unsupported_body(struct ssh *, int); ++void mm_audit_kex_body(struct ssh *, int, char *, char *, char *, char *, pid_t, uid_t); ++void mm_audit_session_key_free_body(struct ssh *, int, pid_t, uid_t); ++void mm_audit_destroy_sensitive_data(struct ssh *, const char *, pid_t, uid_t); #endif struct Session; -diff -up openssh-7.6p1/packet.c.audit openssh-7.6p1/packet.c ---- openssh-7.6p1/packet.c.audit 2017-10-04 17:18:32.672504220 +0200 -+++ openssh-7.6p1/packet.c 2017-10-04 17:25:48.141741390 +0200 -@@ -67,6 +67,7 @@ +diff -up openssh/packet.c.audit openssh/packet.c +--- openssh/packet.c.audit 2019-03-27 23:26:14.000000000 +0100 ++++ openssh/packet.c 2019-04-03 17:02:20.716886069 +0200 +@@ -77,6 +77,7 @@ #include #include "xmalloc.h" @@ -1605,7 +1662,7 @@ diff -up openssh-7.6p1/packet.c.audit openssh-7.6p1/packet.c #include "crc32.h" #include "compat.h" #include "ssh2.h" -@@ -502,6 +503,13 @@ ssh_packet_get_connection_out(struct ssh +@@ -510,6 +511,13 @@ ssh_packet_get_connection_out(struct ssh return ssh->state->connection_out; } @@ -1619,7 +1676,7 @@ diff -up openssh-7.6p1/packet.c.audit openssh-7.6p1/packet.c /* * Returns the IP-address of the remote host as a string. The returned * string must not be freed. -@@ -566,22 +574,19 @@ ssh_packet_close_internal(struct ssh *ss +@@ -587,22 +595,19 @@ ssh_packet_close_internal(struct ssh *ss { struct session_state *state = ssh->state; u_int mode; @@ -1642,18 +1699,18 @@ diff -up openssh-7.6p1/packet.c.audit openssh-7.6p1/packet.c + state->output = NULL; sshbuf_free(state->outgoing_packet); + state->outgoing_packet = NULL; - sshbuf_free(state->incoming_packet); + sshbuf_free(state->incoming_packet); + state->incoming_packet = NULL; for (mode = 0; mode < MODE_MAX; mode++) { kex_free_newkeys(state->newkeys[mode]); /* current keys */ state->newkeys[mode] = NULL; -@@ -615,8 +616,18 @@ ssh_packet_close_internal(struct ssh *ss +@@ -636,8 +641,18 @@ ssh_packet_close_internal(struct ssh *ss } cipher_free(state->send_context); cipher_free(state->receive_context); + if (had_keys && state->server_side) { + /* Assuming this is called only from privsep child */ -+ audit_session_key_free(MODE_MAX); ++ audit_session_key_free(ssh, MODE_MAX); + } state->send_context = state->receive_context = NULL; if (do_close) { @@ -1666,15 +1723,15 @@ diff -up openssh-7.6p1/packet.c.audit openssh-7.6p1/packet.c free(ssh->local_ipaddr); ssh->local_ipaddr = NULL; free(ssh->remote_ipaddr); -@@ -854,6 +863,7 @@ ssh_set_newkeys(struct ssh *ssh, int mod - (unsigned long long)state->p_read.blocks, +@@ -864,6 +879,7 @@ ssh_set_newkeys(struct ssh *ssh, int mod (unsigned long long)state->p_send.bytes, (unsigned long long)state->p_send.blocks); -+ audit_session_key_free(mode); - cipher_free(*ccp); - *ccp = NULL; kex_free_newkeys(state->newkeys[mode]); -@@ -2135,6 +2145,72 @@ ssh_packet_get_output(struct ssh *ssh) ++ audit_session_key_free(ssh, mode); + state->newkeys[mode] = NULL; + } + /* note that both bytes and the seqnr are not reset */ +@@ -2167,6 +2183,71 @@ ssh_packet_get_output(struct ssh *ssh) return (void *)ssh->state->output; } @@ -1728,18 +1785,17 @@ diff -up openssh-7.6p1/packet.c.audit openssh-7.6p1/packet.c +} + +void -+packet_destroy_all(int audit_it, int privsep) ++packet_destroy_all(struct ssh *ssh, int audit_it, int privsep) +{ + if (audit_it) -+ audit_it = (active_state != NULL && packet_state_has_keys(active_state->state)); -+ if (active_state != NULL) -+ packet_destroy_state(active_state->state); ++ audit_it = packet_state_has_keys(ssh->state); ++ packet_destroy_state(ssh->state); + if (audit_it) { +#ifdef SSH_AUDIT_EVENTS + if (privsep) -+ audit_session_key_free(MODE_MAX); ++ audit_session_key_free(ssh, MODE_MAX); + else -+ audit_session_key_free_body(MODE_MAX, getpid(), getuid()); ++ audit_session_key_free_body(ssh, MODE_MAX, getpid(), getuid()); +#endif + } +} @@ -1747,28 +1803,28 @@ diff -up openssh-7.6p1/packet.c.audit openssh-7.6p1/packet.c /* Reset after_authentication and reset compression in post-auth privsep */ static int ssh_packet_set_postauth(struct ssh *ssh) -diff -up openssh-7.6p1/packet.h.audit openssh-7.6p1/packet.h ---- openssh-7.6p1/packet.h.audit 2017-10-02 21:34:26.000000000 +0200 -+++ openssh-7.6p1/packet.h 2017-10-04 17:18:32.838505069 +0200 -@@ -217,4 +217,5 @@ extern struct ssh *active_state; +diff -up openssh/packet.h.audit openssh/packet.h +--- openssh/packet.h.audit 2019-03-27 23:26:14.000000000 +0100 ++++ openssh/packet.h 2019-04-03 17:02:20.716886069 +0200 +@@ -217,4 +217,5 @@ const u_char *sshpkt_ptr(struct ssh *, s # undef EC_POINT #endif -+void packet_destroy_all(int, int); ++void packet_destroy_all(struct ssh *, int, int); #endif /* PACKET_H */ -diff -up openssh-7.6p1/session.c.audit openssh-7.6p1/session.c ---- openssh-7.6p1/session.c.audit 2017-10-04 17:18:32.812504936 +0200 -+++ openssh-7.6p1/session.c 2017-10-04 17:18:32.839505074 +0200 -@@ -138,7 +138,7 @@ extern char *__progname; +diff -up openssh/session.c.audit openssh/session.c +--- openssh/session.c.audit 2019-04-03 17:02:20.712886031 +0200 ++++ openssh/session.c 2019-04-03 17:02:20.716886069 +0200 +@@ -136,7 +136,7 @@ extern char *__progname; extern int debug_flag; extern u_int utmp_len; extern int startup_pipe; -extern void destroy_sensitive_data(void); -+extern void destroy_sensitive_data(int); ++extern void destroy_sensitive_data(struct ssh *, int); extern struct sshbuf *loginmsg; extern struct sshauthopt *auth_opts; - char *tun_fwd_ifnames; /* serverloop.c */ -@@ -605,6 +605,14 @@ do_exec_pty(struct ssh *ssh, Session *s, + extern char *tun_fwd_ifnames; /* serverloop.c */ +@@ -648,6 +648,14 @@ do_exec_pty(struct ssh *ssh, Session *s, /* Parent. Close the slave side of the pseudo tty. */ close(ttyfd); @@ -1782,8 +1838,8 @@ diff -up openssh-7.6p1/session.c.audit openssh-7.6p1/session.c + /* Enter interactive session. */ s->ptymaster = ptymaster; - packet_set_interactive(1, -@@ -724,15 +732,19 @@ do_exec(struct ssh *ssh, Session *s, con + ssh_packet_set_interactive(ssh, 1, +@@ -740,15 +748,19 @@ do_exec(struct ssh *ssh, Session *s, con s->self); #ifdef SSH_AUDIT_EVENTS @@ -1801,24 +1857,24 @@ diff -up openssh-7.6p1/session.c.audit openssh-7.6p1/session.c + s->command = xstrdup(shell); } + if (s->command != NULL && s->ptyfd == -1) -+ s->command_handle = PRIVSEP(audit_run_command(s->command)); ++ s->command_handle = PRIVSEP(audit_run_command(ssh, s->command)); #endif if (s->ttyfd != -1) ret = do_exec_pty(ssh, s, command); -@@ -1499,8 +1511,11 @@ do_child(struct ssh *ssh, Session *s, co - int r = 0; +@@ -1556,8 +1568,11 @@ do_child(struct ssh *ssh, Session *s, co + sshpkt_fmt_connection_id(ssh, remote_id, sizeof(remote_id)); /* remove hostkey from the child's memory */ - destroy_sensitive_data(); -+ destroy_sensitive_data(1); - packet_clear_keys(); ++ destroy_sensitive_data(ssh, 1); + ssh_packet_clear_keys(ssh); + /* Don't audit this - both us and the parent would be talking to the + monitor over a single socket, with no synchronization. */ -+ packet_destroy_all(0, 1); ++ packet_destroy_all(ssh, 0, 1); /* Force a password change */ if (s->authctxt->force_pwchange) { -@@ -1714,6 +1729,9 @@ session_unused(int id) +@@ -1769,6 +1784,9 @@ session_unused(int id) sessions[id].ttyfd = -1; sessions[id].ptymaster = -1; sessions[id].x11_chanids = NULL; @@ -1828,7 +1884,7 @@ diff -up openssh-7.6p1/session.c.audit openssh-7.6p1/session.c sessions[id].next_unused = sessions_first_unused; sessions_first_unused = id; } -@@ -1796,6 +1814,19 @@ session_open(Authctxt *authctxt, int cha +@@ -1851,6 +1869,19 @@ session_open(Authctxt *authctxt, int cha } Session * @@ -1848,17 +1904,17 @@ diff -up openssh-7.6p1/session.c.audit openssh-7.6p1/session.c session_by_tty(char *tty) { int i; -@@ -2307,6 +2338,32 @@ session_exit_message(struct ssh *ssh, Se +@@ -2461,6 +2492,32 @@ session_exit_message(struct ssh *ssh, Se chan_write_failed(ssh, c); } +#ifdef SSH_AUDIT_EVENTS +void -+session_end_command2(Session *s) ++session_end_command2(struct ssh *ssh, Session *s) +{ + if (s->command != NULL) { + if (s->command_handle != -1) -+ audit_end_command(s->command_handle, s->command); ++ audit_end_command(ssh, s->command_handle, s->command); + free(s->command); + s->command = NULL; + s->command_handle = -1; @@ -1866,11 +1922,11 @@ diff -up openssh-7.6p1/session.c.audit openssh-7.6p1/session.c +} + +static void -+session_end_command(Session *s) ++session_end_command(struct ssh *ssh, Session *s) +{ + if (s->command != NULL) { + if (s->command_handle != -1) -+ PRIVSEP(audit_end_command(s->command_handle, s->command)); ++ PRIVSEP(audit_end_command(ssh, s->command_handle, s->command)); + free(s->command); + s->command = NULL; + s->command_handle = -1; @@ -1881,34 +1937,51 @@ diff -up openssh-7.6p1/session.c.audit openssh-7.6p1/session.c void session_close(struct ssh *ssh, Session *s) { -@@ -2320,6 +2377,10 @@ session_close(struct ssh *ssh, Session * +@@ -2474,6 +2531,10 @@ session_close(struct ssh *ssh, Session * if (s->ttyfd != -1) session_pty_cleanup(s); +#ifdef SSH_AUDIT_EVENTS + if (s->command) -+ session_end_command(s); ++ session_end_command(ssh, s); +#endif free(s->term); free(s->display); free(s->x11_chanids); -@@ -2528,6 +2589,15 @@ do_authenticated2(struct ssh *ssh, Authc +@@ -2549,14 +2610,14 @@ session_close_by_channel(struct ssh *ssh + } + + void +-session_destroy_all(struct ssh *ssh, void (*closefunc)(Session *)) ++session_destroy_all(struct ssh *ssh, void (*closefunc)(struct ssh *ssh, Session *)) + { + int i; + for (i = 0; i < sessions_nalloc; i++) { + Session *s = &sessions[i]; + if (s->used) { + if (closefunc != NULL) +- closefunc(s); ++ closefunc(ssh, s); + else + session_close(ssh, s); + } +@@ -2683,6 +2744,15 @@ do_authenticated2(struct ssh *ssh, Authc server_loop2(ssh, authctxt); } +static void -+do_cleanup_one_session(Session *s) ++do_cleanup_one_session(struct ssh *ssh, Session *s) +{ + session_pty_cleanup2(s); +#ifdef SSH_AUDIT_EVENTS -+ session_end_command2(s); ++ session_end_command2(ssh, s); +#endif +} + void do_cleanup(struct ssh *ssh, Authctxt *authctxt) { -@@ -2585,7 +2655,7 @@ do_cleanup(struct ssh *ssh, Authctxt *au +@@ -2746,7 +2816,7 @@ do_cleanup(struct ssh *ssh, Authctxt *au * or if running in monitor. */ if (!use_privsep || mm_is_monitor()) @@ -1917,10 +1990,10 @@ diff -up openssh-7.6p1/session.c.audit openssh-7.6p1/session.c } /* Return a name for the remote host that fits inside utmp_size */ -diff -up openssh-7.6p1/session.h.audit openssh-7.6p1/session.h ---- openssh-7.6p1/session.h.audit 2017-10-02 21:34:26.000000000 +0200 -+++ openssh-7.6p1/session.h 2017-10-04 17:18:32.839505074 +0200 -@@ -60,6 +60,12 @@ struct Session { +diff -up openssh/session.h.audit openssh/session.h +--- openssh/session.h.audit 2019-03-27 23:26:14.000000000 +0100 ++++ openssh/session.h 2019-04-03 17:02:20.717886079 +0200 +@@ -61,6 +61,12 @@ struct Session { char *name; char *val; } *env; @@ -1933,20 +2006,23 @@ diff -up openssh-7.6p1/session.h.audit openssh-7.6p1/session.h }; void do_authenticated(struct ssh *, Authctxt *); -@@ -72,8 +78,10 @@ void session_close_by_pid(struct ssh *s +@@ -71,10 +77,12 @@ void session_unused(int); + int session_input_channel_req(struct ssh *, Channel *, const char *); + void session_close_by_pid(struct ssh *ssh, pid_t, int); void session_close_by_channel(struct ssh *, int, void *); - void session_destroy_all(struct ssh *, void (*)(Session *)); +-void session_destroy_all(struct ssh *, void (*)(Session *)); ++void session_destroy_all(struct ssh *, void (*)(struct ssh*, Session *)); void session_pty_cleanup2(Session *); -+void session_end_command2(Session *); ++void session_end_command2(struct ssh *ssh, Session *); Session *session_new(void); +Session *session_by_id(int); Session *session_by_tty(char *); void session_close(struct ssh *, Session *); void do_setusercontext(struct passwd *); -diff -up openssh-7.6p1/sshd.c.audit openssh-7.6p1/sshd.c ---- openssh-7.6p1/sshd.c.audit 2017-10-04 17:18:32.830505028 +0200 -+++ openssh-7.6p1/sshd.c 2017-10-04 17:18:32.839505074 +0200 +diff -up openssh/sshd.c.audit openssh/sshd.c +--- openssh/sshd.c.audit 2019-04-03 17:02:20.692885842 +0200 ++++ openssh/sshd.c 2019-04-03 17:02:20.717886079 +0200 @@ -122,6 +122,7 @@ #include "ssh-gss.h" #endif @@ -1955,16 +2031,18 @@ diff -up openssh-7.6p1/sshd.c.audit openssh-7.6p1/sshd.c #include "ssh-sandbox.h" #include "auth-options.h" #include "version.h" -@@ -248,7 +249,7 @@ Buffer loginmsg; +@@ -261,8 +262,8 @@ struct sshbuf *loginmsg; struct passwd *privsep_pw = NULL; /* Prototypes for various functions defined later in this file. */ -void destroy_sensitive_data(void); -+void destroy_sensitive_data(int); - void demote_sensitive_data(void); - static void do_ssh2_kex(void); +-void demote_sensitive_data(void); ++void destroy_sensitive_data(struct ssh *, int); ++void demote_sensitive_data(struct ssh *); + static void do_ssh2_kex(struct ssh *); -@@ -265,6 +266,15 @@ close_listen_socks(void) + /* +@@ -278,6 +279,15 @@ close_listen_socks(void) num_listen_socks = -1; } @@ -1980,8 +2058,8 @@ diff -up openssh-7.6p1/sshd.c.audit openssh-7.6p1/sshd.c static void close_startup_pipes(void) { -@@ -475,18 +485,45 @@ sshd_exchange_identification(struct ssh - } +@@ -380,18 +390,45 @@ grace_alarm_handler(int sig) + ssh_remote_port(the_active_state)); } -/* Destroy the host and server keys. They will no longer be needed. */ @@ -1991,7 +2069,7 @@ diff -up openssh-7.6p1/sshd.c.audit openssh-7.6p1/sshd.c + */ void -destroy_sensitive_data(void) -+destroy_sensitive_data(int privsep) ++destroy_sensitive_data(struct ssh *ssh, int privsep) { u_int i; +#ifdef SSH_AUDIT_EVENTS @@ -2014,10 +2092,10 @@ diff -up openssh-7.6p1/sshd.c.audit openssh-7.6p1/sshd.c + if (fp != NULL) { +#ifdef SSH_AUDIT_EVENTS + if (privsep) -+ PRIVSEP(audit_destroy_sensitive_data(fp, ++ PRIVSEP(audit_destroy_sensitive_data(ssh, fp, + pid, uid)); + else -+ audit_destroy_sensitive_data(fp, ++ audit_destroy_sensitive_data(ssh, fp, + pid, uid); +#endif + free(fp); @@ -2029,7 +2107,13 @@ diff -up openssh-7.6p1/sshd.c.audit openssh-7.6p1/sshd.c sshkey_free(sensitive_data.host_certificates[i]); sensitive_data.host_certificates[i] = NULL; } -@@ -499,16 +536,34 @@ demote_sensitive_data(void) +@@ -400,14 +437,26 @@ destroy_sensitive_data(void) + + /* Demote private to public keys for network child */ + void +-demote_sensitive_data(void) ++demote_sensitive_data(struct ssh *ssh) + { struct sshkey *tmp; u_int i; int r; @@ -2048,23 +2132,41 @@ diff -up openssh-7.6p1/sshd.c.audit openssh-7.6p1/sshd.c + fp = sshkey_fingerprint(sensitive_data.host_keys[i], options.fingerprint_hash, SSH_FP_HEX); + else + fp = NULL; - if ((r = sshkey_demote(sensitive_data.host_keys[i], - &tmp)) != 0) - fatal("could not demote host %s key: %s", - sshkey_type(sensitive_data.host_keys[i]), - ssh_err(r)); - sshkey_free(sensitive_data.host_keys[i]); + if ((r = sshkey_from_private( + sensitive_data.host_keys[i], &tmp)) != 0) + fatal("could not demote host %s key: %s", +@@ -415,6 +464,12 @@ demote_sensitive_data(void) + ssh_err(r)); + sshkey_free(sensitive_data.host_keys[i]); sensitive_data.host_keys[i] = tmp; + if (fp != NULL) { +#ifdef SSH_AUDIT_EVENTS -+ audit_destroy_sensitive_data(fp, pid, uid); ++ audit_destroy_sensitive_data(ssh, fp, pid, uid); +#endif + free(fp); + } } /* Certs do not need demotion */ } -@@ -587,7 +642,7 @@ privsep_preauth(Authctxt *authctxt) +@@ -442,7 +497,7 @@ reseed_prngs(void) + } + + static void +-privsep_preauth_child(void) ++privsep_preauth_child(struct ssh *ssh) + { + gid_t gidset[1]; + +@@ -457,7 +512,7 @@ privsep_preauth_child(void) + reseed_prngs(); + + /* Demote the private keys to public keys. */ +- demote_sensitive_data(); ++ demote_sensitive_data(ssh); + + #ifdef WITH_SELINUX + sshd_selinux_change_privsep_preauth_context(); +@@ -496,7 +551,7 @@ privsep_preauth(struct ssh *ssh) if (use_privsep == PRIVSEP_ON) box = ssh_sandbox_init(pmonitor); @@ -2073,28 +2175,64 @@ diff -up openssh-7.6p1/sshd.c.audit openssh-7.6p1/sshd.c if (pid == -1) { fatal("fork of unprivileged child failed"); } else if (pid != 0) { -@@ -1162,6 +1217,7 @@ server_accept_loop(int *sock_in, int *so +@@ -542,7 +597,7 @@ privsep_preauth(struct ssh *ssh) + /* Arrange for logging to be sent to the monitor */ + set_log_handler(mm_log_handler, pmonitor); + +- privsep_preauth_child(); ++ privsep_preauth_child(ssh); + setproctitle("%s", "[net]"); + if (box != NULL) + ssh_sandbox_child(box); +@@ -594,7 +649,7 @@ privsep_postauth(struct ssh *ssh, Authct + set_log_handler(mm_log_handler, pmonitor); + + /* Demote the private keys to public keys. */ +- demote_sensitive_data(); ++ demote_sensitive_data(ssh); + + reseed_prngs(); + +@@ -1057,7 +1112,7 @@ server_listen(void) + * from this function are in a forked subprocess. + */ + static void +-server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s) ++server_accept_loop(struct ssh *ssh, int *sock_in, int *sock_out, int *newsock, int *config_s) + { + fd_set *fdset; + int i, j, ret, maxfd; +@@ -1112,6 +1167,7 @@ server_accept_loop(int *sock_in, int *so if (received_sigterm) { logit("Received signal %d; terminating.", (int) received_sigterm); -+ destroy_sensitive_data(0); ++ destroy_sensitive_data(ssh, 0); close_listen_socks(); if (options.pid_file != NULL) unlink(options.pid_file); -@@ -2165,6 +2221,9 @@ main(int ac, char **av) +@@ -1978,7 +2034,7 @@ main(int ac, char **av) + #endif + + /* Accept a connection and return in a forked child */ +- server_accept_loop(&sock_in, &sock_out, ++ server_accept_loop(ssh, &sock_in, &sock_out, + &newsock, config_s); + } + +@@ -2222,6 +2278,9 @@ main(int ac, char **av) do_authenticated(ssh, authctxt); /* The connection has been terminated. */ -+ packet_destroy_all(1, 1); -+ destroy_sensitive_data(1); ++ packet_destroy_all(ssh, 1, 1); ++ destroy_sensitive_data(ssh, 1); + - packet_get_bytes(&ibytes, &obytes); + ssh_packet_get_bytes(ssh, &ibytes, &obytes); verbose("Transferred: sent %llu, received %llu bytes", (unsigned long long)obytes, (unsigned long long)ibytes); -@@ -2344,6 +2403,15 @@ void +@@ -2401,6 +2460,15 @@ do_ssh2_kex(struct ssh *ssh) + void cleanup_exit(int i) { - struct ssh *ssh = active_state; /* XXX */ + static int in_cleanup = 0; + int is_privsep_child; + @@ -2104,29 +2242,31 @@ diff -up openssh-7.6p1/sshd.c.audit openssh-7.6p1/sshd.c + if (in_cleanup) + _exit(i); + in_cleanup = 1; - - if (the_authctxt) { - do_cleanup(ssh, the_authctxt); -@@ -2356,9 +2424,14 @@ 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 && +@@ -2414,9 +2482,16 @@ cleanup_exit(int i) pmonitor->m_pid, strerror(errno)); } } + is_privsep_child = use_privsep && pmonitor != NULL && pmonitor->m_pid == 0; -+ if (sensitive_data.host_keys != NULL) -+ destroy_sensitive_data(is_privsep_child); -+ packet_destroy_all(1, is_privsep_child); ++ if (sensitive_data.host_keys != NULL && the_active_state != NULL) ++ destroy_sensitive_data(the_active_state, is_privsep_child); ++ if (the_active_state != NULL) ++ packet_destroy_all(the_active_state, 1, is_privsep_child); #ifdef SSH_AUDIT_EVENTS /* done after do_cleanup so it can cancel the PAM auth 'thread' */ -- if (!use_privsep || mm_is_monitor()) -+ if ((the_authctxt == NULL || !the_authctxt->authenticated) && +- if (the_active_state != NULL && (!use_privsep || mm_is_monitor())) ++ if (the_active_state != NULL && ++ (the_authctxt == NULL || !the_authctxt->authenticated) && + (!use_privsep || mm_is_monitor())) - audit_event(SSH_CONNECTION_ABANDON); + audit_event(the_active_state, SSH_CONNECTION_ABANDON); #endif _exit(i); -diff -up openssh-7.6p1/sshkey.c.audit openssh-7.6p1/sshkey.c ---- openssh-7.6p1/sshkey.c.audit 2017-10-04 17:18:32.758504660 +0200 -+++ openssh-7.6p1/sshkey.c 2017-10-04 17:18:32.839505074 +0200 -@@ -295,6 +295,32 @@ sshkey_type_is_valid_ca(int type) +diff -up openssh/sshkey.c.audit openssh/sshkey.c +--- openssh/sshkey.c.audit 2019-04-03 17:02:20.657885510 +0200 ++++ openssh/sshkey.c 2019-04-03 17:02:20.718886088 +0200 +@@ -331,6 +331,38 @@ sshkey_type_is_valid_ca(int type) } int @@ -2135,11 +2275,17 @@ diff -up openssh-7.6p1/sshkey.c.audit openssh-7.6p1/sshkey.c + switch (k->type) { +#ifdef WITH_OPENSSL + case KEY_RSA_CERT: -+ case KEY_RSA: -+ return k->rsa->d != NULL; ++ case KEY_RSA: { ++ const BIGNUM *d; ++ RSA_get0_key(k->rsa, NULL, NULL, &d); ++ return d != NULL; ++ } + case KEY_DSA_CERT: -+ case KEY_DSA: -+ return k->dsa->priv_key != NULL; ++ case KEY_DSA: { ++ const BIGNUM *priv_key; ++ DSA_get0_key(k->dsa, NULL, &priv_key); ++ return priv_key != NULL; ++ } +#ifdef OPENSSL_HAS_ECC + case KEY_ECDSA_CERT: + case KEY_ECDSA: @@ -2159,10 +2305,10 @@ diff -up openssh-7.6p1/sshkey.c.audit openssh-7.6p1/sshkey.c sshkey_is_cert(const struct sshkey *k) { if (k == NULL) -diff -up openssh-7.6p1/sshkey.h.audit openssh-7.6p1/sshkey.h ---- openssh-7.6p1/sshkey.h.audit 2017-10-04 17:18:32.758504660 +0200 -+++ openssh-7.6p1/sshkey.h 2017-10-04 17:18:32.840505079 +0200 -@@ -133,6 +133,7 @@ u_int sshkey_size(const struct sshkey +diff -up openssh/sshkey.h.audit openssh/sshkey.h +--- openssh/sshkey.h.audit 2019-04-03 17:02:20.657885510 +0200 ++++ openssh/sshkey.h 2019-04-03 17:02:20.718886088 +0200 +@@ -148,6 +148,7 @@ u_int sshkey_size(const struct sshkey int sshkey_generate(int type, u_int bits, struct sshkey **keyp); int sshkey_from_private(const struct sshkey *, struct sshkey **); int sshkey_type_from_name(const char *); diff --git a/openssh-7.6p1-cleanup-selinux.patch b/openssh-7.6p1-cleanup-selinux.patch index 3b5001a..08cd349 100644 --- a/openssh-7.6p1-cleanup-selinux.patch +++ b/openssh-7.6p1-cleanup-selinux.patch @@ -1,6 +1,6 @@ diff -up openssh/auth2-pubkey.c.refactor openssh/auth2-pubkey.c ---- openssh/auth2-pubkey.c.refactor 2017-09-27 13:10:19.556830609 +0200 -+++ openssh/auth2-pubkey.c 2017-09-27 13:10:19.677831274 +0200 +--- openssh/auth2-pubkey.c.refactor 2019-04-04 13:19:12.188821236 +0200 ++++ openssh/auth2-pubkey.c 2019-04-04 13:19:12.276822078 +0200 @@ -72,6 +72,9 @@ extern ServerOptions options; extern u_char *session_id2; @@ -11,7 +11,7 @@ diff -up openssh/auth2-pubkey.c.refactor openssh/auth2-pubkey.c static char * format_key(const struct sshkey *key) -@@ -432,7 +435,8 @@ match_principals_command(struct passwd * +@@ -511,7 +514,8 @@ match_principals_command(struct ssh *ssh if ((pid = subprocess("AuthorizedPrincipalsCommand", runas_pw, command, ac, av, &f, @@ -21,7 +21,7 @@ diff -up openssh/auth2-pubkey.c.refactor openssh/auth2-pubkey.c goto out; uid_swapped = 1; -@@ -762,7 +766,8 @@ user_key_command_allowed2(struct passwd +@@ -981,7 +985,8 @@ user_key_command_allowed2(struct ssh *ss if ((pid = subprocess("AuthorizedKeysCommand", runas_pw, command, ac, av, &f, @@ -32,9 +32,9 @@ diff -up openssh/auth2-pubkey.c.refactor openssh/auth2-pubkey.c uid_swapped = 1; diff -up openssh/auth.c.refactor openssh/auth.c ---- openssh/auth.c.refactor 2017-09-27 13:10:19.640831071 +0200 -+++ openssh/auth.c 2017-09-27 13:10:19.678831279 +0200 -@@ -1435,7 +1435,8 @@ argv_assemble(int argc, char **argv) +--- openssh/auth.c.refactor 2019-04-04 13:19:12.235821686 +0200 ++++ openssh/auth.c 2019-04-04 13:19:12.276822078 +0200 +@@ -756,7 +756,8 @@ auth_get_canonical_hostname(struct ssh * */ pid_t subprocess(const char *tag, struct passwd *pw, const char *command, @@ -44,7 +44,7 @@ diff -up openssh/auth.c.refactor openssh/auth.c { FILE *f = NULL; struct stat st; -@@ -1551,7 +1552,7 @@ subprocess(const char *tag, struct passw +@@ -872,7 +873,7 @@ subprocess(const char *tag, struct passw } #ifdef WITH_SELINUX @@ -54,9 +54,9 @@ diff -up openssh/auth.c.refactor openssh/auth.c strerror(errno)); _exit(127); diff -up openssh/auth.h.refactor openssh/auth.h ---- openssh/auth.h.refactor 2017-09-25 01:48:10.000000000 +0200 -+++ openssh/auth.h 2017-09-27 13:10:19.678831279 +0200 -@@ -144,7 +144,7 @@ int exited_cleanly(pid_t, const char *, +--- openssh/auth.h.refactor 2019-04-04 13:19:12.251821839 +0200 ++++ openssh/auth.h 2019-04-04 13:19:12.276822078 +0200 +@@ -235,7 +235,7 @@ struct passwd *fakepw(void); #define SSH_SUBPROCESS_STDOUT_CAPTURE (1<<1) /* Redirect stdout */ #define SSH_SUBPROCESS_STDERR_DISCARD (1<<2) /* Discard stderr */ pid_t subprocess(const char *, struct passwd *, @@ -66,8 +66,8 @@ diff -up openssh/auth.h.refactor openssh/auth.h int sys_auth_passwd(struct ssh *, const char *); diff -up openssh/openbsd-compat/port-linux.h.refactor openssh/openbsd-compat/port-linux.h ---- openssh/openbsd-compat/port-linux.h.refactor 2017-09-27 13:10:19.634831038 +0200 -+++ openssh/openbsd-compat/port-linux.h 2017-09-27 13:10:54.954025248 +0200 +--- openssh/openbsd-compat/port-linux.h.refactor 2019-04-04 13:19:12.256821887 +0200 ++++ openssh/openbsd-compat/port-linux.h 2019-04-04 13:19:12.276822078 +0200 @@ -26,8 +26,8 @@ void ssh_selinux_setfscreatecon(const ch int sshd_selinux_enabled(void); @@ -80,9 +80,9 @@ diff -up openssh/openbsd-compat/port-linux.h.refactor openssh/openbsd-compat/por #endif diff -up openssh/openbsd-compat/port-linux-sshd.c.refactor openssh/openbsd-compat/port-linux-sshd.c ---- openssh/openbsd-compat/port-linux-sshd.c.refactor 2017-09-27 13:10:19.634831038 +0200 -+++ openssh/openbsd-compat/port-linux-sshd.c 2017-09-27 13:12:06.811420371 +0200 -@@ -48,11 +48,6 @@ +--- openssh/openbsd-compat/port-linux-sshd.c.refactor 2019-04-04 13:19:12.256821887 +0200 ++++ openssh/openbsd-compat/port-linux-sshd.c 2019-04-04 13:19:12.276822078 +0200 +@@ -49,11 +49,6 @@ #include #endif @@ -94,7 +94,7 @@ diff -up openssh/openbsd-compat/port-linux-sshd.c.refactor openssh/openbsd-compa /* Wrapper around is_selinux_enabled() to log its return value once only */ int sshd_selinux_enabled(void) -@@ -222,7 +217,8 @@ get_user_context(const char *sename, con +@@ -223,7 +218,8 @@ get_user_context(const char *sename, con } static void @@ -104,7 +104,7 @@ diff -up openssh/openbsd-compat/port-linux-sshd.c.refactor openssh/openbsd-compa { *role = NULL; *level = NULL; -@@ -240,8 +236,8 @@ ssh_selinux_get_role_level(char **role, +@@ -241,8 +237,8 @@ ssh_selinux_get_role_level(char **role, /* Return the default security context for the given username */ static int @@ -115,7 +115,7 @@ diff -up openssh/openbsd-compat/port-linux-sshd.c.refactor openssh/openbsd-compa { char *sename, *lvl; char *role; -@@ -249,7 +245,7 @@ sshd_selinux_getctxbyname(char *pwname, +@@ -250,7 +246,7 @@ sshd_selinux_getctxbyname(char *pwname, int r = 0; context_t con = NULL; @@ -124,7 +124,7 @@ diff -up openssh/openbsd-compat/port-linux-sshd.c.refactor openssh/openbsd-compa #ifdef HAVE_GETSEUSERBYNAME if ((r=getseuserbyname(pwname, &sename, &lvl)) != 0) { -@@ -271,7 +267,7 @@ sshd_selinux_getctxbyname(char *pwname, +@@ -272,7 +268,7 @@ sshd_selinux_getctxbyname(char *pwname, if (r == 0) { /* If launched from xinetd, we must use current level */ @@ -133,7 +133,7 @@ diff -up openssh/openbsd-compat/port-linux-sshd.c.refactor openssh/openbsd-compa security_context_t sshdsc=NULL; if (getcon_raw(&sshdsc) < 0) -@@ -332,7 +328,8 @@ sshd_selinux_getctxbyname(char *pwname, +@@ -333,7 +329,8 @@ sshd_selinux_getctxbyname(char *pwname, /* Setup environment variables for pam_selinux */ static int @@ -143,7 +143,7 @@ diff -up openssh/openbsd-compat/port-linux-sshd.c.refactor openssh/openbsd-compa { const char *reqlvl; char *role; -@@ -341,11 +338,11 @@ sshd_selinux_setup_variables(int(*set_it +@@ -342,11 +339,11 @@ sshd_selinux_setup_variables(int(*set_it debug3("%s: setting execution context", __func__); @@ -157,7 +157,7 @@ diff -up openssh/openbsd-compat/port-linux-sshd.c.refactor openssh/openbsd-compa use_current = "1"; } else { use_current = ""; -@@ -361,9 +358,10 @@ sshd_selinux_setup_variables(int(*set_it +@@ -362,9 +359,10 @@ sshd_selinux_setup_variables(int(*set_it } static int @@ -170,7 +170,7 @@ diff -up openssh/openbsd-compat/port-linux-sshd.c.refactor openssh/openbsd-compa } static int -@@ -373,25 +371,28 @@ do_setenv(char *name, const char *value) +@@ -374,25 +372,28 @@ do_setenv(char *name, const char *value) } int @@ -204,7 +204,7 @@ diff -up openssh/openbsd-compat/port-linux-sshd.c.refactor openssh/openbsd-compa switch (security_getenforce()) { case -1: fatal("%s: security_getenforce() failed", __func__); -@@ -409,7 +410,7 @@ sshd_selinux_setup_exec_context(char *pw +@@ -410,7 +411,7 @@ sshd_selinux_setup_exec_context(char *pw debug3("%s: setting execution context", __func__); @@ -214,9 +214,9 @@ diff -up openssh/openbsd-compat/port-linux-sshd.c.refactor openssh/openbsd-compa r = setexeccon(user_ctx); if (r < 0) { diff -up openssh/platform.c.refactor openssh/platform.c ---- openssh/platform.c.refactor 2017-09-27 13:10:19.574830708 +0200 -+++ openssh/platform.c 2017-09-27 13:11:45.475303050 +0200 -@@ -33,6 +33,9 @@ +--- openssh/platform.c.refactor 2019-04-04 13:19:12.204821389 +0200 ++++ openssh/platform.c 2019-04-04 13:19:12.277822088 +0200 +@@ -32,6 +32,9 @@ extern int use_privsep; extern ServerOptions options; @@ -226,7 +226,7 @@ diff -up openssh/platform.c.refactor openssh/platform.c void platform_pre_listen(void) -@@ -184,7 +187,9 @@ platform_setusercontext_post_groups(stru +@@ -183,7 +186,9 @@ platform_setusercontext_post_groups(stru } #endif /* HAVE_SETPCRED */ #ifdef WITH_SELINUX @@ -238,9 +238,27 @@ diff -up openssh/platform.c.refactor openssh/platform.c } diff -up openssh/sshd.c.refactor openssh/sshd.c ---- openssh/sshd.c.refactor 2017-09-27 13:10:19.674831257 +0200 -+++ openssh/sshd.c 2017-09-27 13:12:01.635391909 +0200 -@@ -2135,7 +2135,9 @@ main(int ac, char **av) +--- openssh/sshd.c.refactor 2019-04-04 13:19:12.275822068 +0200 ++++ openssh/sshd.c 2019-04-04 13:19:51.270195262 +0200 +@@ -158,7 +158,7 @@ int debug_flag = 0; + static int test_flag = 0; + + /* Flag indicating that the daemon is being started from inetd. */ +-static int inetd_flag = 0; ++int inetd_flag = 0; + + /* Flag indicating that sshd should not detach and become a daemon. */ + static int no_daemon_flag = 0; +@@ -171,7 +171,7 @@ static char **saved_argv; + static int saved_argc; + + /* re-exec */ +-static int rexeced_flag = 0; ++int rexeced_flag = 0; + static int rexec_flag = 1; + static int rexec_argc = 0; + static char **rexec_argv; +@@ -2192,7 +2192,9 @@ main(int ac, char **av) } #endif #ifdef WITH_SELINUX diff --git a/openssh-7.6p1-pkcs11-ecdsa.patch b/openssh-7.6p1-pkcs11-ecdsa.patch deleted file mode 100644 index 2e73d45..0000000 --- a/openssh-7.6p1-pkcs11-ecdsa.patch +++ /dev/null @@ -1,801 +0,0 @@ -diff -up openssh-7.6p1/ssh-pkcs11-client.c.pkcs11-ecdsa openssh-7.6p1/ssh-pkcs11-client.c ---- openssh-7.6p1/ssh-pkcs11-client.c.pkcs11-ecdsa 2018-02-16 13:25:59.426469253 +0100 -+++ openssh-7.6p1/ssh-pkcs11-client.c 2018-02-16 13:25:59.428469265 +0100 -@@ -31,6 +31,15 @@ - #include - - #include -+#ifdef OPENSSL_HAS_ECC -+#include -+#if ((defined(LIBRESSL_VERSION_NUMBER) && \ -+ (LIBRESSL_VERSION_NUMBER >= 0x20010002L))) || \ -+ (defined(ECDSA_F_ECDSA_METHOD_NEW)) || \ -+ (OPENSSL_VERSION_NUMBER >= 0x00010100L) -+#define ENABLE_PKCS11_ECDSA 1 -+#endif -+#endif - - #include "pathnames.h" - #include "xmalloc.h" -@@ -139,9 +147,9 @@ pkcs11_rsa_private_encrypt(int flen, con - return (ret); - } - --/* redirect the private key encrypt operation to the ssh-pkcs11-helper */ -+/* redirect the RSA private key encrypt operation to the ssh-pkcs11-helper */ - static int --wrap_key(RSA *rsa) -+wrap_rsa_key(RSA *rsa) - { - static RSA_METHOD helper_rsa; - -@@ -152,6 +160,88 @@ wrap_key(RSA *rsa) - return (0); - } - -+#ifdef ENABLE_PKCS11_ECDSA -+static ECDSA_SIG * -+pkcs11_ecdsa_private_sign(const unsigned char *from, int flen, -+ const BIGNUM *inv, const BIGNUM *rp, EC_KEY * ecdsa) -+{ -+ struct sshkey *key = NULL; -+ u_char *blob, *signature = NULL; -+ size_t blen, slen = 0; -+ struct sshbuf *msg = NULL; -+ ECDSA_SIG *ret = NULL; -+ BIGNUM *r = NULL, *s = NULL; -+ int rv; -+ -+ if ((key = sshkey_new(KEY_ECDSA)) == NULL) -+ fatal("%s: sshkey_new failed", __func__); -+ key->ecdsa = ecdsa; -+ key->ecdsa_nid = sshkey_ecdsa_key_to_nid(ecdsa); -+ if (sshkey_to_blob(key, &blob, &blen) == 0) -+ goto out; -+ if ((msg = sshbuf_new()) == NULL) -+ fatal("%s: sshbuf_new failed", __func__); -+ if ((rv = sshbuf_put_u8(msg, SSH2_AGENTC_SIGN_REQUEST)) != 0 || -+ (rv = sshbuf_put_string(msg, blob, blen)) != 0 || -+ (rv = sshbuf_put_string(msg, from, flen)) != 0 || -+ (rv = sshbuf_put_u32(msg, 0)) != 0) -+ fatal("%s: buffer error: %s", __func__, ssh_err(rv)); -+ free(blob); -+ send_msg(msg); -+ sshbuf_reset(msg); -+ -+ if (recv_msg(msg) == SSH2_AGENT_SIGN_RESPONSE) { -+ if ((rv = sshbuf_get_string(msg, &signature, &slen)) != 0) -+ fatal("%s: buffer error: %s", __func__, ssh_err(rv)); -+ if (slen <= (size_t)ECDSA_size(ecdsa)) { -+ int nlen = slen / 2; -+ ret = ECDSA_SIG_new(); -+ r = BN_new(); -+ s = BN_new(); -+ BN_bin2bn(&signature[0], nlen, r); -+ BN_bin2bn(&signature[nlen], nlen, s); -+ ECDSA_SIG_set0(ret, r, s); -+ } -+ free(signature); -+ } -+out: -+ sshkey_free(key); -+ sshbuf_free(msg); -+ return (ret); -+} -+ -+/* redirect the ECDSA private key encrypt operation to the ssh-pkcs11-helper */ -+static int -+wrap_ecdsa_key(EC_KEY *ecdsa) { -+#if (OPENSSL_VERSION_NUMBER >= 0x00010100L) -+ static EC_KEY_METHOD *helper_ecdsa = NULL; -+ if (helper_ecdsa == NULL) { -+ const EC_KEY_METHOD *def = EC_KEY_get_default_method(); -+ helper_ecdsa = EC_KEY_METHOD_new(def); -+ EC_KEY_METHOD_set_sign(helper_ecdsa, NULL, NULL, pkcs11_ecdsa_private_sign); -+ } -+ EC_KEY_set_method(ecdsa, helper_ecdsa); -+#else -+ static ECDSA_METHOD *helper_ecdsa = NULL; -+ if(helper_ecdsa == NULL) { -+ const ECDSA_METHOD *def = ECDSA_get_default_method(); -+# ifdef ECDSA_F_ECDSA_METHOD_NEW -+ helper_ecdsa = ECDSA_METHOD_new((ECDSA_METHOD *)def); -+ ECDSA_METHOD_set_name(helper_ecdsa, "ssh-pkcs11-helper-ecdsa"); -+ ECDSA_METHOD_set_sign(helper_ecdsa, pkcs11_ecdsa_private_sign); -+# else -+ helper_ecdsa = xcalloc(1, sizeof(*helper_ecdsa)); -+ memcpy(helper_ecdsa, def, sizeof(*helper_ecdsa)); -+ helper_ecdsa->name = "ssh-pkcs11-helper-ecdsa"; -+ helper_ecdsa->ecdsa_do_sign = pkcs11_ecdsa_private_sign; -+# endif -+ } -+ ECDSA_set_method(ecdsa, helper_ecdsa); -+#endif -+ return (0); -+} -+#endif -+ - static int - pkcs11_start_helper(void) - { -@@ -212,7 +281,15 @@ pkcs11_add_provider(char *name, char *pi - __func__, ssh_err(r)); - if ((r = sshkey_from_blob(blob, blen, &k)) != 0) - fatal("%s: bad key: %s", __func__, ssh_err(r)); -- wrap_key(k->rsa); -+ if(k->type == KEY_RSA) { -+ wrap_rsa_key(k->rsa); -+#ifdef ENABLE_PKCS11_ECDSA -+ } else if(k->type == KEY_ECDSA) { -+ wrap_ecdsa_key(k->ecdsa); -+#endif /* ENABLE_PKCS11_ECDSA */ -+ } else { -+ /* Unsupported type */ -+ } - (*keysp)[i] = k; - free(blob); - } -diff -up openssh-7.6p1/ssh-pkcs11.c.pkcs11-ecdsa openssh-7.6p1/ssh-pkcs11.c ---- openssh-7.6p1/ssh-pkcs11.c.pkcs11-ecdsa 2018-02-16 13:25:59.427469259 +0100 -+++ openssh-7.6p1/ssh-pkcs11.c 2018-02-16 13:44:51.270554797 +0100 -@@ -32,6 +32,16 @@ - #include "openbsd-compat/sys-queue.h" - - #include -+#include -+#ifdef OPENSSL_HAS_ECC -+#include -+#if ((defined(LIBRESSL_VERSION_NUMBER) && \ -+ (LIBRESSL_VERSION_NUMBER >= 0x20010002L))) || \ -+ (defined(ECDSA_F_ECDSA_METHOD_NEW)) || \ -+ (OPENSSL_VERSION_NUMBER >= 0x00010100L) -+#define ENABLE_PKCS11_ECDSA 1 -+#endif -+#endif - - #define CRYPTOKI_COMPAT - #include "pkcs11.h" -@@ -67,6 +76,7 @@ TAILQ_HEAD(, pkcs11_provider) pkcs11_pro - struct pkcs11_key { - struct pkcs11_provider *provider; - CK_ULONG slotidx; -+ CK_ULONG key_type; - int (*orig_finish)(RSA *rsa); - RSA_METHOD rsa_method; - char *keyid; -@@ -75,6 +85,9 @@ struct pkcs11_key { - }; - - int pkcs11_interactive = 0; -+#ifdef ENABLE_PKCS11_ECDSA -+static int pkcs11_key_idx = -1; -+#endif /* ENABLE_PKCS11_ECDSA */ - - /* - * This can't be in the ssh-pkcs11-uri, becase we can not depend on -@@ -289,6 +302,40 @@ pkcs11_find(struct pkcs11_provider *p, C - return (ret); - } - -+int pkcs11_login(struct pkcs11_key *k11, CK_FUNCTION_LIST *f, struct pkcs11_slotinfo *si) { -+ char *pin = NULL, prompt[1024]; -+ CK_RV rv; -+ if ((si->token.flags & CKF_LOGIN_REQUIRED) && !si->logged_in) { -+ if (!pkcs11_interactive) { -+ error("need pin entry%s", (si->token.flags & -+ CKF_PROTECTED_AUTHENTICATION_PATH) ? -+ " on reader keypad" : ""); -+ return (-1); -+ } -+ if (si->token.flags & CKF_PROTECTED_AUTHENTICATION_PATH) -+ verbose("Deferring PIN entry to reader keypad."); -+ else { -+ snprintf(prompt, sizeof(prompt), -+ "Enter PIN for '%s': ", si->token.label); -+ pin = read_passphrase(prompt, RP_ALLOW_EOF); -+ if (pin == NULL) -+ return (-1); /* bail out */ -+ } -+ rv = f->C_Login(si->session, CKU_USER, (u_char *)pin, -+ (pin != NULL) ? strlen(pin) : 0); -+ if (pin != NULL) { -+ explicit_bzero(pin, strlen(pin)); -+ free(pin); -+ } -+ if (rv != CKR_OK && rv != CKR_USER_ALREADY_LOGGED_IN) { -+ error("C_Login failed: %lu", rv); -+ return (-1); -+ } -+ si->logged_in = 1; -+ } -+ return 0; -+} -+ - /* openssl callback doing the actual signing operation */ - static int - pkcs11_rsa_private_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa, -@@ -310,7 +357,6 @@ pkcs11_rsa_private_encrypt(int flen, con - {CKA_ID, NULL, 0}, - {CKA_SIGN, NULL, sizeof(true_val) } - }; -- char *pin = NULL, prompt[1024]; - int rval = -1; - - key_filter[0].pValue = &private_key_class; -@@ -326,33 +372,8 @@ pkcs11_rsa_private_encrypt(int flen, con - } - f = k11->provider->module->function_list; - si = &k11->provider->module->slotinfo[k11->slotidx]; -- if ((si->token.flags & CKF_LOGIN_REQUIRED) && !si->logged_in) { -- if (!pkcs11_interactive) { -- error("need pin entry%s", (si->token.flags & -- CKF_PROTECTED_AUTHENTICATION_PATH) ? -- " on reader keypad" : ""); -- return (-1); -- } -- if (si->token.flags & CKF_PROTECTED_AUTHENTICATION_PATH) -- verbose("Deferring PIN entry to reader keypad."); -- else { -- snprintf(prompt, sizeof(prompt), -- "Enter PIN for '%s': ", si->token.label); -- pin = read_passphrase(prompt, RP_ALLOW_EOF); -- if (pin == NULL) -- return (-1); /* bail out */ -- } -- rv = f->C_Login(si->session, CKU_USER, (u_char *)pin, -- (pin != NULL) ? strlen(pin) : 0); -- if (pin != NULL) { -- explicit_bzero(pin, strlen(pin)); -- free(pin); -- } -- if (rv != CKR_OK && rv != CKR_USER_ALREADY_LOGGED_IN) { -- error("C_Login failed: %lu", rv); -- return (-1); -- } -- si->logged_in = 1; -+ if(pkcs11_login(k11, f, si)) { -+ return (-1); - } - key_filter[1].pValue = k11->keyid; - key_filter[1].ulValueLen = k11->keyid_len; -@@ -390,6 +411,7 @@ pkcs11_rsa_wrap(struct pkcs11_provider * - const RSA_METHOD *def = RSA_get_default_method(); - - k11 = xcalloc(1, sizeof(*k11)); -+ k11->key_type = CKK_RSA; - k11->provider = provider; - provider->refcount++; /* provider referenced by RSA key */ - k11->slotidx = slotidx; -@@ -415,6 +437,184 @@ pkcs11_rsa_wrap(struct pkcs11_provider * - return (0); - } - -+#ifdef ENABLE_PKCS11_ECDSA -+static ECDSA_SIG *pkcs11_ecdsa_sign(const unsigned char *dgst, int dgst_len, -+ const BIGNUM *inv, const BIGNUM *rp, -+ EC_KEY *ecdsa) { -+ struct pkcs11_key *k11; -+ struct pkcs11_slotinfo *si; -+ CK_FUNCTION_LIST *f; -+ CK_OBJECT_HANDLE obj; -+ CK_ULONG tlen = 0; -+ CK_RV rv; -+ CK_OBJECT_CLASS private_key_class = CKO_PRIVATE_KEY; -+ CK_BBOOL true_val = CK_TRUE; -+ CK_MECHANISM mech = { -+ CKM_ECDSA, NULL_PTR, 0 -+ }; -+ CK_ATTRIBUTE key_filter[] = { -+ {CKA_CLASS, NULL, sizeof(private_key_class) }, -+ {CKA_ID, NULL, 0}, -+ {CKA_SIGN, NULL, sizeof(true_val) } -+ }; -+ ECDSA_SIG *rval = NULL; -+ key_filter[0].pValue = &private_key_class; -+ key_filter[2].pValue = &true_val; -+ -+ #if (OPENSSL_VERSION_NUMBER >= 0x00010100L) -+ if ((k11 = (struct pkcs11_key *)EC_KEY_get_ex_data(ecdsa, pkcs11_key_idx)) == NULL) { -+ error("EC_KEY_get_ex_data failed for ecdsa %p", ecdsa); -+ #else -+ if ((k11 = (struct pkcs11_key *)ECDSA_get_ex_data(ecdsa, pkcs11_key_idx)) == NULL) { -+ error("ECDSA_get_ex_data failed for ecdsa %p", ecdsa); -+ #endif -+ return NULL; -+ } -+ if (!k11->provider || !k11->provider->valid) { -+ error("no pkcs11 (valid) provider for ecdsa %p", ecdsa); -+ return NULL; -+ } -+ f = k11->provider->module->function_list; -+ si = &k11->provider->module->slotinfo[k11->slotidx]; -+ if(pkcs11_login(k11, f, si)) { -+ return NULL; -+ } -+ key_filter[1].pValue = k11->keyid; -+ key_filter[1].ulValueLen = k11->keyid_len; -+ /* try to find object w/CKA_SIGN first, retry w/o */ -+ if (pkcs11_find(k11->provider, k11->slotidx, key_filter, 3, &obj) < 0 && -+ pkcs11_find(k11->provider, k11->slotidx, key_filter, 2, &obj) < 0) { -+ error("cannot find private key"); -+ } else if ((rv = f->C_SignInit(si->session, &mech, obj)) != CKR_OK) { -+ error("C_SignInit failed: %lu", rv); -+ } else { -+ CK_BYTE_PTR buf = NULL; -+ BIGNUM *r = NULL, *s = NULL; -+ int nlen; -+ /* Make a call to C_Sign to find out the size of the signature */ -+ rv = f->C_Sign(si->session, (CK_BYTE *)dgst, dgst_len, NULL, &tlen); -+ if (rv != CKR_OK) { -+ error("C_Sign failed: %lu", rv); -+ return NULL; -+ } -+ if ((buf = xmalloc(tlen)) == NULL) { -+ error("failure to allocate signature buffer"); -+ return NULL; -+ } -+ rv = f->C_Sign(si->session, (CK_BYTE *)dgst, dgst_len, buf, &tlen); -+ if (rv != CKR_OK) { -+ error("C_Sign failed: %lu", rv); -+ } -+ -+ if ((rval = ECDSA_SIG_new()) == NULL || -+ (r = BN_new()) == NULL || -+ (s = BN_new()) == NULL) { -+ error("failure to allocate ECDSA signature"); -+ } else { -+ /* -+ * ECDSA signature is 2 large integers of same size returned -+ * concatenated by PKCS#11, we separate them to create an -+ * ECDSA_SIG for OpenSSL. -+ */ -+ nlen = tlen / 2; -+ BN_bin2bn(&buf[0], nlen, r); -+ BN_bin2bn(&buf[nlen], nlen, s); -+ ECDSA_SIG_set0(rval, r, s); -+ } -+ free(buf); -+ } -+ return (rval); -+} -+ -+#if (OPENSSL_VERSION_NUMBER >= 0x00010100L) -+static EC_KEY_METHOD *get_pkcs11_ecdsa_method(void) { -+ static EC_KEY_METHOD *pkcs11_ecdsa_method = NULL; -+ if(pkcs11_key_idx == -1) { -+ pkcs11_key_idx = EC_KEY_get_ex_new_index(0, NULL, NULL, NULL, 0); -+ } -+ if (pkcs11_ecdsa_method == NULL) { -+ const EC_KEY_METHOD *def = EC_KEY_get_default_method(); -+ pkcs11_ecdsa_method = EC_KEY_METHOD_new(def); -+ EC_KEY_METHOD_set_sign(pkcs11_ecdsa_method, NULL, NULL, pkcs11_ecdsa_sign); -+ } -+#else -+static ECDSA_METHOD *get_pkcs11_ecdsa_method(void) { -+ static ECDSA_METHOD *pkcs11_ecdsa_method = NULL; -+ if(pkcs11_key_idx == -1) { -+ pkcs11_key_idx = ECDSA_get_ex_new_index(0, NULL, NULL, NULL, 0); -+ } -+ if(pkcs11_ecdsa_method == NULL) { -+ const ECDSA_METHOD *def = ECDSA_get_default_method(); -+ #ifdef ECDSA_F_ECDSA_METHOD_NEW -+ pkcs11_ecdsa_method = ECDSA_METHOD_new((ECDSA_METHOD *)def); -+ ECDSA_METHOD_set_name(pkcs11_ecdsa_method, "pkcs11"); -+ ECDSA_METHOD_set_sign(pkcs11_ecdsa_method, pkcs11_ecdsa_sign); -+ #else -+ pkcs11_ecdsa_method = xcalloc(1, sizeof(*pkcs11_ecdsa_method)); -+ memcpy(pkcs11_ecdsa_method, def, sizeof(*pkcs11_ecdsa_method)); -+ pkcs11_ecdsa_method->name = "pkcs11"; -+ pkcs11_ecdsa_method->ecdsa_do_sign = pkcs11_ecdsa_sign; -+ #endif -+ } -+#endif -+ return pkcs11_ecdsa_method; -+} -+ -+static int -+pkcs11_ecdsa_wrap(struct pkcs11_provider *provider, CK_ULONG slotidx, -+ CK_ATTRIBUTE *keyid_attrib, CK_ATTRIBUTE *label_attrib, EC_KEY *ecdsa) -+{ -+ struct pkcs11_key *k11; -+ k11 = xcalloc(1, sizeof(*k11)); -+ k11->key_type = CKK_EC; -+ k11->provider = provider; -+ provider->refcount++; /* provider referenced by ECDSA key */ -+ k11->slotidx = slotidx; -+ /* identify key object on smartcard */ -+ k11->keyid_len = keyid_attrib->ulValueLen; -+ if (k11->keyid_len > 0) { -+ k11->keyid = xmalloc(k11->keyid_len); -+ memcpy(k11->keyid, keyid_attrib->pValue, k11->keyid_len); -+ } -+ if (label_attrib->ulValueLen > 0 ) { -+ k11->label = xmalloc(label_attrib->ulValueLen+1); -+ memcpy(k11->label, label_attrib->pValue, label_attrib->ulValueLen); -+ k11->label[label_attrib->ulValueLen] = 0; -+ } -+ #if (OPENSSL_VERSION_NUMBER >= 0x00010100L) -+ EC_KEY_set_method(ecdsa, get_pkcs11_ecdsa_method()); -+ EC_KEY_set_ex_data(ecdsa, pkcs11_key_idx, k11); -+ #else -+ ECDSA_set_method(ecdsa, get_pkcs11_ecdsa_method()); -+ ECDSA_set_ex_data(ecdsa, pkcs11_key_idx, k11); -+ #endif -+ return (0); -+} -+#endif /* ENABLE_PKCS11_ECDSA */ -+ -+int pkcs11_del_key(struct sshkey *key) { -+#ifdef ENABLE_PKCS11_ECDSA -+ if(key->type == KEY_ECDSA) { -+ struct pkcs11_key *k11 = (struct pkcs11_key *) -+ #if (OPENSSL_VERSION_NUMBER >= 0x00010100L) -+ EC_KEY_get_ex_data(key->ecdsa, pkcs11_key_idx); -+ #else -+ ECDSA_get_ex_data(key->ecdsa, pkcs11_key_idx); -+ #endif -+ if (k11 == NULL) { -+ error("EC_KEY_get_ex_data failed for ecdsa %p", key->ecdsa); -+ } else { -+ if (k11->provider) -+ pkcs11_provider_unref(k11->provider); -+ free(k11->keyid); -+ free(k11); -+ } -+ } -+#endif /* ENABLE_PKCS11_ECDSA */ -+ sshkey_free(key); -+ return (0); -+} -+ - /* remove trailing spaces */ - static void - rmspace(u_char *buf, size_t len) -@@ -482,11 +646,13 @@ static int - pkcs11_fetch_keys(struct pkcs11_provider *p, CK_ULONG slotidx, - struct sshkey ***keysp, int *nkeys, struct pkcs11_uri *uri) - { -- size_t filter_size = 1; -+ size_t filter_size = 2; -+ CK_KEY_TYPE pubkey_type = CKK_RSA; - CK_OBJECT_CLASS pubkey_class = CKO_PUBLIC_KEY; - CK_OBJECT_CLASS cert_class = CKO_CERTIFICATE; - CK_ATTRIBUTE pubkey_filter[] = { - { CKA_CLASS, NULL, sizeof(pubkey_class) }, -+ { CKA_KEY_TYPE, NULL, sizeof(pubkey_type) }, - { CKA_ID, NULL, 0 }, - { CKA_LABEL, NULL, 0 } - }; -@@ -507,29 +673,60 @@ pkcs11_fetch_keys(struct pkcs11_provider - { CKA_SUBJECT, NULL, 0 }, - { CKA_VALUE, NULL, 0 } - }; -+#ifdef ENABLE_PKCS11_ECDSA -+ CK_KEY_TYPE ecdsa_type = CKK_EC; -+ CK_ATTRIBUTE ecdsa_filter[] = { -+ { CKA_CLASS, NULL, sizeof(pubkey_class) }, -+ { CKA_KEY_TYPE, NULL, sizeof(ecdsa_type) }, -+ { CKA_ID, NULL, 0 }, -+ { CKA_LABEL, NULL, 0 } -+ }; -+ CK_ATTRIBUTE ecdsa_attribs[] = { -+ { CKA_ID, NULL, 0 }, -+ { CKA_LABEL, NULL, 0 }, -+ { CKA_EC_PARAMS, NULL, 0 }, -+ { CKA_EC_POINT, NULL, 0 } -+ }; -+ ecdsa_filter[0].pValue = &pubkey_class; -+ ecdsa_filter[1].pValue = &ecdsa_type; -+#endif /* ENABLE_PKCS11_ECDSA */ - pubkey_filter[0].pValue = &pubkey_class; -+ pubkey_filter[1].pValue = &pubkey_type; - cert_filter[0].pValue = &cert_class; - - if (uri->id != NULL) { - pubkey_filter[filter_size].pValue = uri->id; - pubkey_filter[filter_size].ulValueLen = uri->id_len; -- cert_filter[filter_size].pValue = uri->id; -- cert_filter[filter_size].ulValueLen = uri->id_len; -+#ifdef ENABLE_PKCS11_ECDSA -+ ecdsa_filter[filter_size].pValue = uri->id; -+ ecdsa_filter[filter_size].ulValueLen = uri->id_len; -+#endif /* ENABLE_PKCS11_ECDSA */ -+ cert_filter[filter_size-1].pValue = uri->id; -+ cert_filter[filter_size-1].ulValueLen = uri->id_len; - filter_size++; - } - if (uri->object != NULL) { - pubkey_filter[filter_size].pValue = uri->object; - pubkey_filter[filter_size].ulValueLen = strlen(uri->object); - pubkey_filter[filter_size].type = CKA_LABEL; -- cert_filter[filter_size].pValue = uri->object; -- cert_filter[filter_size].ulValueLen = strlen(uri->object); -- cert_filter[filter_size].type = CKA_LABEL; -+#ifdef ENABLE_PKCS11_ECDSA -+ ecdsa_filter[filter_size].pValue = uri->object; -+ ecdsa_filter[filter_size].ulValueLen = strlen(uri->object); -+ ecdsa_filter[filter_size].type = CKA_LABEL; -+#endif /* ENABLE_PKCS11_ECDSA */ -+ cert_filter[filter_size-1].pValue = uri->object; -+ cert_filter[filter_size-1].ulValueLen = strlen(uri->object); -+ cert_filter[filter_size-1].type = CKA_LABEL; - filter_size++; - } - - if (pkcs11_fetch_keys_filter(p, slotidx, pubkey_filter, filter_size, - pubkey_attribs, keysp, nkeys) < 0 || -- pkcs11_fetch_keys_filter(p, slotidx, cert_filter, filter_size, -+#ifdef ENABLE_PKCS11_ECDSA -+ pkcs11_fetch_keys_filter(p, slotidx, ecdsa_filter, filter_size, -+ ecdsa_attribs, keysp, nkeys) < 0|| -+#endif /* ENABLE_PKCS11_ECDSA */ -+ pkcs11_fetch_keys_filter(p, slotidx, cert_filter, filter_size - 1, - cert_attribs, keysp, nkeys) < 0) - return (-1); - return (0); -@@ -553,6 +746,11 @@ pkcs11_fetch_keys_filter(struct pkcs11_p - { - struct sshkey *key; - RSA *rsa; -+#ifdef ENABLE_PKCS11_ECDSA -+ EC_KEY *ecdsa; -+#else -+ void *ecdsa; -+#endif /* ENABLE_PKCS11_ECDSA */ - X509 *x509; - EVP_PKEY *evp = NULL; - int i; -@@ -608,6 +806,9 @@ pkcs11_fetch_keys_filter(struct pkcs11_p - * or ID, label, subject and value for certificates. - */ - rsa = NULL; -+#ifdef ENABLE_PKCS11_ECDSA -+ ecdsa = NULL; -+#endif /* ENABLE_PKCS11_ECDSA */ - if ((rv = f->C_GetAttributeValue(session, obj, attribs, nattribs)) - != CKR_OK) { - error("C_GetAttributeValue failed: %lu", rv); -@@ -620,6 +821,45 @@ pkcs11_fetch_keys_filter(struct pkcs11_p - rsa->e = BN_bin2bn(attribs[3].pValue, - attribs[3].ulValueLen, NULL); - } -+#ifdef ENABLE_PKCS11_ECDSA -+ } else if (attribs[2].type == CKA_EC_PARAMS ) { -+ if ((ecdsa = EC_KEY_new()) == NULL) { -+ error("EC_KEY_new failed"); -+ } else { -+ const unsigned char *ptr1 = attribs[2].pValue; -+ const unsigned char *ptr2 = attribs[3].pValue; -+ CK_ULONG len1 = attribs[2].ulValueLen; -+ CK_ULONG len2 = attribs[3].ulValueLen; -+ ASN1_OCTET_STRING *point = NULL; -+ -+ /* -+ * CKA_EC_PARAMS contains the curve parameters of the key -+ * either referenced as an OID or directly with all values. -+ * CKA_EC_POINT contains the point (public key) on the curve. -+ * The point is should be returned inside a DER-encoded -+ * ASN.1 OCTET STRING value (but some implementation). -+ */ -+ if ((point = d2i_ASN1_OCTET_STRING(NULL, &ptr2, len2))) { -+ /* Pointing to OCTET STRING content */ -+ ptr2 = point->data; -+ len2 = point->length; -+ } else { -+ /* No OCTET STRING */ -+ ptr2 = attribs[3].pValue; -+ } -+ -+ if((d2i_ECParameters(&ecdsa, &ptr1, len1) == NULL) || -+ (o2i_ECPublicKey(&ecdsa, &ptr2, len2) == NULL)) { -+ EC_KEY_free(ecdsa); -+ ecdsa = NULL; -+ error("EC public key parsing failed"); -+ } -+ -+ if(point) { -+ ASN1_OCTET_STRING_free(point); -+ } -+ } -+#endif /* ENABLE_PKCS11_ECDSA */ - } else { - cp = attribs[3].pValue; - if ((x509 = X509_new()) == NULL) { -@@ -639,13 +879,28 @@ pkcs11_fetch_keys_filter(struct pkcs11_p - X509_free(x509); - EVP_PKEY_free(evp); - } -- if (rsa && rsa->n && rsa->e && -- pkcs11_rsa_wrap(p, slotidx, &attribs[0], &attribs[1], rsa) == 0) { -- if ((key = sshkey_new(KEY_UNSPEC)) == NULL) -- fatal("sshkey_new failed"); -- key->rsa = rsa; -- key->type = KEY_RSA; -- key->flags |= SSHKEY_FLAG_EXT; -+ key = NULL; -+ if (rsa || ecdsa) { -+ if (rsa && rsa->n && rsa->e && -+ pkcs11_rsa_wrap(p, slotidx, &attribs[0], &attribs[1], rsa) == 0) { -+ if ((key = sshkey_new(KEY_UNSPEC)) == NULL) -+ fatal("sshkey_new failed"); -+ key->rsa = rsa; -+ key->type = KEY_RSA; -+ key->flags |= SSHKEY_FLAG_EXT; -+#ifdef ENABLE_PKCS11_ECDSA -+ } else if(ecdsa && pkcs11_ecdsa_wrap(p, slotidx, &attribs[0], &attribs[1], ecdsa) == 0) { -+ if ((key = sshkey_new(KEY_UNSPEC)) == NULL) -+ fatal("sshkey_new failed"); -+ key->ecdsa = ecdsa; -+ key->ecdsa_nid = sshkey_ecdsa_key_to_nid(ecdsa); -+ key->type = KEY_ECDSA; -+ key->flags |= SSHKEY_FLAG_EXT; -+#endif /* ENABLE_PKCS11_ECDSA */ -+ } -+ } -+ -+ if(key) { - if (pkcs11_key_included(keysp, nkeys, key)) { - sshkey_free(key); - } else { -@@ -658,6 +913,10 @@ pkcs11_fetch_keys_filter(struct pkcs11_p - } - } else if (rsa) { - RSA_free(rsa); -+#ifdef ENABLE_PKCS11_ECDSA -+ } else if (ecdsa) { -+ EC_KEY_free(ecdsa); -+#endif /* ENABLE_PKCS11_ECDSA */ - } - for (i = 0; i < nattribs; i++) - free(attribs[i].pValue); -diff -up openssh-7.6p1/ssh-pkcs11-helper.c.pkcs11-ecdsa openssh-7.6p1/ssh-pkcs11-helper.c ---- openssh-7.6p1/ssh-pkcs11-helper.c.pkcs11-ecdsa 2017-10-02 21:34:26.000000000 +0200 -+++ openssh-7.6p1/ssh-pkcs11-helper.c 2018-02-16 13:25:59.428469265 +0100 -@@ -24,6 +24,17 @@ - - #include "openbsd-compat/sys-queue.h" - -+#include -+#ifdef OPENSSL_HAS_ECC -+#include -+#if ((defined(LIBRESSL_VERSION_NUMBER) && \ -+ (LIBRESSL_VERSION_NUMBER >= 0x20010002L))) || \ -+ (defined(ECDSA_F_ECDSA_METHOD_NEW)) || \ -+ (OPENSSL_VERSION_NUMBER >= 0x00010100L) -+#define ENABLE_PKCS11_ECDSA 1 -+#endif -+#endif -+ - #include - #include - #include -@@ -80,7 +90,7 @@ del_keys_by_name(char *name) - if (!strcmp(ki->providername, name)) { - TAILQ_REMOVE(&pkcs11_keylist, ki, next); - free(ki->providername); -- sshkey_free(ki->key); -+ pkcs11_del_key(ki->key); - free(ki); - } - } -@@ -164,6 +174,20 @@ process_del(void) - sshbuf_free(msg); - } - -+#ifdef ENABLE_PKCS11_ECDSA -+static u_int EC_KEY_order_size(EC_KEY *key) -+{ -+ const EC_GROUP *group = EC_KEY_get0_group(key); -+ BIGNUM *order = BN_new(); -+ u_int nbytes = 0; -+ if ((group != NULL) && (order != NULL) && EC_GROUP_get_order(group, order, NULL)) { -+ nbytes = BN_num_bytes(order); -+ } -+ BN_clear_free(order); -+ return nbytes; -+} -+#endif /* ENABLE_PKCS11_ECDSA */ -+ - static void - process_sign(void) - { -@@ -180,14 +204,38 @@ process_sign(void) - else { - if ((found = lookup_key(key)) != NULL) { - #ifdef WITH_OPENSSL -- int ret; -- -- slen = RSA_size(key->rsa); -- signature = xmalloc(slen); -- if ((ret = RSA_private_encrypt(dlen, data, signature, -- found->rsa, RSA_PKCS1_PADDING)) != -1) { -- slen = ret; -- ok = 0; -+ if(found->type == KEY_RSA) { -+ int ret; -+ slen = RSA_size(key->rsa); -+ signature = xmalloc(slen); -+ if ((ret = RSA_private_encrypt(dlen, data, signature, -+ found->rsa, RSA_PKCS1_PADDING)) != -1) { -+ slen = ret; -+ ok = 0; -+ } -+#ifdef ENABLE_PKCS11_ECDSA -+ } else if(found->type == KEY_ECDSA) { -+ ECDSA_SIG *sig; -+ const BIGNUM *r = NULL, *s = NULL; -+ if ((sig = ECDSA_do_sign(data, dlen, found->ecdsa)) != NULL) { -+ /* PKCS11 2.3.1 recommends both r and s to have the order size for -+ backward compatiblity */ -+ ECDSA_SIG_get0(sig, &r, &s); -+ u_int o_len = EC_KEY_order_size(found->ecdsa); -+ u_int r_len = BN_num_bytes(r); -+ u_int s_len = BN_num_bytes(s); -+ if (o_len > 0 && r_len <= o_len && s_len <= o_len) { -+ signature = xcalloc(2, o_len); -+ BN_bn2bin(r, signature + o_len - r_len); -+ BN_bn2bin(s, signature + (2 * o_len) - s_len); -+ slen = 2 * o_len; -+ ok = 0; -+ } -+ ECDSA_SIG_free(sig); -+ } -+#endif /* ENABLE_PKCS11_ECDSA */ -+ } else { -+ /* Unsupported type */ - } - #endif /* WITH_OPENSSL */ - } -diff -up openssh-7.6p1/ssh-pkcs11.h.pkcs11-ecdsa openssh-7.6p1/ssh-pkcs11.h ---- openssh-7.6p1/ssh-pkcs11.h.pkcs11-ecdsa 2018-02-16 13:25:59.429469272 +0100 -+++ openssh-7.6p1/ssh-pkcs11.h 2018-02-16 13:45:29.623800048 +0100 -@@ -20,6 +20,7 @@ - int pkcs11_init(int); - void pkcs11_terminate(void); - int pkcs11_add_provider(char *, char *, struct sshkey ***); -+int pkcs11_del_key(struct sshkey *); - int pkcs11_add_provider_by_uri(struct pkcs11_uri *, char *, struct sshkey ***); - int pkcs11_del_provider(char *); - int pkcs11_uri_write(const struct sshkey *, FILE *); - - - -diff -up openssh-7.6p1/ssh-pkcs11.c.old openssh-7.6p1/ssh-pkcs11.c ---- openssh-7.6p1/ssh-pkcs11.c.old 2018-02-16 16:43:08.861520255 +0100 -+++ openssh-7.6p1/ssh-pkcs11.c 2018-02-16 16:56:35.312601451 +0100 -@@ -917,13 +917,28 @@ pkcs11_fetch_keys_filter(struct pkcs11_p - } else if (d2i_X509(&x509, &cp, attribs[3].ulValueLen) - == NULL) { - error("d2i_X509 failed"); -- } else if ((evp = X509_get_pubkey(x509)) == NULL || -- evp->type != EVP_PKEY_RSA || -- evp->pkey.rsa == NULL) { -- debug("X509_get_pubkey failed or no rsa"); -- } else if ((rsa = RSAPublicKey_dup(evp->pkey.rsa)) -- == NULL) { -- error("RSAPublicKey_dup"); -+ } else if ((evp = X509_get_pubkey(x509)) == NULL) { -+ debug("X509_get_pubkey failed"); -+ } else { -+ switch (evp->type) { -+ case EVP_PKEY_RSA: -+ if (evp->pkey.rsa == NULL) -+ debug("Missing RSA key"); -+ else if ((rsa = RSAPublicKey_dup( -+ evp->pkey.rsa)) == NULL) -+ error("RSAPublicKey_dup failed"); -+ break; -+ case EVP_PKEY_EC: -+ if (evp->pkey.ecdsa == NULL) -+ debug("Missing ECDSA key"); -+ else if ((ecdsa = EC_KEY_dup( -+ evp->pkey.ecdsa)) == NULL) -+ error("EC_KEY_dup failed"); -+ break; -+ default: -+ debug("not a RSA or ECDSA key"); -+ break; -+ } - } - X509_free(x509); - EVP_PKEY_free(evp); diff --git a/openssh-7.6p1-pkcs11-uri.patch b/openssh-7.6p1-pkcs11-uri.patch deleted file mode 100644 index 6fd463e..0000000 --- a/openssh-7.6p1-pkcs11-uri.patch +++ /dev/null @@ -1,4771 +0,0 @@ -diff --git a/Makefile.in b/Makefile.in -index ac959c1f..f8ed1781 100644 ---- a/Makefile.in -+++ b/Makefile.in -@@ -93,7 +93,7 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \ - monitor_fdpass.o rijndael.o ssh-dss.o ssh-ecdsa.o ssh-rsa.o dh.o \ - kexgssc.o \ - msg.o progressmeter.o dns.o entropy.o gss-genr.o umac.o umac128.o \ -- ssh-pkcs11.o smult_curve25519_ref.o \ -+ ssh-pkcs11.o ssh-pkcs11-uri.o smult_curve25519_ref.o \ - poly1305.o chacha.o cipher-chachapoly.o \ - ssh-ed25519.o digest-openssl.o digest-libc.o hmac.o \ - sc25519.o ge25519.o fe25519.o ed25519.o verify.o hash.o \ -@@ -248,6 +248,8 @@ clean: regressclean - rm -f regress/unittests/match/test_match$(EXEEXT) - rm -f regress/unittests/utf8/*.o - rm -f regress/unittests/utf8/test_utf8$(EXEEXT) -+ rm -f regress/unittests/pkcs11/*.o -+ rm -f regress/unittests/pkcs11/test_pkcs11$(EXEEXT) - rm -f regress/misc/kexfuzz/*.o - rm -f regress/misc/kexfuzz/kexfuzz$(EXEEXT) - (cd openbsd-compat && $(MAKE) clean) -@@ -276,6 +278,8 @@ distclean: regressclean - rm -f regress/unittests/match/test_match - rm -f regress/unittests/utf8/*.o - rm -f regress/unittests/utf8/test_utf8 -+ rm -f regress/unittests/pkcs11/*.o -+ rm -f regress/unittests/pkcs11/test_pkcs11 - rm -f regress/misc/kexfuzz/*.o - rm -f regress/misc/kexfuzz - (cd openbsd-compat && $(MAKE) distclean) -@@ -437,6 +441,7 @@ regress-prep: - $(MKDIR_P) `pwd`/regress/unittests/kex - $(MKDIR_P) `pwd`/regress/unittests/match - $(MKDIR_P) `pwd`/regress/unittests/utf8 -+ $(MKDIR_P) `pwd`/regress/unittests/pkcs11 - $(MKDIR_P) `pwd`/regress/misc/kexfuzz - [ -f `pwd`/regress/Makefile ] || \ - ln -s `cd $(srcdir) && pwd`/regress/Makefile `pwd`/regress/Makefile -@@ -455,6 +460,11 @@ regress/netcat$(EXEEXT): $(srcdir)/regress/netcat.c $(REGRESSLIBS) - $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $(srcdir)/regress/netcat.c \ - $(LDFLAGS) -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) - -+regress/soft-pkcs11.so: $(srcdir)/regress/soft-pkcs11.c $(REGRESSLIBS) -+ $(CC) $(CFLAGS) $(CPPFLAGS) -fpic -c $(srcdir)/regress/soft-pkcs11.c \ -+ -o $(srcdir)/regress/soft-pkcs11.o -+ $(CC) -shared -o $@ $(srcdir)/regress/soft-pkcs11.o -+ - regress/check-perm$(EXEEXT): $(srcdir)/regress/check-perm.c $(REGRESSLIBS) - $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $(srcdir)/regress/check-perm.c \ - $(LDFLAGS) -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) -@@ -556,6 +566,16 @@ regress/unittests/utf8/test_utf8$(EXEEXT): \ - regress/unittests/test_helper/libtest_helper.a \ - -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) - -+UNITTESTS_TEST_PKCS11_OBJS=\ -+ regress/unittests/pkcs11/tests.o -+ -+regress/unittests/pkcs11/test_pkcs11$(EXEEXT): \ -+ ${UNITTESTS_TEST_PKCS11_OBJS} \ -+ regress/unittests/test_helper/libtest_helper.a libssh.a -+ $(LD) -o $@ $(LDFLAGS) $(UNITTESTS_TEST_PKCS11_OBJS) \ -+ regress/unittests/test_helper/libtest_helper.a \ -+ -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) -+ - MISC_KEX_FUZZ_OBJS=\ - regress/misc/kexfuzz/kexfuzz.o - -@@ -566,6 +586,7 @@ regress/misc/kexfuzz/kexfuzz$(EXEEXT): ${MISC_KEX_FUZZ_OBJS} libssh.a - regress-binaries: regress/modpipe$(EXEEXT) \ - regress/setuid-allowed$(EXEEXT) \ - regress/netcat$(EXEEXT) \ -+ regress/soft-pkcs11.so \ - regress/check-perm$(EXEEXT) \ - regress/mkdtemp$(EXEEXT) \ - regress/unittests/sshbuf/test_sshbuf$(EXEEXT) \ -@@ -575,6 +596,7 @@ regress-binaries: regress/modpipe$(EXEEXT) \ - regress/unittests/kex/test_kex$(EXEEXT) \ - regress/unittests/match/test_match$(EXEEXT) \ - regress/unittests/utf8/test_utf8$(EXEEXT) \ -+ regress/unittests/pkcs11/test_pkcs11$(EXEEXT) \ - regress/misc/kexfuzz/kexfuzz$(EXEEXT) - - tests interop-tests t-exec unit: regress-prep regress-binaries $(TARGETS) -diff --git a/authfd.c b/authfd.c -index 1eff7ba9..35153f47 100644 ---- a/authfd.c -+++ b/authfd.c -@@ -312,6 +312,8 @@ ssh_free_identitylist(struct ssh_identitylist *idl) - if (idl->comments != NULL) - free(idl->comments[i]); - } -+ free(idl->keys); -+ free(idl->comments); - free(idl); - } - -diff --git a/configure.ac b/configure.ac -index d7bcaf01..171a8597 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -1895,12 +1895,14 @@ AC_LINK_IFELSE( - [AC_DEFINE([HAVE_ISBLANK], [1], [Define if you have isblank(3C).]) - ]) - -+SCARD_MSG="yes" - disable_pkcs11= - AC_ARG_ENABLE([pkcs11], - [ --disable-pkcs11 disable PKCS#11 support code [no]], - [ - if test "x$enableval" = "xno" ; then - disable_pkcs11=1 -+ SCARD_MSG="no" - fi - ] - ) -@@ -1916,6 +1918,40 @@ if test "x$openssl" = "xyes" && test "x$disable_pkcs11" = "x"; then - ) - fi - -+# Check whether we have a p11-kit, we got default provider on command line -+DEFAULT_PKCS11_PROVIDER_MSG="no" -+AC_ARG_WITH([default-pkcs11-provider], -+ [ --with-default-pkcs11-provider[[=PATH]] Use default pkcs11 provider (p11-kit detected by default)], -+ [ if test "x$withval" != "xno" && test "x$disable_pkcs11" = "x"; then -+ if test "x$withval" = "xyes" ; then -+ AC_PATH_TOOL([PKGCONFIG], [pkg-config], [no]) -+ if test "x$PKGCONFIG" != "xno"; then -+ AC_MSG_CHECKING([if $PKGCONFIG knows about p11-kit]) -+ if "$PKGCONFIG" "p11-kit-1"; then -+ AC_MSG_RESULT([yes]) -+ use_pkgconfig_for_p11kit=yes -+ else -+ AC_MSG_RESULT([no]) -+ fi -+ fi -+ else -+ PKCS11_PATH="${withval}" -+ fi -+ if test "x$use_pkgconfig_for_p11kit" = "xyes"; then -+ PKCS11_PATH=`$PKGCONFIG --variable=proxy_module p11-kit-1` -+ fi -+ AC_CHECK_FILE("$PKCS11_PATH", -+ [ AC_DEFINE_UNQUOTED([PKCS11_DEFAULT_PROVIDER], ["$PKCS11_PATH"], [Path to default PKCS#11 provider (p11-kit proxy)]) -+ DEFAULT_PKCS11_PROVIDER_MSG="$PKCS11_PATH" -+ ], -+ [ AC_MSG_ERROR([Requested PKCS11 provided not found]) ] -+ ) -+ else -+ AC_MSG_WARN([Needs PKCS11 support to enable default pkcs11 provider]) -+ fi ] -+) -+ -+ - # IRIX has a const char return value for gai_strerror() - AC_CHECK_FUNCS([gai_strerror], [ - AC_DEFINE([HAVE_GAI_STRERROR]) -@@ -5226,6 +5262,7 @@ echo " Translate v4 in v6 hack: $IPV4_IN6_HACK_MSG" - echo " Random number source: $RAND_MSG" - echo " Privsep sandbox style: $SANDBOX_STYLE" - echo " Vendor patch level: $SSH_VENDOR_PATCHLEVEL" -+echo " Default PKCS#11 provider: $DEFAULT_PKCS11_PROVIDER_MSG" - - echo "" - -diff --git a/regress/Makefile b/regress/Makefile -index d15898ad..9c15afa4 100644 ---- a/regress/Makefile -+++ b/regress/Makefile -@@ -108,9 +110,11 @@ CLEANFILES= *.core actual agent-key.* authorized_keys_${USERNAME} \ - 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 \ -+ remote_pid revoked-* rsa rsa-agent rsa-agent.pub \ -+ rsa-agent-cert.pub rsa.pub \ - rsa1 rsa1-agent rsa1-agent.pub rsa1.pub rsa_ssh2_cr.prv \ -- rsa_ssh2_crnl.prv scp-ssh-wrapper.exe \ -+ soft-pkcs11.so soft-pkcs11.o pkcs11*.crt pkcs11*.key \ -+ pkcs11.info 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_config ssh_config.* ssh_proxy ssh_proxy_bak \ -@@ -225,6 +229,7 @@ unit: - V="" ; \ - test "x${USE_VALGRIND}" = "x" || \ - V=${.CURDIR}/valgrind-unit.sh ; \ -+ $$V ${.OBJDIR}/unittests/pkcs11/test_pkcs11 ; \ - $$V ${.OBJDIR}/unittests/sshbuf/test_sshbuf ; \ - $$V ${.OBJDIR}/unittests/sshkey/test_sshkey \ - -d ${.CURDIR}/unittests/sshkey/testdata ; \ -diff --git a/regress/agent-pkcs11.sh b/regress/agent-pkcs11.sh -index db3018b8..4991d0bc 100644 ---- a/regress/agent-pkcs11.sh -+++ b/regress/agent-pkcs11.sh -@@ -4,10 +4,17 @@ - tid="pkcs11 agent test" - - TEST_SSH_PIN="" --TEST_SSH_PKCS11=/usr/local/lib/soft-pkcs11.so.0.0 -+TEST_SSH_PKCS11=$OBJ/soft-pkcs11.so - - test -f "$TEST_SSH_PKCS11" || fatal "$TEST_SSH_PKCS11 does not exist" - -+# requires ssh-agent built with correct path to ssh-pkcs11-helper -+# otherwise it fails to start the helper -+strings ${TEST_SSH_SSHAGENT} | grep "$TEST_SSH_SSHPKCS11HELPER" -+if [ $? -ne 0 ]; then -+ fatal "Needs to reconfigure with --libexecdir=\`pwd\` or so" -+fi -+ - # setup environment for soft-pkcs11 token - SOFTPKCS11RC=$OBJ/pkcs11.info - export SOFTPKCS11RC -@@ -23,7 +30,7 @@ notty() { - } - - trace "start agent" --eval `${SSHAGENT} -s` > /dev/null -+eval `${SSHAGENT} -s -P "${OBJ}/*"` > /dev/null - r=$? - if [ $r -ne 0 ]; then - fail "could not start ssh-agent: exit code $r" -@@ -60,7 +67,7 @@ else - fi - - trace "remove pkcs11 keys" -- echo ${TEST_SSH_PIN} | notty ${SSHADD} -e ${TEST_SSH_PKCS11} > /dev/null 2>&1 -+ ${SSHADD} -e ${TEST_SSH_PKCS11} > /dev/null 2>&1 - r=$? - if [ $r -ne 0 ]; then - fail "ssh-add -e failed: exit code $r" -diff --git a/regress/locl.h b/regress/locl.h -new file mode 100644 -index 00000000..afefe27d ---- /dev/null -+++ b/regress/locl.h -@@ -0,0 +1,79 @@ -+/* -+ * Copyright (c) 2004, Stockholms universitet -+ * (Stockholm University, Stockholm Sweden) -+ * 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 COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. -+ */ -+ -+/* $Id: locl.h,v 1.5 2005/08/28 15:30:31 lha Exp $ */ -+ -+#ifdef HAVE_CONFIG_H -+#include -+#endif -+ -+#include -+#include -+#include -+#include -+#include -+#include "../libcrypto-compat.h" -+ -+#include -+#include -+#include -+#include -+#define _GNU_SOURCE -+#include -+#include -+#include -+#include -+ -+#include "../pkcs11.h" -+ -+#define OPENSSL_ASN1_MALLOC_ENCODE(T, B, BL, S, R) \ -+{ \ -+ unsigned char *p; \ -+ (BL) = i2d_##T((S), NULL); \ -+ if ((BL) <= 0) { \ -+ (R) = EINVAL; \ -+ } else { \ -+ (B) = malloc((BL)); \ -+ if ((B) == NULL) { \ -+ (R) = ENOMEM; \ -+ } else { \ -+ p = (B); \ -+ (R) = 0; \ -+ (BL) = i2d_##T((S), &p); \ -+ if ((BL) <= 0) { \ -+ free((B)); \ -+ (R) = EINVAL; \ -+ } \ -+ } \ -+ } \ -+} -diff --git a/regress/pkcs11.sh b/regress/pkcs11.sh -new file mode 100644 -index 00000000..cf98e379 ---- /dev/null -+++ b/regress/pkcs11.sh -@@ -0,0 +1,300 @@ -+# -+# Copyright (c) 2017 Red Hat -+# -+# Authors: Jakub Jelen -+# -+# 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. -+ -+tid="pkcs11 tests with soft token" -+ -+TEST_SSH_PIN="" -+TEST_SSH_PKCS11=$OBJ/soft-pkcs11.so -+ -+test -f "$TEST_SSH_PKCS11" || fatal "$TEST_SSH_PKCS11 does not exist" -+ -+# requires ssh-agent built with correct path to ssh-pkcs11-helper -+# otherwise it fails to start the helper -+strings ${TEST_SSH_SSHAGENT} | grep "$TEST_SSH_SSHPKCS11HELPER" -+if [ $? -ne 0 ]; then -+ fatal "Needs to reconfigure with --libexecdir=\`pwd\` or so" -+fi -+ -+# setup environment for soft-pkcs11 token -+SOFTPKCS11RC=$OBJ/pkcs11.info -+rm -f $SOFTPKCS11RC -+export SOFTPKCS11RC -+# prevent ssh-agent from calling ssh-askpass -+SSH_ASKPASS=/usr/bin/true -+export SSH_ASKPASS -+unset DISPLAY -+ -+# start command w/o tty, so ssh accepts pin from stdin (from agent-pkcs11.sh) -+notty() { -+ perl -e 'use POSIX; POSIX::setsid(); -+ if (fork) { wait; exit($? >> 8); } else { exec(@ARGV) }' "$@" -+} -+ -+create_key() { -+ ID=$1 -+ LABEL=$2 -+ rm -f $OBJ/pkcs11-${ID}.key $OBJ/pkcs11-${ID}.crt -+ openssl genrsa -out $OBJ/pkcs11-${ID}.key 2048 > /dev/null 2>&1 -+ chmod 600 $OBJ/pkcs11-${ID}.key -+ openssl req -key $OBJ/pkcs11-${ID}.key -new -x509 \ -+ -out $OBJ/pkcs11-${ID}.crt -text -subj '/CN=pkcs11 test' >/dev/null -+ printf "${ID}\t${LABEL}\t$OBJ/pkcs11-${ID}.crt\t$OBJ/pkcs11-${ID}.key\n" \ -+ >> $SOFTPKCS11RC -+} -+ -+trace "Create a key pairs on soft token" -+ID1="02" -+ID2="04" -+create_key "$ID1" "SSH RSA Key" -+create_key "$ID2" "SSH RSA Key 2" -+ -+trace "List the keys in the ssh-keygen with PKCS#11 URIs" -+${SSHKEYGEN} -D ${TEST_SSH_PKCS11} > $OBJ/token_keys -+if [ $? -ne 0 ]; then -+ fail "keygen fails to enumerate keys on PKCS#11 token" -+fi -+grep "pkcs11:" $OBJ/token_keys > /dev/null -+if [ $? -ne 0 ]; then -+ fail "The keys from ssh-keygen do not contain PKCS#11 URI as a comment" -+fi -+tail -n 1 $OBJ/token_keys > $OBJ/authorized_keys_$USER -+ -+ -+trace "Simple connect with ssh (without PKCS#11 URI)" -+echo ${TEST_SSH_PIN} | notty ${SSH} -I ${TEST_SSH_PKCS11} \ -+ -F $OBJ/ssh_proxy somehost exit 5 -+r=$? -+if [ $r -ne 5 ]; then -+ fail "ssh connect with pkcs11 failed (exit code $r)" -+fi -+ -+ -+trace "Connect with PKCS#11 URI" -+trace " (second key should succeed)" -+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \ -+ -i "pkcs11:id=${ID2}?module-path=${TEST_SSH_PKCS11}" somehost exit 5 -+r=$? -+if [ $r -ne 5 ]; then -+ fail "ssh connect with PKCS#11 URI failed (exit code $r)" -+fi -+ -+trace " (first key should fail)" -+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \ -+ -i "pkcs11:id=${ID1}?module-path=${TEST_SSH_PKCS11}" somehost exit 5 -+r=$? -+if [ $r -eq 5 ]; then -+ fail "ssh connect with PKCS#11 URI succeeded (should fail)" -+fi -+ -+trace "Connect with various filtering options in PKCS#11 URI" -+trace " (by object label, second key should succeed)" -+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \ -+ -i "pkcs11:object=SSH%20RSA%20Key%202?module-path=${TEST_SSH_PKCS11}" somehost exit 5 -+r=$? -+if [ $r -ne 5 ]; then -+ fail "ssh connect with PKCS#11 URI failed (exit code $r)" -+fi -+ -+trace " (by object label, first key should fail)" -+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \ -+ -i "pkcs11:object=SSH%20RSA%20Key?module-path=${TEST_SSH_PKCS11}" somehost exit 5 -+r=$? -+if [ $r -eq 5 ]; then -+ fail "ssh connect with PKCS#11 URI succeeded (should fail)" -+fi -+ -+trace " (by token label, second key should succeed)" -+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \ -+ -i "pkcs11:id=${ID2};token=SoftToken%20(token)?module-path=${TEST_SSH_PKCS11}" somehost exit 5 -+r=$? -+if [ $r -ne 5 ]; then -+ fail "ssh connect with PKCS#11 URI failed (exit code $r)" -+fi -+ -+trace " (by wrong token label, should fail)" -+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \ -+ -i "pkcs11:token=SoftToken?module-path=${TEST_SSH_PKCS11}" somehost exit 5 -+r=$? -+if [ $r -eq 5 ]; then -+ fail "ssh connect with PKCS#11 URI succeeded (should fail)" -+fi -+ -+ -+ -+ -+trace "Test PKCS#11 URI specification in configuration files" -+echo "IdentityFile \"pkcs11:id=${ID2}?module-path=${TEST_SSH_PKCS11}\"" \ -+ >> $OBJ/ssh_proxy -+trace " (second key should succeed)" -+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy somehost exit 5 -+r=$? -+if [ $r -ne 5 ]; then -+ fail "ssh connect with PKCS#11 URI in config failed (exit code $r)" -+fi -+ -+trace " (first key should fail)" -+head -n 1 $OBJ/token_keys > $OBJ/authorized_keys_$USER -+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy somehost exit 5 -+r=$? -+if [ $r -eq 5 ]; then -+ fail "ssh connect with PKCS#11 URI in config succeeded (should fail)" -+fi -+sed -i -e "/IdentityFile/d" $OBJ/ssh_proxy -+ -+trace "Test PKCS#11 URI specification in configuration files with bogus spaces" -+echo "IdentityFile \" pkcs11:id=${ID1}?module-path=${TEST_SSH_PKCS11} \"" \ -+ >> $OBJ/ssh_proxy -+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy somehost exit 5 -+r=$? -+if [ $r -ne 5 ]; then -+ fail "ssh connect with PKCS#11 URI with bogus spaces in config failed" \ -+ "(exit code $r)" -+fi -+sed -i -e "/IdentityFile/d" $OBJ/ssh_proxy -+ -+ -+trace "Combination of PKCS11Provider and PKCS11URI on commandline" -+trace " (first key should succeed)" -+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \ -+ -i "pkcs11:id=${ID1}" -I ${TEST_SSH_PKCS11} somehost exit 5 -+r=$? -+if [ $r -ne 5 ]; then -+ fail "ssh connect with PKCS#11 URI and provider combination" \ -+ "failed (exit code $r)" -+fi -+ -+trace "Regress: Missing provider in PKCS11URI option" -+${SSH} -F $OBJ/ssh_proxy \ -+ -o IdentityFile=\"pkcs11:token=segfault\" somehost exit 5 -+r=$? -+if [ $r -eq 139 ]; then -+ fail "ssh connect with missing provider_id from configuration option" \ -+ "crashed (exit code $r)" -+fi -+ -+ -+trace "SSH Agent can work with PKCS#11 URI" -+trace "start the agent" -+eval `${SSHAGENT} -s -P "${OBJ}/*"` > /dev/null -+ -+r=$? -+if [ $r -ne 0 ]; then -+ fail "could not start ssh-agent: exit code $r" -+else -+ trace "add whole provider to agent" -+ echo ${TEST_SSH_PIN} | notty ${SSHADD} \ -+ "pkcs11:?module-path=${TEST_SSH_PKCS11}" > /dev/null 2>&1 -+ r=$? -+ if [ $r -ne 0 ]; then -+ fail "ssh-add failed with whole provider: exit code $r" -+ fi -+ -+ trace " pkcs11 list via agent (all keys)" -+ ${SSHADD} -l > /dev/null 2>&1 -+ r=$? -+ if [ $r -ne 0 ]; then -+ fail "ssh-add -l failed with whole provider: exit code $r" -+ fi -+ -+ trace " pkcs11 connect via agent (all keys)" -+ ${SSH} -F $OBJ/ssh_proxy somehost exit 5 -+ r=$? -+ if [ $r -ne 5 ]; then -+ fail "ssh connect failed with whole provider (exit code $r)" -+ fi -+ -+ trace " remove pkcs11 keys (all keys)" -+ ${SSHADD} -d "pkcs11:?module-path=${TEST_SSH_PKCS11}" > /dev/null 2>&1 -+ r=$? -+ if [ $r -ne 0 ]; then -+ fail "ssh-add -d failed with whole provider: exit code $r" -+ fi -+ -+ trace "add only first key to the agent" -+ echo ${TEST_SSH_PIN} | notty ${SSHADD} \ -+ "pkcs11:id=${ID1}?module-path=${TEST_SSH_PKCS11}" > /dev/null 2>&1 -+ r=$? -+ if [ $r -ne 0 ]; then -+ fail "ssh-add failed with first key: exit code $r" -+ fi -+ -+ trace " pkcs11 connect via agent (first key)" -+ ${SSH} -F $OBJ/ssh_proxy somehost exit 5 -+ r=$? -+ if [ $r -ne 5 ]; then -+ fail "ssh connect failed with first key (exit code $r)" -+ fi -+ -+ trace " remove first pkcs11 key" -+ ${SSHADD} -d "pkcs11:id=${ID1}?module-path=${TEST_SSH_PKCS11}" \ -+ > /dev/null 2>&1 -+ r=$? -+ if [ $r -ne 0 ]; then -+ fail "ssh-add -d failed with first key: exit code $r" -+ fi -+ -+ trace "add only second key to the agent" -+ echo ${TEST_SSH_PIN} | notty ${SSHADD} \ -+ "pkcs11:id=${ID2}?module-path=${TEST_SSH_PKCS11}" > /dev/null 2>&1 -+ r=$? -+ if [ $r -ne 0 ]; then -+ fail "ssh-add failed with second key: exit code $r" -+ fi -+ -+ trace " pkcs11 connect via agent (second key should fail)" -+ ${SSH} -F $OBJ/ssh_proxy somehost exit 5 -+ r=$? -+ if [ $r -eq 5 ]; then -+ fail "ssh connect passed without key (should fail)" -+ fi -+ -+ trace "add also the first key to the agent" -+ echo ${TEST_SSH_PIN} | notty ${SSHADD} \ -+ "pkcs11:id=${ID1}?module-path=${TEST_SSH_PKCS11}" > /dev/null 2>&1 -+ r=$? -+ if [ $r -ne 0 ]; then -+ fail "ssh-add failed with first key: exit code $r" -+ fi -+ -+ trace " remove second pkcs11 key" -+ ${SSHADD} -d "pkcs11:id=${ID2}?module-path=${TEST_SSH_PKCS11}" \ -+ > /dev/null 2>&1 -+ r=$? -+ if [ $r -ne 0 ]; then -+ fail "ssh-add -d failed with second key: exit code $r" -+ fi -+ -+ trace " remove already-removed pkcs11 key should fail" -+ ${SSHADD} -d "pkcs11:id=${ID2}?module-path=${TEST_SSH_PKCS11}" \ -+ > /dev/null 2>&1 -+ r=$? -+ if [ $r -eq 0 ]; then -+ fail "ssh-add -d passed with non-existing key (should fail)" -+ fi -+ -+ trace " pkcs11 connect via agent (the first key should be still usable)" -+ ${SSH} -F $OBJ/ssh_proxy somehost exit 5 -+ r=$? -+ if [ $r -ne 5 ]; then -+ fail "ssh connect failed with first key (after removing second): exit code $r" -+ fi -+ -+ trace "kill agent" -+ ${SSHAGENT} -k > /dev/null -+fi -+ -+rm -rf $OBJ/.tokens $OBJ/token_keys -diff --git a/regress/soft-pkcs11.c b/regress/soft-pkcs11.c -new file mode 100644 -index 00000000..8b4981bd ---- /dev/null -+++ b/regress/soft-pkcs11.c -@@ -0,0 +1,2058 @@ -+/* -+ * Copyright (c) 2004-2006, Stockholms universitet -+ * (Stockholm University, Stockholm Sweden) -+ * 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 COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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 "locl.h" -+ -+/* RCSID("$Id: main.c,v 1.24 2006/01/11 12:42:53 lha Exp $"); */ -+ -+#define OBJECT_ID_MASK 0xfff -+#define HANDLE_OBJECT_ID(h) ((h) & OBJECT_ID_MASK) -+#define OBJECT_ID(obj) HANDLE_OBJECT_ID((obj)->object_handle) -+ -+#if OPENSSL_VERSION_NUMBER < 0x10100000L -+ #define RSA_PKCS1_SSLeay RSA_PKCS1_OpenSSL -+#endif -+ -+struct st_attr { -+ CK_ATTRIBUTE attribute; -+ int secret; -+}; -+ -+struct st_object { -+ CK_OBJECT_HANDLE object_handle; -+ struct st_attr *attrs; -+ int num_attributes; -+ enum { -+ STO_T_CERTIFICATE, -+ STO_T_PRIVATE_KEY, -+ STO_T_PUBLIC_KEY -+ } type; -+ union { -+ X509 *cert; -+ EVP_PKEY *public_key; -+ struct { -+ const char *file; -+ EVP_PKEY *key; -+ X509 *cert; -+ } private_key; -+ } u; -+}; -+ -+static struct soft_token { -+ CK_VOID_PTR application; -+ CK_NOTIFY notify; -+ struct { -+ struct st_object **objs; -+ int num_objs; -+ } object; -+ struct { -+ int hardware_slot; -+ int app_error_fatal; -+ int login_done; -+ } flags; -+ int open_sessions; -+ struct session_state { -+ CK_SESSION_HANDLE session_handle; -+ -+ struct { -+ CK_ATTRIBUTE *attributes; -+ CK_ULONG num_attributes; -+ int next_object; -+ } find; -+ -+ int encrypt_object; -+ CK_MECHANISM_PTR encrypt_mechanism; -+ int decrypt_object; -+ CK_MECHANISM_PTR decrypt_mechanism; -+ int sign_object; -+ CK_MECHANISM_PTR sign_mechanism; -+ int verify_object; -+ CK_MECHANISM_PTR verify_mechanism; -+ int digest_object; -+ } state[10]; -+#define MAX_NUM_SESSION (sizeof(soft_token.state)/sizeof(soft_token.state[0])) -+ FILE *logfile; -+} soft_token; -+ -+static void -+application_error(const char *fmt, ...) -+{ -+ va_list ap; -+ va_start(ap, fmt); -+ vprintf(fmt, ap); -+ va_end(ap); -+ if (soft_token.flags.app_error_fatal) -+ abort(); -+} -+ -+static void -+st_logf(const char *fmt, ...) -+{ -+ va_list ap; -+ if (soft_token.logfile == NULL) -+ return; -+ va_start(ap, fmt); -+ vfprintf(soft_token.logfile, fmt, ap); -+ va_end(ap); -+ fflush(soft_token.logfile); -+} -+ -+static void -+snprintf_fill(char *str, size_t size, char fillchar, const char *fmt, ...) -+{ -+ int len; -+ va_list ap; -+ len = vsnprintf(str, size, fmt, ap); -+ va_end(ap); -+ if (len < 0 || len > (int) size) -+ return; -+ while(len < (int) size) -+ str[len++] = fillchar; -+} -+ -+#ifndef TEST_APP -+#define printf error_use_st_logf -+#endif -+ -+#define VERIFY_SESSION_HANDLE(s, state) \ -+{ \ -+ CK_RV ret; \ -+ ret = verify_session_handle(s, state); \ -+ if (ret != CKR_OK) { \ -+ /* return CKR_OK */; \ -+ } \ -+} -+ -+static CK_RV -+verify_session_handle(CK_SESSION_HANDLE hSession, -+ struct session_state **state) -+{ -+ size_t i; -+ -+ for (i = 0; i < MAX_NUM_SESSION; i++){ -+ if (soft_token.state[i].session_handle == hSession) -+ break; -+ } -+ if (i == MAX_NUM_SESSION) { -+ application_error("use of invalid handle: 0x%08lx\n", -+ (unsigned long)hSession); -+ return CKR_SESSION_HANDLE_INVALID; -+ } -+ if (state) -+ *state = &soft_token.state[i]; -+ return CKR_OK; -+} -+ -+static CK_RV -+object_handle_to_object(CK_OBJECT_HANDLE handle, -+ struct st_object **object) -+{ -+ int i = HANDLE_OBJECT_ID(handle); -+ -+ *object = NULL; -+ if (i >= soft_token.object.num_objs) -+ return CKR_ARGUMENTS_BAD; -+ if (soft_token.object.objs[i] == NULL) -+ return CKR_ARGUMENTS_BAD; -+ if (soft_token.object.objs[i]->object_handle != handle) -+ return CKR_ARGUMENTS_BAD; -+ *object = soft_token.object.objs[i]; -+ return CKR_OK; -+} -+ -+static int -+attributes_match(const struct st_object *obj, -+ const CK_ATTRIBUTE *attributes, -+ CK_ULONG num_attributes) -+{ -+ CK_ULONG i; -+ int j; -+ st_logf("attributes_match: %ld\n", (unsigned long)OBJECT_ID(obj)); -+ -+ for (i = 0; i < num_attributes; i++) { -+ int match = 0; -+ for (j = 0; j < obj->num_attributes; j++) { -+ if (attributes[i].type == obj->attrs[j].attribute.type && -+ attributes[i].ulValueLen == obj->attrs[j].attribute.ulValueLen && -+ memcmp(attributes[i].pValue, obj->attrs[j].attribute.pValue, -+ attributes[i].ulValueLen) == 0) { -+ match = 1; -+ break; -+ } -+ } -+ if (match == 0) { -+ st_logf("type %d attribute have no match\n", attributes[i].type); -+ return 0; -+ } -+ } -+ st_logf("attribute matches\n"); -+ return 1; -+} -+ -+static void -+print_attributes(const CK_ATTRIBUTE *attributes, -+ CK_ULONG num_attributes) -+{ -+ CK_ULONG i; -+ -+ st_logf("find objects: attrs: %lu\n", (unsigned long)num_attributes); -+ -+ for (i = 0; i < num_attributes; i++) { -+ st_logf(" type: "); -+ switch (attributes[i].type) { -+ case CKA_TOKEN: { -+ CK_BBOOL *ck_true; -+ if (attributes[i].ulValueLen != sizeof(CK_BBOOL)) { -+ application_error("token attribute wrong length\n"); -+ break; -+ } -+ ck_true = attributes[i].pValue; -+ st_logf("token: %s", *ck_true ? "TRUE" : "FALSE"); -+ break; -+ } -+ case CKA_CLASS: { -+ CK_OBJECT_CLASS *class; -+ if (attributes[i].ulValueLen != sizeof(CK_ULONG)) { -+ application_error("class attribute wrong length\n"); -+ break; -+ } -+ class = attributes[i].pValue; -+ st_logf("class "); -+ switch (*class) { -+ case CKO_CERTIFICATE: -+ st_logf("certificate"); -+ break; -+ case CKO_PUBLIC_KEY: -+ st_logf("public key"); -+ break; -+ case CKO_PRIVATE_KEY: -+ st_logf("private key"); -+ break; -+ case CKO_SECRET_KEY: -+ st_logf("secret key"); -+ break; -+ case CKO_DOMAIN_PARAMETERS: -+ st_logf("domain parameters"); -+ break; -+ default: -+ st_logf("[class %lx]", (long unsigned)*class); -+ break; -+ } -+ break; -+ } -+ case CKA_PRIVATE: -+ st_logf("private"); -+ break; -+ case CKA_LABEL: -+ st_logf("label"); -+ break; -+ case CKA_APPLICATION: -+ st_logf("application"); -+ break; -+ case CKA_VALUE: -+ st_logf("value"); -+ break; -+ case CKA_ID: -+ st_logf("id"); -+ break; -+ default: -+ st_logf("[unknown 0x%08lx]", (unsigned long)attributes[i].type); -+ break; -+ } -+ st_logf("\n"); -+ } -+} -+ -+static struct st_object * -+add_st_object(void) -+{ -+ struct st_object *o, **objs; -+ int i; -+ -+ o = malloc(sizeof(*o)); -+ if (o == NULL) -+ return NULL; -+ memset(o, 0, sizeof(*o)); -+ o->attrs = NULL; -+ o->num_attributes = 0; -+ -+ for (i = 0; i < soft_token.object.num_objs; i++) { -+ if (soft_token.object.objs == NULL) { -+ soft_token.object.objs[i] = o; -+ break; -+ } -+ } -+ if (i == soft_token.object.num_objs) { -+ objs = realloc(soft_token.object.objs, -+ (soft_token.object.num_objs + 1) * sizeof(soft_token.object.objs[0])); -+ if (objs == NULL) { -+ free(o); -+ return NULL; -+ } -+ soft_token.object.objs = objs; -+ soft_token.object.objs[soft_token.object.num_objs++] = o; -+ } -+ soft_token.object.objs[i]->object_handle = -+ (random() & (~OBJECT_ID_MASK)) | i; -+ -+ return o; -+} -+ -+static CK_RV -+add_object_attribute(struct st_object *o, -+ int secret, -+ CK_ATTRIBUTE_TYPE type, -+ CK_VOID_PTR pValue, -+ CK_ULONG ulValueLen) -+{ -+ struct st_attr *a; -+ int i; -+ -+ i = o->num_attributes; -+ a = realloc(o->attrs, (i + 1) * sizeof(o->attrs[0])); -+ if (a == NULL) -+ return CKR_DEVICE_MEMORY; -+ o->attrs = a; -+ o->attrs[i].secret = secret; -+ o->attrs[i].attribute.type = type; -+ o->attrs[i].attribute.pValue = malloc(ulValueLen); -+ if (o->attrs[i].attribute.pValue == NULL && ulValueLen != 0) -+ return CKR_DEVICE_MEMORY; -+ memcpy(o->attrs[i].attribute.pValue, pValue, ulValueLen); -+ o->attrs[i].attribute.ulValueLen = ulValueLen; -+ o->num_attributes++; -+ -+ return CKR_OK; -+} -+ -+static CK_RV -+add_pubkey_info(struct st_object *o, CK_KEY_TYPE key_type, EVP_PKEY *key) -+{ -+ switch (key_type) { -+ case CKK_RSA: { -+ CK_BYTE *modulus = NULL; -+ size_t modulus_len = 0; -+ CK_ULONG modulus_bits = 0; -+ CK_BYTE *exponent = NULL; -+ size_t exponent_len = 0; -+ RSA* rsa = NULL; -+ const BIGNUM *n = NULL, *e = NULL; -+ -+ rsa = EVP_PKEY_get0_RSA(key); -+ RSA_get0_key(rsa, &n, &e, NULL); -+ -+ modulus_bits = BN_num_bits(n); -+ -+ modulus_len = BN_num_bytes(n); -+ modulus = malloc(modulus_len); -+ BN_bn2bin(n, modulus); -+ -+ exponent_len = BN_num_bytes(e); -+ exponent = malloc(exponent_len); -+ BN_bn2bin(e, exponent); -+ -+ add_object_attribute(o, 0, CKA_MODULUS, modulus, modulus_len); -+ add_object_attribute(o, 0, CKA_MODULUS_BITS, -+ &modulus_bits, sizeof(modulus_bits)); -+ add_object_attribute(o, 0, CKA_PUBLIC_EXPONENT, -+ exponent, exponent_len); -+ -+ RSA_set_method(rsa, RSA_PKCS1_OpenSSL()); -+ -+ free(modulus); -+ free(exponent); -+ } -+ default: -+ /* XXX */ -+ break; -+ } -+ return CKR_OK; -+} -+ -+ -+static int -+pem_callback(char *buf, int num, int w, void *key) -+{ -+ return -1; -+} -+ -+ -+static CK_RV -+add_certificate(char *label, -+ const char *cert_file, -+ const char *private_key_file, -+ char *id, -+ int anchor) -+{ -+ struct st_object *o = NULL; -+ CK_BBOOL bool_true = CK_TRUE; -+ CK_BBOOL bool_false = CK_FALSE; -+ CK_OBJECT_CLASS c; -+ CK_CERTIFICATE_TYPE cert_type = CKC_X_509; -+ CK_KEY_TYPE key_type; -+ CK_MECHANISM_TYPE mech_type; -+ void *cert_data = NULL; -+ size_t cert_length; -+ void *subject_data = NULL; -+ size_t subject_length; -+ void *issuer_data = NULL; -+ size_t issuer_length; -+ void *serial_data = NULL; -+ size_t serial_length; -+ CK_RV ret = CKR_GENERAL_ERROR; -+ X509 *cert; -+ EVP_PKEY *public_key; -+ -+ size_t id_len = strlen(id); -+ -+ { -+ FILE *f; -+ -+ f = fopen(cert_file, "r"); -+ if (f == NULL) { -+ st_logf("failed to open file %s\n", cert_file); -+ return CKR_GENERAL_ERROR; -+ } -+ -+ cert = PEM_read_X509(f, NULL, NULL, NULL); -+ fclose(f); -+ if (cert == NULL) { -+ st_logf("failed reading PEM cert\n"); -+ return CKR_GENERAL_ERROR; -+ } -+ -+ OPENSSL_ASN1_MALLOC_ENCODE(X509, cert_data, cert_length, cert, ret); -+ if (ret) -+ goto out; -+ -+ OPENSSL_ASN1_MALLOC_ENCODE(X509_NAME, issuer_data, issuer_length, -+ X509_get_issuer_name(cert), ret); -+ if (ret) -+ goto out; -+ -+ OPENSSL_ASN1_MALLOC_ENCODE(X509_NAME, subject_data, subject_length, -+ X509_get_subject_name(cert), ret); -+ if (ret) -+ goto out; -+ -+ OPENSSL_ASN1_MALLOC_ENCODE(ASN1_INTEGER, serial_data, serial_length, -+ X509_get_serialNumber(cert), ret); -+ if (ret) -+ goto out; -+ -+ } -+ -+ st_logf("done parsing, adding to internal structure\n"); -+ -+ o = add_st_object(); -+ if (o == NULL) { -+ ret = CKR_DEVICE_MEMORY; -+ goto out; -+ } -+ o->type = STO_T_CERTIFICATE; -+ o->u.cert = cert; -+ public_key = X509_get_pubkey(o->u.cert); -+ -+ switch (EVP_PKEY_base_id(public_key)) { -+ case EVP_PKEY_RSA: -+ key_type = CKK_RSA; -+ break; -+ case EVP_PKEY_DSA: -+ key_type = CKK_DSA; -+ break; -+ default: -+ /* XXX */ -+ break; -+ } -+ -+ c = CKO_CERTIFICATE; -+ add_object_attribute(o, 0, CKA_CLASS, &c, sizeof(c)); -+ add_object_attribute(o, 0, CKA_TOKEN, &bool_true, sizeof(bool_true)); -+ add_object_attribute(o, 0, CKA_PRIVATE, &bool_false, sizeof(bool_false)); -+ add_object_attribute(o, 0, CKA_MODIFIABLE, &bool_false, sizeof(bool_false)); -+ add_object_attribute(o, 0, CKA_LABEL, label, strlen(label)); -+ -+ add_object_attribute(o, 0, CKA_CERTIFICATE_TYPE, &cert_type, sizeof(cert_type)); -+ add_object_attribute(o, 0, CKA_ID, id, id_len); -+ -+ add_object_attribute(o, 0, CKA_SUBJECT, subject_data, subject_length); -+ add_object_attribute(o, 0, CKA_ISSUER, issuer_data, issuer_length); -+ add_object_attribute(o, 0, CKA_SERIAL_NUMBER, serial_data, serial_length); -+ add_object_attribute(o, 0, CKA_VALUE, cert_data, cert_length); -+ if (anchor) -+ add_object_attribute(o, 0, CKA_TRUSTED, &bool_true, sizeof(bool_true)); -+ else -+ add_object_attribute(o, 0, CKA_TRUSTED, &bool_false, sizeof(bool_false)); -+ -+ st_logf("add cert ok: %lx\n", (unsigned long)OBJECT_ID(o)); -+ -+ o = add_st_object(); -+ if (o == NULL) { -+ ret = CKR_DEVICE_MEMORY; -+ goto out; -+ } -+ o->type = STO_T_PUBLIC_KEY; -+ o->u.public_key = public_key; -+ -+ c = CKO_PUBLIC_KEY; -+ add_object_attribute(o, 0, CKA_CLASS, &c, sizeof(c)); -+ add_object_attribute(o, 0, CKA_TOKEN, &bool_true, sizeof(bool_true)); -+ add_object_attribute(o, 0, CKA_PRIVATE, &bool_false, sizeof(bool_false)); -+ add_object_attribute(o, 0, CKA_MODIFIABLE, &bool_false, sizeof(bool_false)); -+ add_object_attribute(o, 0, CKA_LABEL, label, strlen(label)); -+ -+ add_object_attribute(o, 0, CKA_KEY_TYPE, &key_type, sizeof(key_type)); -+ add_object_attribute(o, 0, CKA_ID, id, id_len); -+ add_object_attribute(o, 0, CKA_START_DATE, "", 1); /* XXX */ -+ add_object_attribute(o, 0, CKA_END_DATE, "", 1); /* XXX */ -+ add_object_attribute(o, 0, CKA_DERIVE, &bool_false, sizeof(bool_false)); -+ add_object_attribute(o, 0, CKA_LOCAL, &bool_false, sizeof(bool_false)); -+ mech_type = CKM_RSA_X_509; -+ add_object_attribute(o, 0, CKA_KEY_GEN_MECHANISM, &mech_type, sizeof(mech_type)); -+ -+ add_object_attribute(o, 0, CKA_SUBJECT, subject_data, subject_length); -+ add_object_attribute(o, 0, CKA_ENCRYPT, &bool_true, sizeof(bool_true)); -+ add_object_attribute(o, 0, CKA_VERIFY, &bool_true, sizeof(bool_true)); -+ add_object_attribute(o, 0, CKA_VERIFY_RECOVER, &bool_false, sizeof(bool_false)); -+ add_object_attribute(o, 0, CKA_WRAP, &bool_true, sizeof(bool_true)); -+ add_object_attribute(o, 0, CKA_TRUSTED, &bool_true, sizeof(bool_true)); -+ -+ add_pubkey_info(o, key_type, public_key); -+ -+ st_logf("add key ok: %lx\n", (unsigned long)OBJECT_ID(o)); -+ -+ if (private_key_file) { -+ CK_FLAGS flags; -+ FILE *f; -+ -+ o = add_st_object(); -+ if (o == NULL) { -+ ret = CKR_DEVICE_MEMORY; -+ goto out; -+ } -+ o->type = STO_T_PRIVATE_KEY; -+ o->u.private_key.file = strdup(private_key_file); -+ o->u.private_key.key = NULL; -+ -+ o->u.private_key.cert = cert; -+ -+ c = CKO_PRIVATE_KEY; -+ add_object_attribute(o, 0, CKA_CLASS, &c, sizeof(c)); -+ add_object_attribute(o, 0, CKA_TOKEN, &bool_true, sizeof(bool_true)); -+ add_object_attribute(o, 0, CKA_PRIVATE, &bool_true, sizeof(bool_false)); -+ add_object_attribute(o, 0, CKA_MODIFIABLE, &bool_false, sizeof(bool_false)); -+ add_object_attribute(o, 0, CKA_LABEL, label, strlen(label)); -+ -+ add_object_attribute(o, 0, CKA_KEY_TYPE, &key_type, sizeof(key_type)); -+ add_object_attribute(o, 0, CKA_ID, id, id_len); -+ add_object_attribute(o, 0, CKA_START_DATE, "", 1); /* XXX */ -+ add_object_attribute(o, 0, CKA_END_DATE, "", 1); /* XXX */ -+ add_object_attribute(o, 0, CKA_DERIVE, &bool_false, sizeof(bool_false)); -+ add_object_attribute(o, 0, CKA_LOCAL, &bool_false, sizeof(bool_false)); -+ mech_type = CKM_RSA_X_509; -+ add_object_attribute(o, 0, CKA_KEY_GEN_MECHANISM, &mech_type, sizeof(mech_type)); -+ -+ add_object_attribute(o, 0, CKA_SUBJECT, subject_data, subject_length); -+ add_object_attribute(o, 0, CKA_SENSITIVE, &bool_true, sizeof(bool_true)); -+ add_object_attribute(o, 0, CKA_SECONDARY_AUTH, &bool_false, sizeof(bool_true)); -+ flags = 0; -+ add_object_attribute(o, 0, CKA_AUTH_PIN_FLAGS, &flags, sizeof(flags)); -+ -+ add_object_attribute(o, 0, CKA_DECRYPT, &bool_true, sizeof(bool_true)); -+ add_object_attribute(o, 0, CKA_SIGN, &bool_true, sizeof(bool_true)); -+ add_object_attribute(o, 0, CKA_SIGN_RECOVER, &bool_false, sizeof(bool_false)); -+ add_object_attribute(o, 0, CKA_UNWRAP, &bool_true, sizeof(bool_true)); -+ add_object_attribute(o, 0, CKA_EXTRACTABLE, &bool_true, sizeof(bool_true)); -+ add_object_attribute(o, 0, CKA_NEVER_EXTRACTABLE, &bool_false, sizeof(bool_false)); -+ -+ add_pubkey_info(o, key_type, public_key); -+ -+ f = fopen(private_key_file, "r"); -+ if (f == NULL) { -+ st_logf("failed to open private key\n"); -+ return CKR_GENERAL_ERROR; -+ } -+ -+ o->u.private_key.key = PEM_read_PrivateKey(f, NULL, pem_callback, NULL); -+ fclose(f); -+ if (o->u.private_key.key == NULL) { -+ st_logf("failed to read private key a startup\n"); -+ /* don't bother with this failure for now, -+ fix it at C_Login time */; -+ } else { -+ /* XXX verify keytype */ -+ -+ if (key_type == CKK_RSA) { -+ RSA *rsa = EVP_PKEY_get0_RSA(o->u.private_key.key); -+ RSA_set_method(rsa, RSA_PKCS1_OpenSSL()); -+ } -+ -+ if (X509_check_private_key(cert, o->u.private_key.key) != 1) { -+ EVP_PKEY_free(o->u.private_key.key); -+ o->u.private_key.key = NULL; -+ st_logf("private key doesn't verify\n"); -+ } else { -+ st_logf("private key usable\n"); -+ soft_token.flags.login_done = 1; -+ } -+ } -+ } -+ -+ ret = CKR_OK; -+ out: -+ if (ret != CKR_OK) { -+ st_logf("something went wrong when adding cert!\n"); -+ -+ /* XXX wack o */; -+ } -+ free(cert_data); -+ free(serial_data); -+ free(issuer_data); -+ free(subject_data); -+ -+ return ret; -+} -+ -+static void -+find_object_final(struct session_state *state) -+{ -+ if (state->find.attributes) { -+ CK_ULONG i; -+ -+ for (i = 0; i < state->find.num_attributes; i++) { -+ if (state->find.attributes[i].pValue) -+ free(state->find.attributes[i].pValue); -+ } -+ free(state->find.attributes); -+ state->find.attributes = NULL; -+ state->find.num_attributes = 0; -+ state->find.next_object = -1; -+ } -+} -+ -+static void -+reset_crypto_state(struct session_state *state) -+{ -+ state->encrypt_object = -1; -+ if (state->encrypt_mechanism) -+ free(state->encrypt_mechanism); -+ state->encrypt_mechanism = NULL_PTR; -+ state->decrypt_object = -1; -+ if (state->decrypt_mechanism) -+ free(state->decrypt_mechanism); -+ state->decrypt_mechanism = NULL_PTR; -+ state->sign_object = -1; -+ if (state->sign_mechanism) -+ free(state->sign_mechanism); -+ state->sign_mechanism = NULL_PTR; -+ state->verify_object = -1; -+ if (state->verify_mechanism) -+ free(state->verify_mechanism); -+ state->verify_mechanism = NULL_PTR; -+ state->digest_object = -1; -+} -+ -+static void -+close_session(struct session_state *state) -+{ -+ if (state->find.attributes) { -+ application_error("application didn't do C_FindObjectsFinal\n"); -+ find_object_final(state); -+ } -+ -+ state->session_handle = CK_INVALID_HANDLE; -+ soft_token.application = NULL_PTR; -+ soft_token.notify = NULL_PTR; -+ reset_crypto_state(state); -+} -+ -+static const char * -+has_session(void) -+{ -+ return soft_token.open_sessions > 0 ? "yes" : "no"; -+} -+ -+static void -+read_conf_file(const char *fn) -+{ -+ char buf[1024], *cert, *key, *id, *label, *s, *p; -+ int anchor; -+ FILE *f; -+ -+ f = fopen(fn, "r"); -+ if (f == NULL) { -+ st_logf("can't open configuration file %s\n", fn); -+ return; -+ } -+ -+ while(fgets(buf, sizeof(buf), f) != NULL) { -+ buf[strcspn(buf, "\n")] = '\0'; -+ -+ anchor = 0; -+ -+ st_logf("line: %s\n", buf); -+ -+ p = buf; -+ while (isspace(*p)) -+ p++; -+ if (*p == '#') -+ continue; -+ while (isspace(*p)) -+ p++; -+ -+ s = NULL; -+ id = strtok_r(p, "\t", &s); -+ if (id == NULL) -+ continue; -+ label = strtok_r(NULL, "\t", &s); -+ if (label == NULL) -+ continue; -+ cert = strtok_r(NULL, "\t", &s); -+ if (cert == NULL) -+ continue; -+ key = strtok_r(NULL, "\t", &s); -+ -+ /* XXX */ -+ if (strcmp(id, "anchor") == 0) { -+ id = "\x00\x00"; -+ anchor = 1; -+ } -+ -+ st_logf("adding: %s\n", label); -+ -+ add_certificate(label, cert, key, id, anchor); -+ } -+} -+ -+static CK_RV -+func_not_supported(void) -+{ -+ st_logf("function not supported\n"); -+ return CKR_FUNCTION_NOT_SUPPORTED; -+} -+ -+CK_RV -+C_Initialize(CK_VOID_PTR a) -+{ -+ CK_C_INITIALIZE_ARGS_PTR args = a; -+ st_logf("Initialize\n"); -+ size_t i; -+ -+ OpenSSL_add_all_algorithms(); -+ ERR_load_crypto_strings(); -+ -+ srandom(getpid() ^ time(NULL)); -+ -+ for (i = 0; i < MAX_NUM_SESSION; i++) { -+ soft_token.state[i].session_handle = CK_INVALID_HANDLE; -+ soft_token.state[i].find.attributes = NULL; -+ soft_token.state[i].find.num_attributes = 0; -+ soft_token.state[i].find.next_object = -1; -+ reset_crypto_state(&soft_token.state[i]); -+ } -+ -+ soft_token.flags.hardware_slot = 1; -+ soft_token.flags.app_error_fatal = 0; -+ soft_token.flags.login_done = 0; -+ -+ soft_token.object.objs = NULL; -+ soft_token.object.num_objs = 0; -+ -+ soft_token.logfile = NULL; -+#if 1 -+// soft_token.logfile = stdout; -+#endif -+#if 0 -+ soft_token.logfile = fopen("/tmp/log-pkcs11.txt", "a"); -+#endif -+ -+ if (a != NULL_PTR) { -+ st_logf("\tCreateMutex:\t%p\n", args->CreateMutex); -+ st_logf("\tDestroyMutext\t%p\n", args->DestroyMutex); -+ st_logf("\tLockMutext\t%p\n", args->LockMutex); -+ st_logf("\tUnlockMutext\t%p\n", args->UnlockMutex); -+ st_logf("\tFlags\t%04x\n", (unsigned int)args->flags); -+ } -+ -+ { -+ char *fn = NULL, *home = NULL; -+ -+ if (getuid() == geteuid()) { -+ fn = getenv("SOFTPKCS11RC"); -+ if (fn) -+ fn = strdup(fn); -+ home = getenv("HOME"); -+ } -+ if (fn == NULL && home == NULL) { -+ struct passwd *pw = getpwuid(getuid()); -+ if(pw != NULL) -+ home = pw->pw_dir; -+ } -+ if (fn == NULL) { -+ if (home) -+ asprintf(&fn, "%s/.soft-token.rc", home); -+ else -+ fn = strdup("/etc/soft-token.rc"); -+ } -+ -+ read_conf_file(fn); -+ free(fn); -+ } -+ -+ return CKR_OK; -+} -+ -+CK_RV -+C_Finalize(CK_VOID_PTR args) -+{ -+ size_t i; -+ -+ st_logf("Finalize\n"); -+ -+ for (i = 0; i < MAX_NUM_SESSION; i++) { -+ if (soft_token.state[i].session_handle != CK_INVALID_HANDLE) { -+ application_error("application finalized without " -+ "closing session\n"); -+ close_session(&soft_token.state[i]); -+ } -+ } -+ -+ return CKR_OK; -+} -+ -+CK_RV -+C_GetInfo(CK_INFO_PTR args) -+{ -+ st_logf("GetInfo\n"); -+ -+ memset(args, 17, sizeof(*args)); -+ args->cryptokiVersion.major = 2; -+ args->cryptokiVersion.minor = 10; -+ snprintf_fill((char *)args->manufacturerID, -+ sizeof(args->manufacturerID), -+ ' ', -+ "SoftToken"); -+ snprintf_fill((char *)args->libraryDescription, -+ sizeof(args->libraryDescription), ' ', -+ "SoftToken"); -+ args->libraryVersion.major = 1; -+ args->libraryVersion.minor = 8; -+ -+ return CKR_OK; -+} -+ -+extern CK_FUNCTION_LIST funcs; -+ -+CK_RV -+C_GetFunctionList(CK_FUNCTION_LIST_PTR_PTR ppFunctionList) -+{ -+ *ppFunctionList = &funcs; -+ return CKR_OK; -+} -+ -+CK_RV -+C_GetSlotList(CK_BBOOL tokenPresent, -+ CK_SLOT_ID_PTR pSlotList, -+ CK_ULONG_PTR pulCount) -+{ -+ st_logf("GetSlotList: %s\n", -+ tokenPresent ? "tokenPresent" : "token not Present"); -+ if (pSlotList) -+ pSlotList[0] = 1; -+ *pulCount = 1; -+ return CKR_OK; -+} -+ -+CK_RV -+C_GetSlotInfo(CK_SLOT_ID slotID, -+ CK_SLOT_INFO_PTR pInfo) -+{ -+ st_logf("GetSlotInfo: slot: %d : %s\n", (int)slotID, has_session()); -+ -+ memset(pInfo, 18, sizeof(*pInfo)); -+ -+ if (slotID != 1) -+ return CKR_ARGUMENTS_BAD; -+ -+ snprintf_fill((char *)pInfo->slotDescription, -+ sizeof(pInfo->slotDescription), -+ ' ', -+ "SoftToken (slot)"); -+ snprintf_fill((char *)pInfo->manufacturerID, -+ sizeof(pInfo->manufacturerID), -+ ' ', -+ "SoftToken (slot)"); -+ pInfo->flags = CKF_TOKEN_PRESENT; -+ if (soft_token.flags.hardware_slot) -+ pInfo->flags |= CKF_HW_SLOT; -+ pInfo->hardwareVersion.major = 1; -+ pInfo->hardwareVersion.minor = 0; -+ pInfo->firmwareVersion.major = 1; -+ pInfo->firmwareVersion.minor = 0; -+ -+ return CKR_OK; -+} -+ -+CK_RV -+C_GetTokenInfo(CK_SLOT_ID slotID, -+ CK_TOKEN_INFO_PTR pInfo) -+{ -+ st_logf("GetTokenInfo: %s\n", has_session()); -+ -+ memset(pInfo, 19, sizeof(*pInfo)); -+ -+ snprintf_fill((char *)pInfo->label, -+ sizeof(pInfo->label), -+ ' ', -+ "SoftToken (token)"); -+ snprintf_fill((char *)pInfo->manufacturerID, -+ sizeof(pInfo->manufacturerID), -+ ' ', -+ "SoftToken (token)"); -+ snprintf_fill((char *)pInfo->model, -+ sizeof(pInfo->model), -+ ' ', -+ "SoftToken (token)"); -+ snprintf_fill((char *)pInfo->serialNumber, -+ sizeof(pInfo->serialNumber), -+ ' ', -+ "4711"); -+ pInfo->flags = -+ CKF_TOKEN_INITIALIZED | -+ CKF_USER_PIN_INITIALIZED; -+ -+ if (soft_token.flags.login_done == 0) -+ pInfo->flags |= CKF_LOGIN_REQUIRED; -+ -+ /* CFK_RNG | -+ CKF_RESTORE_KEY_NOT_NEEDED | -+ */ -+ pInfo->ulMaxSessionCount = MAX_NUM_SESSION; -+ pInfo->ulSessionCount = soft_token.open_sessions; -+ pInfo->ulMaxRwSessionCount = MAX_NUM_SESSION; -+ pInfo->ulRwSessionCount = soft_token.open_sessions; -+ pInfo->ulMaxPinLen = 1024; -+ pInfo->ulMinPinLen = 0; -+ pInfo->ulTotalPublicMemory = 4711; -+ pInfo->ulFreePublicMemory = 4712; -+ pInfo->ulTotalPrivateMemory = 4713; -+ pInfo->ulFreePrivateMemory = 4714; -+ pInfo->hardwareVersion.major = 2; -+ pInfo->hardwareVersion.minor = 0; -+ pInfo->firmwareVersion.major = 2; -+ pInfo->firmwareVersion.minor = 0; -+ -+ return CKR_OK; -+} -+ -+CK_RV -+C_GetMechanismList(CK_SLOT_ID slotID, -+ CK_MECHANISM_TYPE_PTR pMechanismList, -+ CK_ULONG_PTR pulCount) -+{ -+ st_logf("GetMechanismList\n"); -+ -+ *pulCount = 2; -+ if (pMechanismList == NULL_PTR) -+ return CKR_OK; -+ pMechanismList[0] = CKM_RSA_X_509; -+ pMechanismList[1] = CKM_RSA_PKCS; -+ -+ return CKR_OK; -+} -+ -+CK_RV -+C_GetMechanismInfo(CK_SLOT_ID slotID, -+ CK_MECHANISM_TYPE type, -+ CK_MECHANISM_INFO_PTR pInfo) -+{ -+ st_logf("GetMechanismInfo: slot %d type: %d\n", -+ (int)slotID, (int)type); -+ return CKR_FUNCTION_NOT_SUPPORTED; -+} -+ -+CK_RV -+C_InitToken(CK_SLOT_ID slotID, -+ CK_UTF8CHAR_PTR pPin, -+ CK_ULONG ulPinLen, -+ CK_UTF8CHAR_PTR pLabel) -+{ -+ st_logf("InitToken: slot %d\n", (int)slotID); -+ return CKR_FUNCTION_NOT_SUPPORTED; -+} -+ -+CK_RV -+C_OpenSession(CK_SLOT_ID slotID, -+ CK_FLAGS flags, -+ CK_VOID_PTR pApplication, -+ CK_NOTIFY Notify, -+ CK_SESSION_HANDLE_PTR phSession) -+{ -+ size_t i; -+ -+ st_logf("OpenSession: slot: %d\n", (int)slotID); -+ -+ if (soft_token.open_sessions == MAX_NUM_SESSION) -+ return CKR_SESSION_COUNT; -+ -+ soft_token.application = pApplication; -+ soft_token.notify = Notify; -+ -+ for (i = 0; i < MAX_NUM_SESSION; i++) -+ if (soft_token.state[i].session_handle == CK_INVALID_HANDLE) -+ break; -+ if (i == MAX_NUM_SESSION) -+ abort(); -+ -+ soft_token.open_sessions++; -+ -+ soft_token.state[i].session_handle = -+ (CK_SESSION_HANDLE)(random() & 0xfffff); -+ *phSession = soft_token.state[i].session_handle; -+ -+ return CKR_OK; -+} -+ -+CK_RV -+C_CloseSession(CK_SESSION_HANDLE hSession) -+{ -+ struct session_state *state; -+ st_logf("CloseSession\n"); -+ -+ if (verify_session_handle(hSession, &state) != CKR_OK) -+ application_error("closed session not open"); -+ else -+ close_session(state); -+ -+ return CKR_OK; -+} -+ -+CK_RV -+C_CloseAllSessions(CK_SLOT_ID slotID) -+{ -+ size_t i; -+ -+ st_logf("CloseAllSessions\n"); -+ -+ for (i = 0; i < MAX_NUM_SESSION; i++) -+ if (soft_token.state[i].session_handle != CK_INVALID_HANDLE) -+ close_session(&soft_token.state[i]); -+ -+ return CKR_OK; -+} -+ -+CK_RV -+C_GetSessionInfo(CK_SESSION_HANDLE hSession, -+ CK_SESSION_INFO_PTR pInfo) -+{ -+ st_logf("GetSessionInfo\n"); -+ -+ VERIFY_SESSION_HANDLE(hSession, NULL); -+ -+ memset(pInfo, 20, sizeof(*pInfo)); -+ -+ pInfo->slotID = 1; -+ if (soft_token.flags.login_done) -+ pInfo->state = CKS_RO_USER_FUNCTIONS; -+ else -+ pInfo->state = CKS_RO_PUBLIC_SESSION; -+ pInfo->flags = CKF_SERIAL_SESSION; -+ pInfo->ulDeviceError = 0; -+ -+ return CKR_OK; -+} -+ -+CK_RV -+C_Login(CK_SESSION_HANDLE hSession, -+ CK_USER_TYPE userType, -+ CK_UTF8CHAR_PTR pPin, -+ CK_ULONG ulPinLen) -+{ -+ char *pin = NULL; -+ int i; -+ -+ st_logf("Login\n"); -+ -+ VERIFY_SESSION_HANDLE(hSession, NULL); -+ -+ if (pPin != NULL_PTR) { -+ asprintf(&pin, "%.*s", (int)ulPinLen, pPin); -+ st_logf("type: %d password: %s\n", (int)userType, pin); -+ } -+ -+ for (i = 0; i < soft_token.object.num_objs; i++) { -+ struct st_object *o = soft_token.object.objs[i]; -+ FILE *f; -+ -+ if (o->type != STO_T_PRIVATE_KEY) -+ continue; -+ -+ if (o->u.private_key.key) -+ continue; -+ -+ f = fopen(o->u.private_key.file, "r"); -+ if (f == NULL) { -+ st_logf("can't open private file: %s\n", o->u.private_key.file); -+ continue; -+ } -+ -+ o->u.private_key.key = PEM_read_PrivateKey(f, NULL, NULL, pin); -+ fclose(f); -+ if (o->u.private_key.key == NULL) { -+ st_logf("failed to read key: %s error: %s\n", -+ o->u.private_key.file, -+ ERR_error_string(ERR_get_error(), NULL)); -+ /* just ignore failure */; -+ continue; -+ } -+ -+ /* XXX check keytype */ -+ RSA *rsa = EVP_PKEY_get0_RSA(o->u.private_key.key); -+ RSA_set_method(rsa, RSA_PKCS1_OpenSSL()); -+ -+ if (X509_check_private_key(o->u.private_key.cert, o->u.private_key.key) != 1) { -+ EVP_PKEY_free(o->u.private_key.key); -+ o->u.private_key.key = NULL; -+ st_logf("private key %s doesn't verify\n", o->u.private_key.file); -+ continue; -+ } -+ -+ soft_token.flags.login_done = 1; -+ } -+ free(pin); -+ -+ return soft_token.flags.login_done ? CKR_OK : CKR_PIN_INCORRECT; -+} -+ -+CK_RV -+C_Logout(CK_SESSION_HANDLE hSession) -+{ -+ st_logf("Logout\n"); -+ VERIFY_SESSION_HANDLE(hSession, NULL); -+ return CKR_FUNCTION_NOT_SUPPORTED; -+} -+ -+CK_RV -+C_GetObjectSize(CK_SESSION_HANDLE hSession, -+ CK_OBJECT_HANDLE hObject, -+ CK_ULONG_PTR pulSize) -+{ -+ st_logf("GetObjectSize\n"); -+ VERIFY_SESSION_HANDLE(hSession, NULL); -+ return CKR_FUNCTION_NOT_SUPPORTED; -+} -+ -+CK_RV -+C_GetAttributeValue(CK_SESSION_HANDLE hSession, -+ CK_OBJECT_HANDLE hObject, -+ CK_ATTRIBUTE_PTR pTemplate, -+ CK_ULONG ulCount) -+{ -+ struct session_state *state; -+ struct st_object *obj; -+ CK_ULONG i; -+ CK_RV ret; -+ int j; -+ -+ st_logf("GetAttributeValue: %lx\n", -+ (unsigned long)HANDLE_OBJECT_ID(hObject)); -+ VERIFY_SESSION_HANDLE(hSession, &state); -+ -+ if ((ret = object_handle_to_object(hObject, &obj)) != CKR_OK) { -+ st_logf("object not found: %lx\n", -+ (unsigned long)HANDLE_OBJECT_ID(hObject)); -+ return ret; -+ } -+ -+ ret = CKR_OK; -+ for (i = 0; i < ulCount; i++) { -+ st_logf(" getting 0x%08lx\n", (unsigned long)pTemplate[i].type); -+ for (j = 0; j < obj->num_attributes; j++) { -+ if (obj->attrs[j].secret) { -+ pTemplate[i].ulValueLen = (CK_ULONG)-1; -+ break; -+ } -+ if (pTemplate[i].type == obj->attrs[j].attribute.type) { -+ if (pTemplate[i].pValue != NULL_PTR && obj->attrs[j].secret == 0) { -+ if (pTemplate[i].ulValueLen >= obj->attrs[j].attribute.ulValueLen) -+ memcpy(pTemplate[i].pValue, obj->attrs[j].attribute.pValue, -+ obj->attrs[j].attribute.ulValueLen); -+ } -+ pTemplate[i].ulValueLen = obj->attrs[j].attribute.ulValueLen; -+ break; -+ } -+ } -+ if (j == obj->num_attributes) { -+ st_logf("key type: 0x%08lx not found\n", (unsigned long)pTemplate[i].type); -+ pTemplate[i].ulValueLen = (CK_ULONG)-1; -+ ret = CKR_ATTRIBUTE_TYPE_INVALID; -+ } -+ -+ } -+ return ret; -+} -+ -+CK_RV -+C_FindObjectsInit(CK_SESSION_HANDLE hSession, -+ CK_ATTRIBUTE_PTR pTemplate, -+ CK_ULONG ulCount) -+{ -+ struct session_state *state; -+ -+ st_logf("FindObjectsInit\n"); -+ -+ VERIFY_SESSION_HANDLE(hSession, &state); -+ -+ if (state->find.next_object != -1) { -+ application_error("application didn't do C_FindObjectsFinal\n"); -+ find_object_final(state); -+ } -+ if (ulCount) { -+ CK_ULONG i; -+ -+ print_attributes(pTemplate, ulCount); -+ -+ state->find.attributes = -+ calloc(1, ulCount * sizeof(state->find.attributes[0])); -+ if (state->find.attributes == NULL) -+ return CKR_DEVICE_MEMORY; -+ for (i = 0; i < ulCount; i++) { -+ state->find.attributes[i].pValue = -+ malloc(pTemplate[i].ulValueLen); -+ if (state->find.attributes[i].pValue == NULL) { -+ find_object_final(state); -+ return CKR_DEVICE_MEMORY; -+ } -+ memcpy(state->find.attributes[i].pValue, -+ pTemplate[i].pValue, pTemplate[i].ulValueLen); -+ state->find.attributes[i].type = pTemplate[i].type; -+ state->find.attributes[i].ulValueLen = pTemplate[i].ulValueLen; -+ } -+ state->find.num_attributes = ulCount; -+ state->find.next_object = 0; -+ } else { -+ st_logf("find all objects\n"); -+ state->find.attributes = NULL; -+ state->find.num_attributes = 0; -+ state->find.next_object = 0; -+ } -+ -+ return CKR_OK; -+} -+ -+CK_RV -+C_FindObjects(CK_SESSION_HANDLE hSession, -+ CK_OBJECT_HANDLE_PTR phObject, -+ CK_ULONG ulMaxObjectCount, -+ CK_ULONG_PTR pulObjectCount) -+{ -+ struct session_state *state; -+ int i; -+ -+ st_logf("FindObjects\n"); -+ -+ VERIFY_SESSION_HANDLE(hSession, &state); -+ -+ if (state->find.next_object == -1) { -+ application_error("application didn't do C_FindObjectsInit\n"); -+ return CKR_ARGUMENTS_BAD; -+ } -+ if (ulMaxObjectCount == 0) { -+ application_error("application asked for 0 objects\n"); -+ return CKR_ARGUMENTS_BAD; -+ } -+ *pulObjectCount = 0; -+ for (i = state->find.next_object; i < soft_token.object.num_objs; i++) { -+ st_logf("FindObjects: %d\n", i); -+ state->find.next_object = i + 1; -+ if (attributes_match(soft_token.object.objs[i], -+ state->find.attributes, -+ state->find.num_attributes)) { -+ *phObject++ = soft_token.object.objs[i]->object_handle; -+ ulMaxObjectCount--; -+ (*pulObjectCount)++; -+ if (ulMaxObjectCount == 0) -+ break; -+ } -+ } -+ return CKR_OK; -+} -+ -+CK_RV -+C_FindObjectsFinal(CK_SESSION_HANDLE hSession) -+{ -+ struct session_state *state; -+ -+ st_logf("FindObjectsFinal\n"); -+ VERIFY_SESSION_HANDLE(hSession, &state); -+ find_object_final(state); -+ return CKR_OK; -+} -+ -+static CK_RV -+commonInit(CK_ATTRIBUTE *attr_match, int attr_match_len, -+ const CK_MECHANISM_TYPE *mechs, int mechs_len, -+ const CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey, -+ struct st_object **o) -+{ -+ CK_RV ret; -+ int i; -+ -+ *o = NULL; -+ if ((ret = object_handle_to_object(hKey, o)) != CKR_OK) -+ return ret; -+ -+ ret = attributes_match(*o, attr_match, attr_match_len); -+ if (!ret) { -+ application_error("called commonInit on key that doesn't " -+ "support required attr"); -+ return CKR_ARGUMENTS_BAD; -+ } -+ -+ for (i = 0; i < mechs_len; i++) -+ if (mechs[i] == pMechanism->mechanism) -+ break; -+ if (i == mechs_len) { -+ application_error("called mech (%08lx) not supported\n", -+ pMechanism->mechanism); -+ return CKR_ARGUMENTS_BAD; -+ } -+ return CKR_OK; -+} -+ -+ -+static CK_RV -+dup_mechanism(CK_MECHANISM_PTR *dup, const CK_MECHANISM_PTR pMechanism) -+{ -+ CK_MECHANISM_PTR p; -+ -+ p = malloc(sizeof(*p)); -+ if (p == NULL) -+ return CKR_DEVICE_MEMORY; -+ -+ if (*dup) -+ free(*dup); -+ *dup = p; -+ memcpy(p, pMechanism, sizeof(*p)); -+ -+ return CKR_OK; -+} -+ -+ -+CK_RV -+C_EncryptInit(CK_SESSION_HANDLE hSession, -+ CK_MECHANISM_PTR pMechanism, -+ CK_OBJECT_HANDLE hKey) -+{ -+ struct session_state *state; -+ CK_MECHANISM_TYPE mechs[] = { CKM_RSA_PKCS, CKM_RSA_X_509 }; -+ CK_BBOOL bool_true = CK_TRUE; -+ CK_ATTRIBUTE attr[] = { -+ { CKA_ENCRYPT, &bool_true, sizeof(bool_true) } -+ }; -+ struct st_object *o; -+ CK_RV ret; -+ -+ st_logf("EncryptInit\n"); -+ VERIFY_SESSION_HANDLE(hSession, &state); -+ -+ ret = commonInit(attr, sizeof(attr)/sizeof(attr[0]), -+ mechs, sizeof(mechs)/sizeof(mechs[0]), -+ pMechanism, hKey, &o); -+ if (ret) -+ return ret; -+ -+ ret = dup_mechanism(&state->encrypt_mechanism, pMechanism); -+ if (ret == CKR_OK) -+ state->encrypt_object = OBJECT_ID(o); -+ -+ return ret; -+} -+ -+CK_RV -+C_Encrypt(CK_SESSION_HANDLE hSession, -+ CK_BYTE_PTR pData, -+ CK_ULONG ulDataLen, -+ CK_BYTE_PTR pEncryptedData, -+ CK_ULONG_PTR pulEncryptedDataLen) -+{ -+ struct session_state *state; -+ struct st_object *o; -+ void *buffer = NULL; -+ CK_RV ret; -+ RSA *rsa; -+ int padding, len, buffer_len, padding_len; -+ -+ st_logf("Encrypt\n"); -+ -+ VERIFY_SESSION_HANDLE(hSession, &state); -+ -+ if (state->encrypt_object == -1) -+ return CKR_ARGUMENTS_BAD; -+ -+ o = soft_token.object.objs[state->encrypt_object]; -+ -+ if (o->u.public_key == NULL) { -+ st_logf("public key NULL\n"); -+ return CKR_ARGUMENTS_BAD; -+ } -+ -+ rsa = EVP_PKEY_get0_RSA(o->u.public_key); -+ if (rsa == NULL) -+ return CKR_ARGUMENTS_BAD; -+ -+ RSA_blinding_off(rsa); /* XXX RAND is broken while running in mozilla ? */ -+ -+ buffer_len = RSA_size(rsa); -+ -+ buffer = malloc(buffer_len); -+ if (buffer == NULL) { -+ ret = CKR_DEVICE_MEMORY; -+ goto out; -+ } -+ -+ ret = CKR_OK; -+ switch(state->encrypt_mechanism->mechanism) { -+ case CKM_RSA_PKCS: -+ padding = RSA_PKCS1_PADDING; -+ padding_len = RSA_PKCS1_PADDING_SIZE; -+ break; -+ case CKM_RSA_X_509: -+ padding = RSA_NO_PADDING; -+ padding_len = 0; -+ break; -+ default: -+ ret = CKR_FUNCTION_NOT_SUPPORTED; -+ goto out; -+ } -+ -+ if (buffer_len + padding_len < (long) ulDataLen) { -+ ret = CKR_ARGUMENTS_BAD; -+ goto out; -+ } -+ -+ if (pulEncryptedDataLen == NULL) { -+ st_logf("pulEncryptedDataLen NULL\n"); -+ ret = CKR_ARGUMENTS_BAD; -+ goto out; -+ } -+ -+ if (pData == NULL_PTR) { -+ st_logf("data NULL\n"); -+ ret = CKR_ARGUMENTS_BAD; -+ goto out; -+ } -+ -+ len = RSA_public_encrypt(ulDataLen, pData, buffer, rsa, padding); -+ if (len <= 0) { -+ ret = CKR_DEVICE_ERROR; -+ goto out; -+ } -+ if (len > buffer_len) -+ abort(); -+ -+ if (pEncryptedData != NULL_PTR) -+ memcpy(pEncryptedData, buffer, len); -+ *pulEncryptedDataLen = len; -+ -+ out: -+ if (buffer) { -+ memset(buffer, 0, buffer_len); -+ free(buffer); -+ } -+ return ret; -+} -+ -+CK_RV -+C_EncryptUpdate(CK_SESSION_HANDLE hSession, -+ CK_BYTE_PTR pPart, -+ CK_ULONG ulPartLen, -+ CK_BYTE_PTR pEncryptedPart, -+ CK_ULONG_PTR pulEncryptedPartLen) -+{ -+ st_logf("EncryptUpdate\n"); -+ VERIFY_SESSION_HANDLE(hSession, NULL); -+ return CKR_FUNCTION_NOT_SUPPORTED; -+} -+ -+ -+CK_RV -+C_EncryptFinal(CK_SESSION_HANDLE hSession, -+ CK_BYTE_PTR pLastEncryptedPart, -+ CK_ULONG_PTR pulLastEncryptedPartLen) -+{ -+ st_logf("EncryptFinal\n"); -+ VERIFY_SESSION_HANDLE(hSession, NULL); -+ return CKR_FUNCTION_NOT_SUPPORTED; -+} -+ -+ -+/* C_DecryptInit initializes a decryption operation. */ -+CK_RV -+C_DecryptInit(CK_SESSION_HANDLE hSession, -+ CK_MECHANISM_PTR pMechanism, -+ CK_OBJECT_HANDLE hKey) -+{ -+ struct session_state *state; -+ CK_MECHANISM_TYPE mechs[] = { CKM_RSA_PKCS, CKM_RSA_X_509 }; -+ CK_BBOOL bool_true = CK_TRUE; -+ CK_ATTRIBUTE attr[] = { -+ { CKA_DECRYPT, &bool_true, sizeof(bool_true) } -+ }; -+ struct st_object *o; -+ CK_RV ret; -+ -+ st_logf("DecryptInit\n"); -+ VERIFY_SESSION_HANDLE(hSession, &state); -+ -+ ret = commonInit(attr, sizeof(attr)/sizeof(attr[0]), -+ mechs, sizeof(mechs)/sizeof(mechs[0]), -+ pMechanism, hKey, &o); -+ if (ret) -+ return ret; -+ -+ ret = dup_mechanism(&state->decrypt_mechanism, pMechanism); -+ if (ret == CKR_OK) -+ state->decrypt_object = OBJECT_ID(o); -+ -+ return CKR_OK; -+} -+ -+ -+CK_RV -+C_Decrypt(CK_SESSION_HANDLE hSession, -+ CK_BYTE_PTR pEncryptedData, -+ CK_ULONG ulEncryptedDataLen, -+ CK_BYTE_PTR pData, -+ CK_ULONG_PTR pulDataLen) -+{ -+ struct session_state *state; -+ struct st_object *o; -+ void *buffer = NULL; -+ CK_RV ret; -+ RSA *rsa; -+ int padding, len, buffer_len, padding_len; -+ -+ st_logf("Decrypt\n"); -+ -+ VERIFY_SESSION_HANDLE(hSession, &state); -+ -+ if (state->decrypt_object == -1) -+ return CKR_ARGUMENTS_BAD; -+ -+ o = soft_token.object.objs[state->decrypt_object]; -+ -+ if (o->u.private_key.key == NULL) { -+ st_logf("private key NULL\n"); -+ return CKR_ARGUMENTS_BAD; -+ } -+ -+ rsa = EVP_PKEY_get0_RSA(o->u.private_key.key); -+ if (rsa == NULL) -+ return CKR_ARGUMENTS_BAD; -+ -+ RSA_blinding_off(rsa); /* XXX RAND is broken while running in mozilla ? */ -+ -+ buffer_len = RSA_size(rsa); -+ -+ buffer = malloc(buffer_len); -+ if (buffer == NULL) { -+ ret = CKR_DEVICE_MEMORY; -+ goto out; -+ } -+ -+ ret = CKR_OK; -+ switch(state->decrypt_mechanism->mechanism) { -+ case CKM_RSA_PKCS: -+ padding = RSA_PKCS1_PADDING; -+ padding_len = RSA_PKCS1_PADDING_SIZE; -+ break; -+ case CKM_RSA_X_509: -+ padding = RSA_NO_PADDING; -+ padding_len = 0; -+ break; -+ default: -+ ret = CKR_FUNCTION_NOT_SUPPORTED; -+ goto out; -+ } -+ -+ if (buffer_len + padding_len < (long) ulEncryptedDataLen) { -+ ret = CKR_ARGUMENTS_BAD; -+ goto out; -+ } -+ -+ if (pulDataLen == NULL) { -+ st_logf("pulDataLen NULL\n"); -+ ret = CKR_ARGUMENTS_BAD; -+ goto out; -+ } -+ -+ if (pEncryptedData == NULL_PTR) { -+ st_logf("data NULL\n"); -+ ret = CKR_ARGUMENTS_BAD; -+ goto out; -+ } -+ -+ len = RSA_private_decrypt(ulEncryptedDataLen, pEncryptedData, buffer, -+ rsa, padding); -+ if (len <= 0) { -+ ret = CKR_DEVICE_ERROR; -+ goto out; -+ } -+ if (len > buffer_len) -+ abort(); -+ -+ if (pData != NULL_PTR) -+ memcpy(pData, buffer, len); -+ *pulDataLen = len; -+ -+ out: -+ if (buffer) { -+ memset(buffer, 0, buffer_len); -+ free(buffer); -+ } -+ return ret; -+} -+ -+ -+CK_RV -+C_DecryptUpdate(CK_SESSION_HANDLE hSession, -+ CK_BYTE_PTR pEncryptedPart, -+ CK_ULONG ulEncryptedPartLen, -+ CK_BYTE_PTR pPart, -+ CK_ULONG_PTR pulPartLen) -+ -+{ -+ st_logf("DecryptUpdate\n"); -+ VERIFY_SESSION_HANDLE(hSession, NULL); -+ return CKR_FUNCTION_NOT_SUPPORTED; -+} -+ -+ -+CK_RV -+C_DecryptFinal(CK_SESSION_HANDLE hSession, -+ CK_BYTE_PTR pLastPart, -+ CK_ULONG_PTR pulLastPartLen) -+{ -+ st_logf("DecryptFinal\n"); -+ VERIFY_SESSION_HANDLE(hSession, NULL); -+ return CKR_FUNCTION_NOT_SUPPORTED; -+} -+ -+CK_RV -+C_DigestInit(CK_SESSION_HANDLE hSession, -+ CK_MECHANISM_PTR pMechanism) -+{ -+ st_logf("DigestInit\n"); -+ VERIFY_SESSION_HANDLE(hSession, NULL); -+ return CKR_FUNCTION_NOT_SUPPORTED; -+} -+ -+CK_RV -+C_SignInit(CK_SESSION_HANDLE hSession, -+ CK_MECHANISM_PTR pMechanism, -+ CK_OBJECT_HANDLE hKey) -+{ -+ struct session_state *state; -+ CK_MECHANISM_TYPE mechs[] = { CKM_RSA_PKCS, CKM_RSA_X_509 }; -+ CK_BBOOL bool_true = CK_TRUE; -+ CK_ATTRIBUTE attr[] = { -+ { CKA_SIGN, &bool_true, sizeof(bool_true) } -+ }; -+ struct st_object *o; -+ CK_RV ret; -+ -+ st_logf("SignInit\n"); -+ VERIFY_SESSION_HANDLE(hSession, &state); -+ -+ ret = commonInit(attr, sizeof(attr)/sizeof(attr[0]), -+ mechs, sizeof(mechs)/sizeof(mechs[0]), -+ pMechanism, hKey, &o); -+ if (ret) -+ return ret; -+ -+ ret = dup_mechanism(&state->sign_mechanism, pMechanism); -+ if (ret == CKR_OK) -+ state->sign_object = OBJECT_ID(o); -+ -+ return CKR_OK; -+} -+ -+CK_RV -+C_Sign(CK_SESSION_HANDLE hSession, -+ CK_BYTE_PTR pData, -+ CK_ULONG ulDataLen, -+ CK_BYTE_PTR pSignature, -+ CK_ULONG_PTR pulSignatureLen) -+{ -+ struct session_state *state; -+ struct st_object *o; -+ void *buffer = NULL; -+ CK_RV ret; -+ RSA *rsa; -+ int padding, len, buffer_len; -+ size_t padding_len; -+ -+ st_logf("Sign\n"); -+ VERIFY_SESSION_HANDLE(hSession, &state); -+ -+ if (state->sign_object == -1) -+ return CKR_ARGUMENTS_BAD; -+ -+ o = soft_token.object.objs[state->sign_object]; -+ -+ if (o->u.private_key.key == NULL) { -+ st_logf("private key NULL\n"); -+ return CKR_ARGUMENTS_BAD; -+ } -+ -+ rsa = EVP_PKEY_get0_RSA(o->u.private_key.key); -+ if (rsa == NULL) -+ return CKR_ARGUMENTS_BAD; -+ -+ RSA_blinding_off(rsa); /* XXX RAND is broken while running in mozilla ? */ -+ -+ buffer_len = RSA_size(rsa); -+ -+ buffer = malloc(buffer_len); -+ if (buffer == NULL) { -+ ret = CKR_DEVICE_MEMORY; -+ goto out; -+ } -+ -+ switch(state->sign_mechanism->mechanism) { -+ case CKM_RSA_PKCS: -+ padding = RSA_PKCS1_PADDING; -+ padding_len = RSA_PKCS1_PADDING_SIZE; -+ break; -+ case CKM_RSA_X_509: -+ padding = RSA_NO_PADDING; -+ padding_len = 0; -+ break; -+ default: -+ ret = CKR_FUNCTION_NOT_SUPPORTED; -+ goto out; -+ } -+ -+ if ((size_t) buffer_len < ulDataLen + padding_len) { -+ ret = CKR_ARGUMENTS_BAD; -+ goto out; -+ } -+ -+ if (pulSignatureLen == NULL) { -+ st_logf("signature len NULL\n"); -+ ret = CKR_ARGUMENTS_BAD; -+ goto out; -+ } -+ -+ if (pData == NULL_PTR) { -+ st_logf("data NULL\n"); -+ ret = CKR_ARGUMENTS_BAD; -+ goto out; -+ } -+ -+ len = RSA_private_encrypt(ulDataLen, pData, buffer, rsa, padding); -+ st_logf("private encrypt done\n"); -+ if (len <= 0) { -+ ret = CKR_DEVICE_ERROR; -+ goto out; -+ } -+ if (len > buffer_len) -+ abort(); -+ -+ if (pSignature != NULL_PTR) -+ memcpy(pSignature, buffer, len); -+ *pulSignatureLen = len; -+ -+ ret = CKR_OK; -+ -+ out: -+ if (buffer) { -+ memset(buffer, 0, buffer_len); -+ free(buffer); -+ } -+ return ret; -+} -+ -+CK_RV -+C_SignUpdate(CK_SESSION_HANDLE hSession, -+ CK_BYTE_PTR pPart, -+ CK_ULONG ulPartLen) -+{ -+ st_logf("SignUpdate\n"); -+ VERIFY_SESSION_HANDLE(hSession, NULL); -+ return CKR_FUNCTION_NOT_SUPPORTED; -+} -+ -+ -+CK_RV -+C_SignFinal(CK_SESSION_HANDLE hSession, -+ CK_BYTE_PTR pSignature, -+ CK_ULONG_PTR pulSignatureLen) -+{ -+ st_logf("SignUpdate\n"); -+ VERIFY_SESSION_HANDLE(hSession, NULL); -+ return CKR_FUNCTION_NOT_SUPPORTED; -+} -+ -+CK_RV -+C_VerifyInit(CK_SESSION_HANDLE hSession, -+ CK_MECHANISM_PTR pMechanism, -+ CK_OBJECT_HANDLE hKey) -+{ -+ struct session_state *state; -+ CK_MECHANISM_TYPE mechs[] = { CKM_RSA_PKCS, CKM_RSA_X_509 }; -+ CK_BBOOL bool_true = CK_TRUE; -+ CK_ATTRIBUTE attr[] = { -+ { CKA_VERIFY, &bool_true, sizeof(bool_true) } -+ }; -+ struct st_object *o; -+ CK_RV ret; -+ -+ st_logf("VerifyInit\n"); -+ VERIFY_SESSION_HANDLE(hSession, &state); -+ -+ ret = commonInit(attr, sizeof(attr)/sizeof(attr[0]), -+ mechs, sizeof(mechs)/sizeof(mechs[0]), -+ pMechanism, hKey, &o); -+ if (ret) -+ return ret; -+ -+ ret = dup_mechanism(&state->verify_mechanism, pMechanism); -+ if (ret == CKR_OK) -+ state->verify_object = OBJECT_ID(o); -+ -+ return ret; -+} -+ -+CK_RV -+C_Verify(CK_SESSION_HANDLE hSession, -+ CK_BYTE_PTR pData, -+ CK_ULONG ulDataLen, -+ CK_BYTE_PTR pSignature, -+ CK_ULONG ulSignatureLen) -+{ -+ struct session_state *state; -+ struct st_object *o; -+ void *buffer = NULL; -+ CK_RV ret; -+ RSA *rsa; -+ int padding, len, buffer_len; -+ -+ st_logf("Verify\n"); -+ VERIFY_SESSION_HANDLE(hSession, &state); -+ -+ if (state->verify_object == -1) -+ return CKR_ARGUMENTS_BAD; -+ -+ o = soft_token.object.objs[state->verify_object]; -+ -+ if (o->u.public_key == NULL) { -+ st_logf("public key NULL\n"); -+ return CKR_ARGUMENTS_BAD; -+ } -+ -+ rsa = EVP_PKEY_get0_RSA(o->u.public_key); -+ if (rsa == NULL) -+ return CKR_ARGUMENTS_BAD; -+ -+ RSA_blinding_off(rsa); /* XXX RAND is broken while running in mozilla ? */ -+ -+ buffer_len = RSA_size(rsa); -+ -+ buffer = malloc(buffer_len); -+ if (buffer == NULL) { -+ ret = CKR_DEVICE_MEMORY; -+ goto out; -+ } -+ -+ ret = CKR_OK; -+ switch(state->verify_mechanism->mechanism) { -+ case CKM_RSA_PKCS: -+ padding = RSA_PKCS1_PADDING; -+ break; -+ case CKM_RSA_X_509: -+ padding = RSA_NO_PADDING; -+ break; -+ default: -+ ret = CKR_FUNCTION_NOT_SUPPORTED; -+ goto out; -+ } -+ -+ if (buffer_len < (long) ulDataLen) { -+ ret = CKR_ARGUMENTS_BAD; -+ goto out; -+ } -+ -+ if (pSignature == NULL) { -+ st_logf("signature NULL\n"); -+ ret = CKR_ARGUMENTS_BAD; -+ goto out; -+ } -+ -+ if (pData == NULL_PTR) { -+ st_logf("data NULL\n"); -+ ret = CKR_ARGUMENTS_BAD; -+ goto out; -+ } -+ -+ len = RSA_public_decrypt(ulDataLen, pData, buffer, rsa, padding); -+ st_logf("private encrypt done\n"); -+ if (len <= 0) { -+ ret = CKR_DEVICE_ERROR; -+ goto out; -+ } -+ if (len > buffer_len) -+ abort(); -+ -+ if ((size_t) len != ulSignatureLen) { -+ ret = CKR_GENERAL_ERROR; -+ goto out; -+ } -+ -+ if (memcmp(pSignature, buffer, len) != 0) { -+ ret = CKR_GENERAL_ERROR; -+ goto out; -+ } -+ -+ out: -+ if (buffer) { -+ memset(buffer, 0, buffer_len); -+ free(buffer); -+ } -+ return ret; -+} -+ -+ -+CK_RV -+C_VerifyUpdate(CK_SESSION_HANDLE hSession, -+ CK_BYTE_PTR pPart, -+ CK_ULONG ulPartLen) -+{ -+ st_logf("VerifyUpdate\n"); -+ VERIFY_SESSION_HANDLE(hSession, NULL); -+ return CKR_FUNCTION_NOT_SUPPORTED; -+} -+ -+CK_RV -+C_VerifyFinal(CK_SESSION_HANDLE hSession, -+ CK_BYTE_PTR pSignature, -+ CK_ULONG ulSignatureLen) -+{ -+ st_logf("VerifyFinal\n"); -+ VERIFY_SESSION_HANDLE(hSession, NULL); -+ return CKR_FUNCTION_NOT_SUPPORTED; -+} -+ -+CK_RV -+C_GenerateRandom(CK_SESSION_HANDLE hSession, -+ CK_BYTE_PTR RandomData, -+ CK_ULONG ulRandomLen) -+{ -+ st_logf("GenerateRandom\n"); -+ VERIFY_SESSION_HANDLE(hSession, NULL); -+ return CKR_FUNCTION_NOT_SUPPORTED; -+} -+ -+ -+CK_FUNCTION_LIST funcs = { -+ { 2, 11 }, -+ C_Initialize, -+ C_Finalize, -+ C_GetInfo, -+ C_GetFunctionList, -+ C_GetSlotList, -+ C_GetSlotInfo, -+ C_GetTokenInfo, -+ C_GetMechanismList, -+ C_GetMechanismInfo, -+ C_InitToken, -+ (void *)func_not_supported, /* C_InitPIN */ -+ (void *)func_not_supported, /* C_SetPIN */ -+ C_OpenSession, -+ C_CloseSession, -+ C_CloseAllSessions, -+ C_GetSessionInfo, -+ (void *)func_not_supported, /* C_GetOperationState */ -+ (void *)func_not_supported, /* C_SetOperationState */ -+ C_Login, -+ C_Logout, -+ (void *)func_not_supported, /* C_CreateObject */ -+ (void *)func_not_supported, /* C_CopyObject */ -+ (void *)func_not_supported, /* C_DestroyObject */ -+ (void *)func_not_supported, /* C_GetObjectSize */ -+ C_GetAttributeValue, -+ (void *)func_not_supported, /* C_SetAttributeValue */ -+ C_FindObjectsInit, -+ C_FindObjects, -+ C_FindObjectsFinal, -+ C_EncryptInit, -+ C_Encrypt, -+ C_EncryptUpdate, -+ C_EncryptFinal, -+ C_DecryptInit, -+ C_Decrypt, -+ C_DecryptUpdate, -+ C_DecryptFinal, -+ C_DigestInit, -+ (void *)func_not_supported, /* C_Digest */ -+ (void *)func_not_supported, /* C_DigestUpdate */ -+ (void *)func_not_supported, /* C_DigestKey */ -+ (void *)func_not_supported, /* C_DigestFinal */ -+ C_SignInit, -+ C_Sign, -+ C_SignUpdate, -+ C_SignFinal, -+ (void *)func_not_supported, /* C_SignRecoverInit */ -+ (void *)func_not_supported, /* C_SignRecover */ -+ C_VerifyInit, -+ C_Verify, -+ C_VerifyUpdate, -+ C_VerifyFinal, -+ (void *)func_not_supported, /* C_VerifyRecoverInit */ -+ (void *)func_not_supported, /* C_VerifyRecover */ -+ (void *)func_not_supported, /* C_DigestEncryptUpdate */ -+ (void *)func_not_supported, /* C_DecryptDigestUpdate */ -+ (void *)func_not_supported, /* C_SignEncryptUpdate */ -+ (void *)func_not_supported, /* C_DecryptVerifyUpdate */ -+ (void *)func_not_supported, /* C_GenerateKey */ -+ (void *)func_not_supported, /* C_GenerateKeyPair */ -+ (void *)func_not_supported, /* C_WrapKey */ -+ (void *)func_not_supported, /* C_UnwrapKey */ -+ (void *)func_not_supported, /* C_DeriveKey */ -+ (void *)func_not_supported, /* C_SeedRandom */ -+ C_GenerateRandom, -+ (void *)func_not_supported, /* C_GetFunctionStatus */ -+ (void *)func_not_supported, /* C_CancelFunction */ -+ (void *)func_not_supported /* C_WaitForSlotEvent */ -+}; -diff --git a/regress/unittests/Makefile b/regress/unittests/Makefile -index e464b085..a0e5a37c 100644 ---- a/regress/unittests/Makefile -+++ b/regress/unittests/Makefile -@@ -2,6 +2,6 @@ - - REGRESS_FAIL_EARLY?= yes - SUBDIR= test_helper sshbuf sshkey bitmap kex hostkeys utf8 match conversion --SUBDIR+=authopt -+SUBDIR+=pkcs11 authopt - - .include -diff --git a/regress/unittests/pkcs11/Makefile b/regress/unittests/pkcs11/Makefile -new file mode 100644 -index 00000000..481b13d0 ---- /dev/null -+++ b/regress/unittests/pkcs11/Makefile -@@ -0,0 +1,9 @@ -+ -+PROG=test_pkcs11 -+SRCS=tests.c -+REGRESS_TARGETS=run-regress-${PROG} -+ -+run-regress-${PROG}: ${PROG} -+ env ${TEST_ENV} ./${PROG} -+ -+.include -diff --git a/regress/unittests/pkcs11/tests.c b/regress/unittests/pkcs11/tests.c -new file mode 100644 -index 00000000..e83aca54 ---- /dev/null -+++ b/regress/unittests/pkcs11/tests.c -@@ -0,0 +1,330 @@ -+/* -+ * Copyright (c) 2017 Red Hat -+ * -+ * Authors: Jakub Jelen -+ * -+ * 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 "../test_helper/test_helper.h" -+ -+#include "sshbuf.h" -+#include "ssh-pkcs11-uri.h" -+ -+#define EMPTY_URI compose_uri(NULL, 0, NULL, NULL, NULL, NULL, NULL) -+ -+/* prototypes are not public -- specify them here internally for tests */ -+struct sshbuf *percent_encode(const char *, size_t, char *); -+int percent_decode(char *, char **); -+ -+void -+compare_uri(struct pkcs11_uri *a, struct pkcs11_uri *b) -+{ -+ ASSERT_PTR_NE(a, NULL); -+ ASSERT_PTR_NE(b, NULL); -+ ASSERT_SIZE_T_EQ(a->id_len, b->id_len); -+ ASSERT_MEM_EQ(a->id, b->id, a->id_len); -+ if (b->object != NULL) -+ ASSERT_STRING_EQ(a->object, b->object); -+ else /* both should be null */ -+ ASSERT_PTR_EQ(a->object, b->object); -+ if (b->module_path != NULL) -+ ASSERT_STRING_EQ(a->module_path, b->module_path); -+ else /* both should be null */ -+ ASSERT_PTR_EQ(a->module_path, b->module_path); -+ if (b->token != NULL) -+ ASSERT_STRING_EQ(a->token, b->token); -+ else /* both should be null */ -+ ASSERT_PTR_EQ(a->token, b->token); -+ if (b->manuf != NULL) -+ ASSERT_STRING_EQ(a->manuf, b->manuf); -+ else /* both should be null */ -+ ASSERT_PTR_EQ(a->manuf, b->manuf); -+ if (b->lib_manuf != NULL) -+ ASSERT_STRING_EQ(a->lib_manuf, b->lib_manuf); -+ else /* both should be null */ -+ ASSERT_PTR_EQ(a->lib_manuf, b->lib_manuf); -+} -+ -+void -+check_parse_rv(char *uri, struct pkcs11_uri *expect, int expect_rv) -+{ -+ char *buf = NULL, *str; -+ struct pkcs11_uri *pkcs11uri = NULL; -+ int rv; -+ -+ if (expect_rv == 0) -+ str = "Valid"; -+ else -+ str = "Invalid"; -+ asprintf(&buf, "%s PKCS#11 URI parsing: %s", str, uri); -+ TEST_START(buf); -+ free(buf); -+ pkcs11uri = pkcs11_uri_init(); -+ rv = pkcs11_uri_parse(uri, pkcs11uri); -+ ASSERT_INT_EQ(rv, expect_rv); -+ if (rv == 0) /* in case of failure result is undefined */ -+ compare_uri(pkcs11uri, expect); -+ pkcs11_uri_cleanup(pkcs11uri); -+ free(expect); -+ TEST_DONE(); -+} -+ -+void -+check_parse(char *uri, struct pkcs11_uri *expect) -+{ -+ check_parse_rv(uri, expect, 0); -+} -+ -+struct pkcs11_uri * -+compose_uri(unsigned char *id, size_t id_len, char *token, char *lib_manuf, -+ char *manuf, char *module_path, char *object) -+{ -+ struct pkcs11_uri *uri = pkcs11_uri_init(); -+ if (id_len > 0) { -+ uri->id_len = id_len; -+ uri->id = id; -+ } -+ uri->module_path = module_path; -+ uri->token = token; -+ uri->lib_manuf = lib_manuf; -+ uri->manuf = manuf; -+ uri->object = object; -+ return uri; -+} -+ -+static void -+test_parse_valid(void) -+{ -+ /* path arguments */ -+ check_parse("pkcs11:id=%01", -+ compose_uri("\x01", 1, NULL, NULL, NULL, NULL, NULL)); -+ check_parse("pkcs11:id=%00%01", -+ compose_uri("\x00\x01", 2, NULL, NULL, NULL, NULL, NULL)); -+ check_parse("pkcs11:token=SSH%20Keys", -+ compose_uri(NULL, 0, "SSH Keys", NULL, NULL, NULL, NULL)); -+ check_parse("pkcs11:library-manufacturer=OpenSC", -+ compose_uri(NULL, 0, NULL, "OpenSC", NULL, NULL, NULL)); -+ check_parse("pkcs11:manufacturer=piv_II", -+ compose_uri(NULL, 0, NULL, NULL, "piv_II", NULL, NULL)); -+ check_parse("pkcs11:object=SIGN%20Key", -+ compose_uri(NULL, 0, NULL, NULL, NULL, NULL, "SIGN Key")); -+ /* query arguments */ -+ check_parse("pkcs11:?module-path=/usr/lib64/p11-kit-proxy.so", -+ compose_uri(NULL, 0, NULL, NULL, NULL, "/usr/lib64/p11-kit-proxy.so", NULL)); -+ -+ /* combinations */ -+ /* ID SHOULD be percent encoded */ -+ check_parse("pkcs11:token=SSH%20Key;id=0", -+ compose_uri("0", 1, "SSH Key", NULL, NULL, NULL, NULL)); -+ check_parse( -+ "pkcs11:manufacturer=CAC?module-path=/usr/lib64/p11-kit-proxy.so", -+ compose_uri(NULL, 0, NULL, NULL, "CAC", -+ "/usr/lib64/p11-kit-proxy.so", NULL)); -+ check_parse( -+ "pkcs11:object=RSA%20Key?module-path=/usr/lib64/pkcs11/opencryptoki.so", -+ compose_uri(NULL, 0, NULL, NULL, NULL, -+ "/usr/lib64/pkcs11/opencryptoki.so", "RSA Key")); -+ -+ /* empty path component matches everything */ -+ check_parse("pkcs11:", EMPTY_URI); -+ -+ /* empty string is a valid to match against (and different from NULL) */ -+ check_parse("pkcs11:token=", -+ compose_uri(NULL, 0, "", NULL, NULL, NULL, NULL)); -+ /* Percent character needs to be percent-encoded */ -+ check_parse("pkcs11:token=%25", -+ compose_uri(NULL, 0, "%", NULL, NULL, NULL, NULL)); -+} -+ -+static void -+test_parse_invalid(void) -+{ -+ /* Invalid percent encoding */ -+ check_parse_rv("pkcs11:id=%0", EMPTY_URI, -1); -+ /* Invalid percent encoding */ -+ check_parse_rv("pkcs11:id=%ZZ", EMPTY_URI, -1); -+ /* Space MUST be percent encoded -- XXX not enforced yet */ -+ check_parse("pkcs11:token=SSH Keys", -+ compose_uri(NULL, 0, "SSH Keys", NULL, NULL, NULL, NULL)); -+ /* MUST NOT contain duplicate attributes of the same name */ -+ check_parse_rv("pkcs11:id=%01;id=%02", EMPTY_URI, -1); -+ /* Unrecognized attribute in path SHOULD be error */ -+ check_parse_rv("pkcs11:key_name=SSH", EMPTY_URI, -1); -+ /* Unrecognized attribute in query SHOULD be ignored */ -+ check_parse("pkcs11:?key_name=SSH", EMPTY_URI); -+} -+ -+void -+check_gen(char *expect, struct pkcs11_uri *uri) -+{ -+ char *buf = NULL, *uri_str; -+ -+ asprintf(&buf, "Valid PKCS#11 URI generation: %s", expect); -+ TEST_START(buf); -+ free(buf); -+ uri_str = pkcs11_uri_get(uri); -+ ASSERT_PTR_NE(uri_str, NULL); -+ ASSERT_STRING_EQ(uri_str, expect); -+ free(uri_str); -+ TEST_DONE(); -+} -+ -+static void -+test_generate_valid(void) -+{ -+ /* path arguments */ -+ check_gen("pkcs11:id=%01", -+ compose_uri("\x01", 1, NULL, NULL, NULL, NULL, NULL)); -+ check_gen("pkcs11:id=%00%01", -+ compose_uri("\x00\x01", 2, NULL, NULL, NULL, NULL, NULL)); -+ check_gen("pkcs11:token=SSH%20Keys", /* space must be percent encoded */ -+ compose_uri(NULL, 0, "SSH Keys", NULL, NULL, NULL, NULL)); -+ /* library-manufacturer is not implmented now */ -+ /*check_gen("pkcs11:library-manufacturer=OpenSC", -+ compose_uri(NULL, 0, NULL, "OpenSC", NULL, NULL, NULL));*/ -+ check_gen("pkcs11:manufacturer=piv_II", -+ compose_uri(NULL, 0, NULL, NULL, "piv_II", NULL, NULL)); -+ check_gen("pkcs11:object=RSA%20Key", -+ compose_uri(NULL, 0, NULL, NULL, NULL, NULL, "RSA Key")); -+ /* query arguments */ -+ check_gen("pkcs11:?module-path=/usr/lib64/p11-kit-proxy.so", -+ compose_uri(NULL, 0, NULL, NULL, NULL, "/usr/lib64/p11-kit-proxy.so", NULL)); -+ -+ /* combinations */ -+ check_gen("pkcs11:id=%02;token=SSH%20Keys", -+ compose_uri("\x02", 1, "SSH Keys", NULL, NULL, NULL, NULL)); -+ check_gen("pkcs11:id=%EE%02?module-path=/usr/lib64/p11-kit-proxy.so", -+ compose_uri("\xEE\x02", 2, NULL, NULL, NULL, "/usr/lib64/p11-kit-proxy.so", NULL)); -+ check_gen("pkcs11:object=Encryption%20Key;manufacturer=piv_II", -+ compose_uri(NULL, 0, NULL, NULL, "piv_II", NULL, "Encryption Key")); -+ -+ /* empty path component matches everything */ -+ check_gen("pkcs11:", EMPTY_URI); -+ -+} -+ -+void -+check_encode(char *source, size_t len, char *whitelist, char *expect) -+{ -+ char *buf = NULL; -+ struct sshbuf *b; -+ -+ asprintf(&buf, "percent_encode: expected %s", expect); -+ TEST_START(buf); -+ free(buf); -+ -+ b = percent_encode(source, len, whitelist); -+ ASSERT_STRING_EQ(sshbuf_ptr(b), expect); -+ sshbuf_free(b); -+ TEST_DONE(); -+} -+ -+static void -+test_percent_encode_multibyte(void) -+{ -+ /* SHOULD be encoded as octets according to the UTF-8 character encoding */ -+ -+ /* multi-byte characters are "for free" */ -+ check_encode("$", 1, "", "%24"); -+ check_encode("¢", 2, "", "%C2%A2"); -+ check_encode("€", 3, "", "%E2%82%AC"); -+ check_encode("𐍈", 4, "", "%F0%90%8D%88"); -+ -+ /* CK_UTF8CHAR is unsigned char (1 byte) */ -+ /* labels SHOULD be normalized to NFC [UAX15] */ -+ -+} -+ -+static void -+test_percent_encode(void) -+{ -+ /* Without whitelist encodes everything (for CKA_ID) */ -+ check_encode("A*", 2, "", "%41%2A"); -+ check_encode("\x00", 1, "", "%00"); -+ check_encode("\x7F", 1, "", "%7F"); -+ check_encode("\x80", 1, "", "%80"); -+ check_encode("\xff", 1, "", "%FF"); -+ -+ /* Default whitelist encodes anything but safe letters */ -+ check_encode("test" "\x00" "0alpha", 11, PKCS11_URI_WHITELIST, -+ "test%000alpha"); -+ check_encode(" ", 1, PKCS11_URI_WHITELIST, -+ "%20"); /* Space MUST be percent encoded */ -+ check_encode("/", 1, PKCS11_URI_WHITELIST, -+ "%2F"); /* '/' delimiter MUST be percent encoded (in the path) */ -+ check_encode("?", 1, PKCS11_URI_WHITELIST, -+ "%3F"); /* delimiter '?' MUST be percent encoded (in the path) */ -+ check_encode("#", 1, PKCS11_URI_WHITELIST, -+ "%23"); /* '#' MUST be always percent encoded */ -+ check_encode("key=value;separator?query&#anch", 35, PKCS11_URI_WHITELIST, -+ "key%3Dvalue%3Bseparator%3Fquery%26amp%3B%23anch"); -+ -+ /* Components in query can have '/' unencoded (useful for paths) */ -+ check_encode("/path/to.file", 13, PKCS11_URI_WHITELIST "/", -+ "/path/to.file"); -+} -+ -+void -+check_decode(char *source, char *expect, int expect_len) -+{ -+ char *buf = NULL, *out = NULL; -+ int rv; -+ -+ asprintf(&buf, "percent_decode: %s", source); -+ TEST_START(buf); -+ free(buf); -+ -+ rv = percent_decode(source, &out); -+ ASSERT_INT_EQ(rv, expect_len); -+ if (rv >= 0) -+ ASSERT_MEM_EQ(out, expect, expect_len); -+ free(out); -+ TEST_DONE(); -+} -+ -+static void -+test_percent_decode(void) -+{ -+ /* simple valid cases */ -+ check_decode("%00", "\x00", 1); -+ check_decode("%FF", "\xFF", 1); -+ -+ /* normal strings shold be kept intact */ -+ check_decode("strings are left", "strings are left", 16); -+ check_decode("10%25 of trees", "10% of trees", 12); -+ -+ /* make sure no more than 2 bytes are parsed */ -+ check_decode("%222", "\x22" "2", 2); -+ -+ /* invalid expects failure */ -+ check_decode("%0", "", -1); -+ check_decode("%Z", "", -1); -+ check_decode("%FG", "", -1); -+} -+ -+void -+tests(void) -+{ -+ test_percent_encode(); -+ test_percent_encode_multibyte(); -+ test_percent_decode(); -+ test_parse_valid(); -+ test_parse_invalid(); -+ test_generate_valid(); -+} -diff --git a/ssh-add.c b/ssh-add.c -index adcc4599..e4fd5623 100644 ---- a/ssh-add.c -+++ b/ssh-add.c -@@ -64,6 +64,7 @@ - #include "misc.h" - #include "ssherr.h" - #include "digest.h" -+#include "ssh-pkcs11-uri.h" - - /* argv0 */ - extern char *__progname; -@@ -188,6 +189,24 @@ delete_all(int agent_fd) - return ret; - } - -+#ifdef ENABLE_PKCS11 -+static int update_card(int, int, const char *); -+ -+int -+update_pkcs11_uri(int agent_fd, int adding, const char *pkcs11_uri) -+{ -+ struct pkcs11_uri *uri; -+ -+ /* dry-run parse to make sure the URI is valid and to report errors */ -+ uri = pkcs11_uri_init(); -+ if (pkcs11_uri_parse((char *) pkcs11_uri, uri) != 0) -+ fatal("Failed to parse PKCS#11 URI"); -+ pkcs11_uri_cleanup(uri); -+ -+ return update_card(agent_fd, adding, pkcs11_uri); -+} -+#endif -+ - static int - add_file(int agent_fd, const char *filename, int key_only, int qflag) - { -@@ -480,6 +499,13 @@ lock_agent(int agent_fd, int lock) - static int - do_file(int agent_fd, int deleting, int key_only, char *file, int qflag) - { -+#ifdef ENABLE_PKCS11 -+ if (strlen(file) >= strlen(PKCS11_URI_SCHEME) && -+ strncmp(file, PKCS11_URI_SCHEME, -+ strlen(PKCS11_URI_SCHEME)) == 0) { -+ return update_pkcs11_uri(agent_fd, !deleting, file); -+ } -+#endif - if (deleting) { - if (delete_file(agent_fd, file, key_only, qflag) == -1) - return -1; -diff --git a/ssh-agent.c b/ssh-agent.c -index 2a4578b0..f6c86240 100644 ---- a/ssh-agent.c -+++ b/ssh-agent.c -@@ -546,10 +546,72 @@ no_identities(SocketEntry *e) - } - - #ifdef ENABLE_PKCS11 -+static char * -+sanitize_pkcs11_provider(const char *provider) -+{ -+ struct pkcs11_uri *uri = NULL; -+ char *sane_uri, *module_path = NULL; /* default path */ -+ char canonical_provider[PATH_MAX]; -+ -+ if (provider == NULL) -+ return NULL; -+ -+ if (strlen(provider) >= strlen(PKCS11_URI_SCHEME) && -+ strncmp(provider, PKCS11_URI_SCHEME, -+ strlen(PKCS11_URI_SCHEME)) == 0) { -+ /* PKCS#11 URI */ -+ uri = pkcs11_uri_init(); -+ if (uri == NULL) { -+ error("Failed to init PCKS#11 URI"); -+ return NULL; -+ } -+ -+ if (pkcs11_uri_parse(provider, uri) != 0) { -+ error("Failed to parse PKCS#11 URI"); -+ return NULL; -+ } -+ /* validate also provider from URI */ -+ if (uri->module_path) -+ module_path = strdup(uri->module_path); -+ } else -+ module_path = strdup(provider); /* simple path */ -+ -+ if (module_path != NULL) { /* do not validate default NULL path in URI */ -+ if (realpath(module_path, canonical_provider) == NULL) { -+ verbose("failed PKCS#11 provider \"%.100s\": realpath: %s", -+ module_path, strerror(errno)); -+ free(module_path); -+ pkcs11_uri_cleanup(uri); -+ return NULL; -+ } -+ free(module_path); -+ if (match_pattern_list(canonical_provider, pkcs11_whitelist, 0) != 1) { -+ verbose("refusing PKCS#11 provider \"%.100s\": " -+ "not whitelisted", canonical_provider); -+ pkcs11_uri_cleanup(uri); -+ return NULL; -+ } -+ -+ /* copy verified and sanitized provider path back to the uri */ -+ if (uri) { -+ free(uri->module_path); -+ uri->module_path = xstrdup(canonical_provider); -+ } -+ } -+ -+ if (uri) { -+ sane_uri = pkcs11_uri_get(uri); -+ pkcs11_uri_cleanup(uri); -+ return sane_uri; -+ } else { -+ return xstrdup(canonical_provider); /* simple path */ -+ } -+} -+ - static void - process_add_smartcard_key(SocketEntry *e) - { -- char *provider = NULL, *pin = NULL, canonical_provider[PATH_MAX]; -+ char *provider = NULL, *pin = NULL, *sane_uri = NULL; - int r, i, count = 0, success = 0, confirm = 0; - u_int seconds; - time_t death = 0; -@@ -585,28 +645,23 @@ process_add_smartcard_key(SocketEntry *e) - goto send; - } - } -- if (realpath(provider, canonical_provider) == NULL) { -- verbose("failed PKCS#11 add of \"%.100s\": realpath: %s", -- provider, strerror(errno)); -- goto send; -- } -- if (match_pattern_list(canonical_provider, pkcs11_whitelist, 0) != 1) { -- verbose("refusing PKCS#11 add of \"%.100s\": " -- "provider not whitelisted", canonical_provider); -+ -+ sane_uri = sanitize_pkcs11_provider(provider); -+ if (sane_uri == NULL) - goto send; -- } -- debug("%s: add %.100s", __func__, canonical_provider); -+ - if (lifetime && !death) - death = monotime() + lifetime; - -- count = pkcs11_add_provider(canonical_provider, pin, &keys); -+ debug("%s: add %.100s", __func__, sane_uri); -+ count = pkcs11_add_provider(sane_uri, pin, &keys); - for (i = 0; i < count; i++) { - k = keys[i]; - if (lookup_identity(k) == NULL) { - id = xcalloc(1, sizeof(Identity)); - id->key = k; -- id->provider = xstrdup(canonical_provider); -- id->comment = xstrdup(canonical_provider); /* XXX */ -+ id->provider = xstrdup(sane_uri); -+ id->comment = xstrdup(sane_uri); - id->death = death; - id->confirm = confirm; - TAILQ_INSERT_TAIL(&idtab->idlist, id, next); -@@ -620,6 +675,7 @@ process_add_smartcard_key(SocketEntry *e) - send: - free(pin); - free(provider); -+ free(sane_uri); - free(keys); - send_status(e, success); - } -@@ -627,7 +683,7 @@ send: - static void - process_remove_smartcard_key(SocketEntry *e) - { -- char *provider = NULL, *pin = NULL, canonical_provider[PATH_MAX]; -+ char *provider = NULL, *pin = NULL, *sane_uri = NULL; - int r, success = 0; - Identity *id, *nxt; - -@@ -638,30 +694,29 @@ process_remove_smartcard_key(SocketEntry *e) - } - free(pin); - -- if (realpath(provider, canonical_provider) == NULL) { -- verbose("failed PKCS#11 add of \"%.100s\": realpath: %s", -- provider, strerror(errno)); -+ sane_uri = sanitize_pkcs11_provider(provider); -+ if (sane_uri == NULL) - goto send; -- } - -- debug("%s: remove %.100s", __func__, canonical_provider); -+ debug("%s: remove %.100s", __func__, sane_uri); - for (id = TAILQ_FIRST(&idtab->idlist); id; id = nxt) { - nxt = TAILQ_NEXT(id, next); - /* Skip file--based keys */ - if (id->provider == NULL) - continue; -- if (!strcmp(canonical_provider, id->provider)) { -+ if (!strcmp(sane_uri, id->provider)) { - TAILQ_REMOVE(&idtab->idlist, id, next); - free_identity(id); - idtab->nentries--; - } - } -- if (pkcs11_del_provider(canonical_provider) == 0) -+ if (pkcs11_del_provider(sane_uri) == 0) - success = 1; - else - error("%s: pkcs11_del_provider failed", __func__); - send: - free(provider); -+ free(sane_uri); - send_status(e, success); - } - #endif /* ENABLE_PKCS11 */ -diff --git a/ssh-keygen.c b/ssh-keygen.c -index d1ffc30c..00e38049 100644 ---- a/ssh-keygen.c -+++ b/ssh-keygen.c -@@ -820,6 +820,7 @@ do_download(struct passwd *pw) - free(fp); - } else { - (void) sshkey_write(keys[i], stdout); /* XXX check */ -+ (void) pkcs11_uri_write(keys[i], stdout); - fprintf(stdout, "\n"); - } - sshkey_free(keys[i]); -diff --git a/ssh-pkcs11-client.c b/ssh-pkcs11-client.c -index a023f5f4..882e8381 100644 ---- a/ssh-pkcs11-client.c -+++ b/ssh-pkcs11-client.c -@@ -117,6 +117,7 @@ pkcs11_rsa_private_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa, - return (-1); - key.type = KEY_RSA; - key.rsa = rsa; -+ key.ecdsa_nid = 0; - if ((r = sshkey_to_blob(&key, &blob, &blen)) != 0) { - error("%s: sshkey_to_blob: %s", __func__, ssh_err(r)); - return -1; -@@ -195,6 +196,8 @@ pkcs11_add_provider(char *name, char *pin, Key ***keysp) - u_int nkeys, i; - struct sshbuf *msg; - -+ debug("%s: called, name = %s", __func__, name); -+ - if (fd < 0 && pkcs11_start_helper() < 0) - return (-1); - -@@ -208,6 +211,7 @@ pkcs11_add_provider(char *name, char *pin, Key ***keysp) - if ((r = sshbuf_get_u32(msg, &nkeys)) != 0) - fatal("%s: buffer error: %s", __func__, ssh_err(r)); - *keysp = xcalloc(nkeys, sizeof(struct sshkey *)); -+ debug("%s: nkeys = %u", __func__, nkeys); - for (i = 0; i < nkeys; i++) { - /* XXX clean up properly instead of fatal() */ - if ((r = sshbuf_get_string(msg, &blob, &blen)) != 0 || -diff --git a/ssh-pkcs11-uri.c b/ssh-pkcs11-uri.c -new file mode 100644 -index 00000000..da15c164 ---- /dev/null -+++ b/ssh-pkcs11-uri.c -@@ -0,0 +1,395 @@ -+/* -+ * Copyright (c) 2017 Red Hat -+ * -+ * Authors: Jakub Jelen -+ * -+ * 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" -+ -+#ifdef ENABLE_PKCS11 -+ -+#include -+#include -+ -+#include "sshkey.h" -+#include "sshbuf.h" -+#include "log.h" -+ -+#define CRYPTOKI_COMPAT -+#include "pkcs11.h" -+ -+#include "ssh-pkcs11-uri.h" -+ -+#define PKCS11_URI_PATH_SEPARATOR ";" -+#define PKCS11_URI_QUERY_SEPARATOR "&" -+#define PKCS11_URI_VALUE_SEPARATOR "=" -+#define PKCS11_URI_ID "id" -+#define PKCS11_URI_TOKEN "token" -+#define PKCS11_URI_OBJECT "object" -+#define PKCS11_URI_LIB_MANUF "library-manufacturer" -+#define PKCS11_URI_MANUF "manufacturer" -+#define PKCS11_URI_MODULE_PATH "module-path" -+ -+/* Keyword tokens. */ -+typedef enum { -+ pId, pToken, pObject, pLibraryManufacturer, pManufacturer, pModulePath, -+ pBadOption -+} pkcs11uriOpCodes; -+ -+/* Textual representation of the tokens. */ -+static struct { -+ const char *name; -+ pkcs11uriOpCodes opcode; -+} keywords[] = { -+ { PKCS11_URI_ID, pId }, -+ { PKCS11_URI_TOKEN, pToken }, -+ { PKCS11_URI_OBJECT, pObject }, -+ { PKCS11_URI_LIB_MANUF, pLibraryManufacturer }, -+ { PKCS11_URI_MANUF, pManufacturer }, -+ { PKCS11_URI_MODULE_PATH, pModulePath }, -+ { NULL, pBadOption } -+}; -+ -+static pkcs11uriOpCodes -+parse_token(const char *cp) -+{ -+ u_int i; -+ -+ for (i = 0; keywords[i].name; i++) -+ if (strncasecmp(cp, keywords[i].name, -+ strlen(keywords[i].name)) == 0) -+ return keywords[i].opcode; -+ -+ return pBadOption; -+} -+ -+int -+percent_decode(char *data, char **outp) -+{ -+ char tmp[3]; -+ char *out, *tmp_end; -+ char *p = data; -+ long value; -+ size_t outlen = 0; -+ -+ out = malloc(strlen(data)+1); /* upper bound */ -+ if (out == NULL) -+ return -1; -+ while (*p != '\0') { -+ switch (*p) { -+ case '%': -+ p++; -+ if (*p == '\0') -+ goto fail; -+ tmp[0] = *p++; -+ if (*p == '\0') -+ goto fail; -+ tmp[1] = *p++; -+ tmp[2] = '\0'; -+ tmp_end = NULL; -+ value = strtol(tmp, &tmp_end, 16); -+ if (tmp_end != tmp+2) -+ goto fail; -+ else -+ out[outlen++] = (char) value; -+ break; -+ default: -+ out[outlen++] = *p++; -+ break; -+ } -+ } -+ -+ /* zero terminate */ -+ out[outlen] = '\0'; -+ *outp = out; -+ return outlen; -+fail: -+ free(out); -+ return -1; -+} -+ -+struct sshbuf * -+percent_encode(const char *data, size_t length, const char *whitelist) -+{ -+ struct sshbuf *b = NULL; -+ char tmp[4], *cp; -+ size_t i; -+ -+ if ((b = sshbuf_new()) == NULL) -+ return NULL; -+ for (i = 0; i < length; i++) { -+ cp = strchr(whitelist, data[i]); -+ /* if c is specified as '\0' pointer to terminator is returned !! */ -+ if (cp != NULL && *cp != '\0') { -+ if (sshbuf_put(b, &data[i], 1) != 0) -+ goto err; -+ } else -+ if (snprintf(tmp, 4, "%%%02X", (unsigned char) data[i]) < 3 -+ || sshbuf_put(b, tmp, 3) != 0) -+ goto err; -+ } -+ if (sshbuf_put(b, "\0", 1) == 0) -+ return b; -+err: -+ sshbuf_free(b); -+ return NULL; -+} -+ -+char * -+pkcs11_uri_append(char *part, const char *separator, const char *key, -+ struct sshbuf *value) -+{ -+ char *new_part; -+ size_t size = 0; -+ -+ if (value == NULL) -+ return NULL; -+ -+ size = asprintf(&new_part, -+ "%s%s%s" PKCS11_URI_VALUE_SEPARATOR "%s", -+ (part != NULL ? part : ""), -+ (part != NULL ? separator : ""), -+ key, sshbuf_ptr(value)); -+ sshbuf_free(value); -+ free(part); -+ -+ if (size <= 0) -+ return NULL; -+ return new_part; -+} -+ -+char * -+pkcs11_uri_get(struct pkcs11_uri *uri) -+{ -+ size_t size = 0; -+ char *p = NULL, *path = NULL, *query = NULL; -+ -+ /* compose a percent-encoded ID */ -+ if (uri->id_len > 0) { -+ struct sshbuf *key_id = percent_encode(uri->id, uri->id_len, ""); -+ path = pkcs11_uri_append(path, PKCS11_URI_PATH_SEPARATOR, -+ PKCS11_URI_ID, key_id); -+ if (path == NULL) -+ goto err; -+ } -+ -+ /* Write object label */ -+ if (uri->object) { -+ struct sshbuf *label = percent_encode(uri->object, strlen(uri->object), -+ PKCS11_URI_WHITELIST); -+ path = pkcs11_uri_append(path, PKCS11_URI_PATH_SEPARATOR, -+ PKCS11_URI_OBJECT, label); -+ if (path == NULL) -+ goto err; -+ } -+ -+ /* Write token label */ -+ if (uri->token) { -+ struct sshbuf *label = percent_encode(uri->token, strlen(uri->token), -+ PKCS11_URI_WHITELIST); -+ path = pkcs11_uri_append(path, PKCS11_URI_PATH_SEPARATOR, -+ PKCS11_URI_TOKEN, label); -+ if (path == NULL) -+ goto err; -+ } -+ -+ /* Write manufacturer */ -+ if (uri->manuf) { -+ struct sshbuf *manuf = percent_encode(uri->manuf, -+ strlen(uri->manuf), PKCS11_URI_WHITELIST); -+ path = pkcs11_uri_append(path, PKCS11_URI_PATH_SEPARATOR, -+ PKCS11_URI_MANUF, manuf); -+ if (path == NULL) -+ goto err; -+ } -+ -+ /* Write module_path */ -+ if (uri->module_path) { -+ struct sshbuf *module = percent_encode(uri->module_path, -+ strlen(uri->module_path), PKCS11_URI_WHITELIST "/"); -+ query = pkcs11_uri_append(query, PKCS11_URI_QUERY_SEPARATOR, -+ PKCS11_URI_MODULE_PATH, module); -+ if (query == NULL) -+ goto err; -+ } -+ -+ size = asprintf(&p, PKCS11_URI_SCHEME "%s%s%s", -+ path != NULL ? path : "", -+ query != NULL ? "?" : "", -+ query != NULL ? query : ""); -+err: -+ free(query); -+ free(path); -+ if (size <= 0) -+ return NULL; -+ return p; -+} -+ -+struct pkcs11_uri * -+pkcs11_uri_init() -+{ -+ struct pkcs11_uri *d = calloc(1, sizeof(struct pkcs11_uri)); -+ return d; -+} -+ -+void -+pkcs11_uri_cleanup(struct pkcs11_uri *pkcs11) -+{ -+ free(pkcs11->id); -+ free(pkcs11->module_path); -+ free(pkcs11->token); -+ free(pkcs11->object); -+ free(pkcs11->lib_manuf); -+ free(pkcs11->manuf); -+ free(pkcs11); -+} -+ -+int -+pkcs11_uri_parse(const char *uri, struct pkcs11_uri *pkcs11) -+{ -+ char *saveptr1, *saveptr2, *str1, *str2, *tok; -+ int rv = 0, len; -+ char *p = NULL; -+ -+ size_t scheme_len = strlen(PKCS11_URI_SCHEME); -+ if (strlen(uri) < scheme_len || /* empty URI matches everything */ -+ strncmp(uri, PKCS11_URI_SCHEME, scheme_len) != 0) { -+ error("%s: The '%s' does not look like PKCS#11 URI", -+ __func__, uri); -+ return -1; -+ } -+ -+ if (pkcs11 == NULL) { -+ error("%s: Bad arguments. The pkcs11 can't be null", __func__); -+ return -1; -+ } -+ -+ /* skip URI schema name */ -+ p = strdup(uri); -+ str1 = p; -+ -+ /* everything before ? */ -+ tok = strtok_r(str1, "?", &saveptr1); -+ if (tok == NULL) { -+ free(p); -+ error("%s: pk11-path expected, got EOF", __func__); -+ return -1; -+ } -+ -+ /* skip URI schema name: -+ * the scheme ensures that there is at least something before "?" -+ * allowing empty pk11-path. Resulting token at worst pointing to -+ * \0 byte */ -+ tok = tok + scheme_len; -+ -+ /* parse pk11-path */ -+ for (str2 = tok; ; str2 = NULL) { -+ char **charptr; -+ pkcs11uriOpCodes opcode; -+ tok = strtok_r(str2, PKCS11_URI_PATH_SEPARATOR, &saveptr2); -+ if (tok == NULL) -+ break; -+ opcode = parse_token(tok); -+ -+ char *arg = tok + strlen(keywords[opcode].name) + 1; /* separator "=" */ -+ switch (opcode) { -+ case pId: -+ /* CKA_ID */ -+ if (pkcs11->id != NULL) { -+ verbose("%s: The id already set in the PKCS#11 URI", -+ __func__); -+ rv = -1; -+ } -+ len = percent_decode(arg, &pkcs11->id); -+ if (len <= 0) { -+ verbose("%s: Failed to percent-decode CKA_ID: %s", -+ __func__, arg); -+ rv = -1; -+ } else -+ pkcs11->id_len = len; -+ debug3("%s: Setting CKA_ID = %s from PKCS#11 URI", -+ __func__, arg); -+ break; -+ case pToken: -+ /* CK_TOKEN_INFO -> label */ -+ charptr = &pkcs11->token; -+ parse_string: -+ if (*charptr != NULL) { -+ verbose("%s: The %s already set in the PKCS#11 URI", -+ keywords[opcode].name, __func__); -+ rv = -1; -+ } -+ percent_decode(arg, charptr); -+ debug3("%s: Setting %s = %s from PKCS#11 URI", -+ __func__, keywords[opcode].name, *charptr); -+ break; -+ -+ case pObject: -+ /* CK_TOKEN_INFO -> manufacturerID */ -+ charptr = &pkcs11->object; -+ goto parse_string; -+ -+ case pManufacturer: -+ /* CK_TOKEN_INFO -> manufacturerID */ -+ charptr = &pkcs11->manuf; -+ goto parse_string; -+ -+ case pLibraryManufacturer: -+ /* CK_INFO -> manufacturerID */ -+ charptr = &pkcs11->lib_manuf; -+ goto parse_string; -+ -+ case pBadOption: -+ default: -+ /* Unrecognized attribute in the URI path SHOULD be error */ -+ verbose("%s: Unknown part of path in PKCS#11 URI: %s", -+ __func__, tok); -+ } -+ } -+ -+ tok = strtok_r(NULL, "?", &saveptr1); -+ if (tok == NULL) { -+ free(p); -+ return rv; -+ } -+ /* parse pk11-query (optional) */ -+ for (str2 = tok; ; str2 = NULL) { -+ size_t key_len = strlen(PKCS11_URI_MODULE_PATH) + 1; -+ tok = strtok_r(str2, PKCS11_URI_QUERY_SEPARATOR, &saveptr2); -+ if (tok == NULL) -+ break; -+ if (strncasecmp(tok, PKCS11_URI_MODULE_PATH -+ PKCS11_URI_VALUE_SEPARATOR, key_len) == 0) { -+ /* module-path is PKCS11Provider */ -+ if (pkcs11->module_path != NULL) { -+ verbose("%s: Multiple module-path attributes are" -+ "not supported the PKCS#11 URI", __func__); -+ rv = -1; -+ } -+ percent_decode(tok + key_len, &pkcs11->module_path); -+ debug3("%s: Setting PKCS11Provider = %s from PKCS#11 URI", -+ __func__, pkcs11->module_path); -+ /* } else if ( pin-value ) { */ -+ } else { -+ /* Unrecognized attribute in the URI query SHOULD be ignored */ -+ verbose("%s: Unknown part of query in PKCS#11 URI: %s", -+ __func__, tok); -+ } -+ } -+ free(p); -+ return rv; -+} -+ -+#endif /* ENABLE_PKCS11 */ -diff --git a/ssh-pkcs11-uri.h b/ssh-pkcs11-uri.h -new file mode 100644 -index 00000000..609c4df1 ---- /dev/null -+++ b/ssh-pkcs11-uri.h -@@ -0,0 +1,41 @@ -+/* -+ * Copyright (c) 2017 Red Hat -+ * -+ * Authors: Jakub Jelen -+ * -+ * 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. -+ */ -+ -+#define PKCS11_URI_SCHEME "pkcs11:" -+#define PKCS11_URI_WHITELIST "abcdefghijklmnopqrstuvwxyz" \ -+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \ -+ "0123456789_-.()" -+ -+struct pkcs11_uri { -+ /* path */ -+ char *id; -+ size_t id_len; -+ char *token; -+ char *object; -+ char *lib_manuf; -+ char *manuf; -+ /* query */ -+ char *module_path; -+}; -+ -+struct pkcs11_uri *pkcs11_uri_init(); -+void pkcs11_uri_cleanup(struct pkcs11_uri *); -+int pkcs11_uri_parse(const char *, struct pkcs11_uri *); -+struct pkcs11_uri *pkcs11_uri_init(); -+char * pkcs11_uri_get(struct pkcs11_uri *uri); -+ -diff --git a/ssh-pkcs11.c b/ssh-pkcs11.c -index 88c9d6e2..a29b4451 100644 ---- a/ssh-pkcs11.c -+++ b/ssh-pkcs11.c -@@ -48,8 +48,8 @@ struct pkcs11_slotinfo { - int logged_in; - }; - --struct pkcs11_provider { -- char *name; -+struct pkcs11_module { -+ char *module_path; - void *handle; - CK_FUNCTION_LIST *function_list; - CK_INFO info; -@@ -58,6 +58,13 @@ struct pkcs11_provider { - struct pkcs11_slotinfo *slotinfo; - int valid; - int refcount; -+}; -+ -+struct pkcs11_provider { -+ char *name; -+ struct pkcs11_module *module; /* can be shared between various providers */ -+ int refcount; -+ int valid; - TAILQ_ENTRY(pkcs11_provider) next; - }; - -@@ -70,10 +77,46 @@ struct pkcs11_key { - RSA_METHOD rsa_method; - char *keyid; - int keyid_len; -+ char *label; - }; - - int pkcs11_interactive = 0; - -+/* -+ * This can't be in the ssh-pkcs11-uri, becase we can not depend on -+ * PKCS#11 structures in ssh-agent (using client-helper communication) -+ */ -+int -+pkcs11_uri_write(const struct sshkey *key, FILE *f) -+{ -+ char *p = NULL; -+ struct pkcs11_uri uri; -+ struct pkcs11_key *k11; -+ -+ /* sanity - is it a RSA key with associated app_data? */ -+ if (key->type != KEY_RSA || -+ (k11 = RSA_get_app_data(key->rsa)) == NULL) -+ return -1; -+ -+ /* omit type -- we are looking for private-public or private-certificate pairs */ -+ uri.id = k11->keyid; -+ uri.id_len = k11->keyid_len; -+ uri.token = k11->provider->module->slotinfo[k11->slotidx].token.label; -+ uri.object = k11->label; -+ uri.module_path = k11->provider->module->module_path; -+ uri.lib_manuf = k11->provider->module->info.manufacturerID; -+ uri.manuf = k11->provider->module->slotinfo[k11->slotidx].token.manufacturerID; -+ -+ p = pkcs11_uri_get(&uri); -+ /* do not cleanup -- we do not allocate here, only reference */ -+ if (p == NULL) -+ return -1; -+ -+ fprintf(f, " %s", p); -+ free(p); -+ return 0; -+} -+ - int - pkcs11_init(int interactive) - { -@@ -89,26 +132,63 @@ pkcs11_init(int interactive) - * this is called when a provider gets unregistered. - */ - static void --pkcs11_provider_finalize(struct pkcs11_provider *p) -+pkcs11_module_finalize(struct pkcs11_module *m) - { - CK_RV rv; - CK_ULONG i; - -- debug("pkcs11_provider_finalize: %p refcount %d valid %d", -- p, p->refcount, p->valid); -- if (!p->valid) -+ debug("%s: %p refcount %d valid %d", __func__, -+ m, m->refcount, m->valid); -+ if (!m->valid) - return; -- for (i = 0; i < p->nslots; i++) { -- if (p->slotinfo[i].session && -- (rv = p->function_list->C_CloseSession( -- p->slotinfo[i].session)) != CKR_OK) -+ for (i = 0; i < m->nslots; i++) { -+ if (m->slotinfo[i].session && -+ (rv = m->function_list->C_CloseSession( -+ m->slotinfo[i].session)) != CKR_OK) - error("C_CloseSession failed: %lu", rv); - } -- if ((rv = p->function_list->C_Finalize(NULL)) != CKR_OK) -+ if ((rv = m->function_list->C_Finalize(NULL)) != CKR_OK) - error("C_Finalize failed: %lu", rv); -+ m->valid = 0; -+ m->function_list = NULL; -+ dlclose(m->handle); -+} -+ -+/* -+ * remove a reference to the pkcs11 module. -+ * called when a provider is unregistered. -+ */ -+static void -+pkcs11_module_unref(struct pkcs11_module *m) -+{ -+ debug("%s: %p refcount %d", __func__, m, m->refcount); -+ if (--m->refcount <= 0) { -+ pkcs11_module_finalize(m); -+ if (m->valid) -+ error("%s: %p still valid", __func__, m); -+ free(m->slotlist); -+ free(m->slotinfo); -+ free(m->module_path); -+ free(m); -+ } -+} -+ -+/* -+ * finalize a provider shared libarary, it's no longer usable. -+ * however, there might still be keys referencing this provider, -+ * so the actuall freeing of memory is handled by pkcs11_provider_unref(). -+ * this is called when a provider gets unregistered. -+ */ -+static void -+pkcs11_provider_finalize(struct pkcs11_provider *p) -+{ -+ debug("%s: %p refcount %d valid %d", __func__, -+ p, p->refcount, p->valid); -+ if (!p->valid) -+ return; -+ pkcs11_module_unref(p->module); -+ p->module = NULL; - p->valid = 0; -- p->function_list = NULL; -- dlclose(p->handle); - } - - /* -@@ -118,12 +198,11 @@ pkcs11_provider_finalize(struct pkcs11_provider *p) - static void - pkcs11_provider_unref(struct pkcs11_provider *p) - { -- debug("pkcs11_provider_unref: %p refcount %d", p, p->refcount); -+ debug("%s: %p refcount %d", __func__, p, p->refcount); - if (--p->refcount <= 0) { -- if (p->valid) -- error("pkcs11_provider_unref: %p still valid", p); -- free(p->slotlist); -- free(p->slotinfo); -+ if (p->module) -+ pkcs11_module_unref(p->module); -+ free(p->name); - free(p); - } - } -@@ -141,6 +220,20 @@ pkcs11_terminate(void) - } - } - -+/* lookup provider by module path */ -+static struct pkcs11_module * -+pkcs11_provider_lookup_module(char *module_path) -+{ -+ struct pkcs11_provider *p; -+ -+ TAILQ_FOREACH(p, &pkcs11_providers, next) { -+ debug("check %p %s (%s)", p, p->name, p->module->module_path); -+ if (!strcmp(module_path, p->module->module_path)) -+ return (p->module); -+ } -+ return (NULL); -+} -+ - /* lookup provider by name */ - static struct pkcs11_provider * - pkcs11_provider_lookup(char *provider_id) -@@ -155,19 +248,52 @@ pkcs11_provider_lookup(char *provider_id) - return (NULL); - } - -+int pkcs11_del_provider_by_uri(struct pkcs11_uri *); -+ - /* unregister provider by name */ - int - pkcs11_del_provider(char *provider_id) -+{ -+ int rv; -+ struct pkcs11_uri *uri; -+ -+ debug("%s: called, provider_id = %s", __func__, provider_id); -+ -+ uri = pkcs11_uri_init(); -+ if (uri == NULL) -+ fatal("Failed to init PCKS#11 URI"); -+ -+ if (strlen(provider_id) >= strlen(PKCS11_URI_SCHEME) && -+ strncmp(provider_id, PKCS11_URI_SCHEME, strlen(PKCS11_URI_SCHEME)) == 0) { -+ if (pkcs11_uri_parse(provider_id, uri) != 0) -+ fatal("Failed to parse PKCS#11 URI"); -+ } else { -+ uri->module_path = strdup(provider_id); -+ } -+ -+ rv = pkcs11_del_provider_by_uri(uri); -+ pkcs11_uri_cleanup(uri); -+ return rv; -+} -+ -+/* unregister provider by PKCS#11 URI */ -+int -+pkcs11_del_provider_by_uri(struct pkcs11_uri *uri) - { - struct pkcs11_provider *p; -+ int rv = -1; -+ char *provider_uri = pkcs11_uri_get(uri); - -- if ((p = pkcs11_provider_lookup(provider_id)) != NULL) { -+ debug3("%s(%s): called", __func__, provider_uri); -+ -+ if ((p = pkcs11_provider_lookup(provider_uri)) != NULL) { - TAILQ_REMOVE(&pkcs11_providers, p, next); - pkcs11_provider_finalize(p); - pkcs11_provider_unref(p); -- return (0); -+ rv = 0; - } -- return (-1); -+ free(provider_uri); -+ return rv; - } - - /* openssl callback for freeing an RSA key */ -@@ -183,6 +309,7 @@ pkcs11_rsa_finish(RSA *rsa) - if (k11->provider) - pkcs11_provider_unref(k11->provider); - free(k11->keyid); -+ free(k11->label); - free(k11); - } - return (rv); -@@ -199,8 +327,8 @@ pkcs11_find(struct pkcs11_provider *p, CK_ULONG slotidx, CK_ATTRIBUTE *attr, - CK_RV rv; - int ret = -1; - -- f = p->function_list; -- session = p->slotinfo[slotidx].session; -+ f = p->module->function_list; -+ session = p->module->slotinfo[slotidx].session; - if ((rv = f->C_FindObjectsInit(session, attr, nattr)) != CKR_OK) { - error("C_FindObjectsInit failed (nattr %lu): %lu", nattr, rv); - return (-1); -@@ -247,12 +375,13 @@ pkcs11_rsa_private_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa, - error("RSA_get_app_data failed for rsa %p", rsa); - return (-1); - } -- if (!k11->provider || !k11->provider->valid) { -+ if (!k11->provider || !k11->provider->valid || !k11->provider->module -+ || !k11->provider->module->valid) { - error("no pkcs11 (valid) provider for rsa %p", rsa); - return (-1); - } -- f = k11->provider->function_list; -- si = &k11->provider->slotinfo[k11->slotidx]; -+ f = k11->provider->module->function_list; -+ si = &k11->provider->module->slotinfo[k11->slotidx]; - if ((si->token.flags & CKF_LOGIN_REQUIRED) && !si->logged_in) { - if (!pkcs11_interactive) { - error("need pin entry%s", (si->token.flags & -@@ -311,7 +440,7 @@ pkcs11_rsa_private_decrypt(int flen, const u_char *from, u_char *to, RSA *rsa, - /* redirect private key operations for rsa key to pkcs11 token */ - static int - pkcs11_rsa_wrap(struct pkcs11_provider *provider, CK_ULONG slotidx, -- CK_ATTRIBUTE *keyid_attrib, RSA *rsa) -+ CK_ATTRIBUTE *keyid_attrib, CK_ATTRIBUTE *label_attrib, RSA *rsa) - { - struct pkcs11_key *k11; - const RSA_METHOD *def = RSA_get_default_method(); -@@ -326,6 +455,11 @@ pkcs11_rsa_wrap(struct pkcs11_provider *provider, CK_ULONG slotidx, - k11->keyid = xmalloc(k11->keyid_len); - memcpy(k11->keyid, keyid_attrib->pValue, k11->keyid_len); - } -+ if (label_attrib->ulValueLen > 0 ) { -+ k11->label = xmalloc(label_attrib->ulValueLen+1); -+ memcpy(k11->label, label_attrib->pValue, label_attrib->ulValueLen); -+ k11->label[label_attrib->ulValueLen] = 0; -+ } - k11->orig_finish = def->finish; - memcpy(&k11->rsa_method, def, sizeof(k11->rsa_method)); - k11->rsa_method.name = "pkcs11"; -@@ -372,16 +506,16 @@ pkcs11_open_session(struct pkcs11_provider *p, CK_ULONG slotidx, char *pin) - CK_SESSION_HANDLE session; - int login_required; - -- f = p->function_list; -- login_required = p->slotinfo[slotidx].token.flags & CKF_LOGIN_REQUIRED; -+ f = p->module->function_list; -+ login_required = p->module->slotinfo[slotidx].token.flags & CKF_LOGIN_REQUIRED; - if (pin && login_required && !strlen(pin)) { - error("pin required"); - return (-1); - } -- if ((rv = f->C_OpenSession(p->slotlist[slotidx], CKF_RW_SESSION| -+ if ((rv = f->C_OpenSession(p->module->slotlist[slotidx], CKF_RW_SESSION| - CKF_SERIAL_SESSION, NULL, NULL, &session)) - != CKR_OK) { -- error("C_OpenSession failed: %lu", rv); -+ error("C_OpenSession failed for slot %lu: %lu", slotidx, rv); - return (-1); - } - if (login_required && pin) { -@@ -393,9 +527,9 @@ pkcs11_open_session(struct pkcs11_provider *p, CK_ULONG slotidx, char *pin) - error("C_CloseSession failed: %lu", rv); - return (-1); - } -- p->slotinfo[slotidx].logged_in = 1; -+ p->module->slotinfo[slotidx].logged_in = 1; - } -- p->slotinfo[slotidx].session = session; -+ p->module->slotinfo[slotidx].session = session; - return (0); - } - -@@ -405,38 +539,62 @@ pkcs11_open_session(struct pkcs11_provider *p, CK_ULONG slotidx, char *pin) - * keysp points to an (possibly empty) array with *nkeys keys. - */ - static int pkcs11_fetch_keys_filter(struct pkcs11_provider *, CK_ULONG, -- CK_ATTRIBUTE [], CK_ATTRIBUTE [3], struct sshkey ***, int *) -+ CK_ATTRIBUTE [], size_t, CK_ATTRIBUTE [3], struct sshkey ***, int *) - __attribute__((__bounded__(__minbytes__,4, 3 * sizeof(CK_ATTRIBUTE)))); - - static int - pkcs11_fetch_keys(struct pkcs11_provider *p, CK_ULONG slotidx, -- struct sshkey ***keysp, int *nkeys) -+ struct sshkey ***keysp, int *nkeys, struct pkcs11_uri *uri) - { -+ size_t filter_size = 1; - CK_OBJECT_CLASS pubkey_class = CKO_PUBLIC_KEY; - CK_OBJECT_CLASS cert_class = CKO_CERTIFICATE; - CK_ATTRIBUTE pubkey_filter[] = { -- { CKA_CLASS, NULL, sizeof(pubkey_class) } -+ { CKA_CLASS, NULL, sizeof(pubkey_class) }, -+ { CKA_ID, NULL, 0 }, -+ { CKA_LABEL, NULL, 0 } - }; - CK_ATTRIBUTE cert_filter[] = { -- { CKA_CLASS, NULL, sizeof(cert_class) } -+ { CKA_CLASS, NULL, sizeof(cert_class) }, -+ { CKA_ID, NULL, 0 }, -+ { CKA_LABEL, NULL, 0 } - }; - CK_ATTRIBUTE pubkey_attribs[] = { - { CKA_ID, NULL, 0 }, -+ { CKA_LABEL, NULL, 0 }, - { CKA_MODULUS, NULL, 0 }, - { CKA_PUBLIC_EXPONENT, NULL, 0 } - }; - CK_ATTRIBUTE cert_attribs[] = { - { CKA_ID, NULL, 0 }, -+ { CKA_LABEL, NULL, 0 }, - { CKA_SUBJECT, NULL, 0 }, - { CKA_VALUE, NULL, 0 } - }; - pubkey_filter[0].pValue = &pubkey_class; - cert_filter[0].pValue = &cert_class; - -- if (pkcs11_fetch_keys_filter(p, slotidx, pubkey_filter, pubkey_attribs, -- keysp, nkeys) < 0 || -- pkcs11_fetch_keys_filter(p, slotidx, cert_filter, cert_attribs, -- keysp, nkeys) < 0) -+ if (uri->id != NULL) { -+ pubkey_filter[filter_size].pValue = uri->id; -+ pubkey_filter[filter_size].ulValueLen = uri->id_len; -+ cert_filter[filter_size].pValue = uri->id; -+ cert_filter[filter_size].ulValueLen = uri->id_len; -+ filter_size++; -+ } -+ if (uri->object != NULL) { -+ pubkey_filter[filter_size].pValue = uri->object; -+ pubkey_filter[filter_size].ulValueLen = strlen(uri->object); -+ pubkey_filter[filter_size].type = CKA_LABEL; -+ cert_filter[filter_size].pValue = uri->object; -+ cert_filter[filter_size].ulValueLen = strlen(uri->object); -+ cert_filter[filter_size].type = CKA_LABEL; -+ filter_size++; -+ } -+ -+ if (pkcs11_fetch_keys_filter(p, slotidx, pubkey_filter, filter_size, -+ pubkey_attribs, keysp, nkeys) < 0 || -+ pkcs11_fetch_keys_filter(p, slotidx, cert_filter, filter_size, -+ cert_attribs, keysp, nkeys) < 0) - return (-1); - return (0); - } -@@ -454,14 +612,15 @@ pkcs11_key_included(struct sshkey ***keysp, int *nkeys, struct sshkey *key) - - static int - pkcs11_fetch_keys_filter(struct pkcs11_provider *p, CK_ULONG slotidx, -- CK_ATTRIBUTE filter[], CK_ATTRIBUTE attribs[3], -+ CK_ATTRIBUTE filter[], size_t filter_size, CK_ATTRIBUTE attribs[4], - struct sshkey ***keysp, int *nkeys) - { - struct sshkey *key; - RSA *rsa; - X509 *x509; -- EVP_PKEY *evp; -+ EVP_PKEY *evp = NULL; - int i; -+ int nattribs = 4; - const u_char *cp; - CK_RV rv; - CK_OBJECT_HANDLE obj; -@@ -470,16 +629,15 @@ pkcs11_fetch_keys_filter(struct pkcs11_provider *p, CK_ULONG slotidx, - CK_SESSION_HANDLE session; - CK_FUNCTION_LIST *f; - -- f = p->function_list; -- session = p->slotinfo[slotidx].session; -+ f = p->module->function_list; -+ session = p->module->slotinfo[slotidx].session; - /* setup a filter the looks for public keys */ -- if ((rv = f->C_FindObjectsInit(session, filter, 1)) != CKR_OK) { -+ if ((rv = f->C_FindObjectsInit(session, filter, filter_size)) != CKR_OK) { - error("C_FindObjectsInit failed: %lu", rv); - return (-1); - } - while (1) { -- /* XXX 3 attributes in attribs[] */ -- for (i = 0; i < 3; i++) { -+ for (i = 0; i < nattribs; i++) { - attribs[i].pValue = NULL; - attribs[i].ulValueLen = 0; - } -@@ -487,22 +645,22 @@ pkcs11_fetch_keys_filter(struct pkcs11_provider *p, CK_ULONG slotidx, - || nfound == 0) - break; - /* found a key, so figure out size of the attributes */ -- if ((rv = f->C_GetAttributeValue(session, obj, attribs, 3)) -+ if ((rv = f->C_GetAttributeValue(session, obj, attribs, nattribs)) - != CKR_OK) { - error("C_GetAttributeValue failed: %lu", rv); - continue; - } - /* -- * Allow CKA_ID (always first attribute) to be empty, but -- * ensure that none of the others are zero length. -+ * Allow CKA_ID (always first attribute) and CKA_LABEL (second) -+ * to be empty, but ensure that none of the others are zero length. - * XXX assumes CKA_ID is always first. - */ -- if (attribs[1].ulValueLen == 0 || -- attribs[2].ulValueLen == 0) { -+ if (attribs[2].ulValueLen == 0 || -+ attribs[3].ulValueLen == 0) { - continue; - } - /* allocate buffers for attributes */ -- for (i = 0; i < 3; i++) { -+ for (i = 0; i < nattribs; i++) { - if (attribs[i].ulValueLen > 0) { - attribs[i].pValue = xmalloc( - attribs[i].ulValueLen); -@@ -510,27 +668,27 @@ pkcs11_fetch_keys_filter(struct pkcs11_provider *p, CK_ULONG slotidx, - } - - /* -- * retrieve ID, modulus and public exponent of RSA key, -- * or ID, subject and value for certificates. -+ * retrieve ID, label, modulus and public exponent of RSA key, -+ * or ID, label, subject and value for certificates. - */ - rsa = NULL; -- if ((rv = f->C_GetAttributeValue(session, obj, attribs, 3)) -+ if ((rv = f->C_GetAttributeValue(session, obj, attribs, nattribs)) - != CKR_OK) { - error("C_GetAttributeValue failed: %lu", rv); -- } else if (attribs[1].type == CKA_MODULUS ) { -+ } else if (attribs[2].type == CKA_MODULUS ) { - if ((rsa = RSA_new()) == NULL) { - error("RSA_new failed"); - } else { -- rsa->n = BN_bin2bn(attribs[1].pValue, -- attribs[1].ulValueLen, NULL); -- rsa->e = BN_bin2bn(attribs[2].pValue, -+ rsa->n = BN_bin2bn(attribs[2].pValue, - attribs[2].ulValueLen, NULL); -+ rsa->e = BN_bin2bn(attribs[3].pValue, -+ attribs[3].ulValueLen, NULL); - } - } else { -- cp = attribs[2].pValue; -+ cp = attribs[3].pValue; - if ((x509 = X509_new()) == NULL) { - error("X509_new failed"); -- } else if (d2i_X509(&x509, &cp, attribs[2].ulValueLen) -+ } else if (d2i_X509(&x509, &cp, attribs[3].ulValueLen) - == NULL) { - error("d2i_X509 failed"); - } else if ((evp = X509_get_pubkey(x509)) == NULL || -@@ -546,9 +704,10 @@ pkcs11_fetch_keys_filter(struct pkcs11_provider *p, CK_ULONG slotidx, - error("RSAPublicKey_dup"); - } - X509_free(x509); -+ EVP_PKEY_free(evp); - } - if (rsa && rsa->n && rsa->e && -- pkcs11_rsa_wrap(p, slotidx, &attribs[0], rsa) == 0) { -+ pkcs11_rsa_wrap(p, slotidx, &attribs[0], &attribs[1], rsa) == 0) { - if ((key = sshkey_new(KEY_UNSPEC)) == NULL) - fatal("sshkey_new failed"); - key->rsa = rsa; -@@ -569,7 +728,7 @@ pkcs11_fetch_keys_filter(struct pkcs11_provider *p, CK_ULONG slotidx, - } else if (rsa) { - RSA_free(rsa); - } -- for (i = 0; i < 3; i++) -+ for (i = 0; i < nattribs; i++) - free(attribs[i].pValue); - } - if ((rv = f->C_FindObjectsFinal(session)) != CKR_OK) -@@ -581,126 +740,239 @@ pkcs11_fetch_keys_filter(struct pkcs11_provider *p, CK_ULONG slotidx, - int - pkcs11_add_provider(char *provider_id, char *pin, struct sshkey ***keyp) - { -- int nkeys, need_finalize = 0; -- struct pkcs11_provider *p = NULL; -+ int rv; -+ struct pkcs11_uri *uri; -+ -+ debug("%s: called, provider_id = %s", __func__, provider_id); -+ -+ uri = pkcs11_uri_init(); -+ if (uri == NULL) -+ fatal("Failed to init PCKS#11 URI"); -+ -+ if (strlen(provider_id) >= strlen(PKCS11_URI_SCHEME) && -+ strncmp(provider_id, PKCS11_URI_SCHEME, strlen(PKCS11_URI_SCHEME)) == 0) { -+ if (pkcs11_uri_parse(provider_id, uri) != 0) -+ fatal("Failed to parse PKCS#11 URI"); -+ } else { -+ uri->module_path = strdup(provider_id); -+ } -+ -+ rv = pkcs11_add_provider_by_uri(uri, pin, keyp); -+ pkcs11_uri_cleanup(uri); -+ return rv; -+} -+ -+struct pkcs11_provider * -+pkcs11_provider_initialize(struct pkcs11_uri *uri) -+{ -+ int need_finalize = 0; - void *handle = NULL; - CK_RV (*getfunctionlist)(CK_FUNCTION_LIST **); - CK_RV rv; - CK_FUNCTION_LIST *f = NULL; - CK_TOKEN_INFO *token; - CK_ULONG i; -+ char *provider_module = NULL; -+ struct pkcs11_provider *p; -+ struct pkcs11_module *m; - -- *keyp = NULL; -- if (pkcs11_provider_lookup(provider_id) != NULL) { -- debug("%s: provider already registered: %s", -- __func__, provider_id); -+ /* if no provider specified, fallback to p11-kit */ -+ if (uri->module_path == NULL) { -+#ifdef PKCS11_DEFAULT_PROVIDER -+ provider_module = strdup(PKCS11_DEFAULT_PROVIDER); -+#else -+ error("%s: No module path provided", __func__); - goto fail; -+#endif -+ } else -+ provider_module = strdup(uri->module_path); -+ -+ p = xcalloc(1, sizeof(*p)); -+ p->name = pkcs11_uri_get(uri); -+ -+ if ((m = pkcs11_provider_lookup_module(provider_module)) != NULL -+ && m->valid) { -+ debug("%s: provider module already initialized: %s", -+ __func__, provider_module); -+ free(provider_module); -+ /* Skip the initialization of PKCS#11 module */ -+ m->refcount++; -+ p->module = m; -+ p->valid = 1; -+ TAILQ_INSERT_TAIL(&pkcs11_providers, p, next); -+ p->refcount++; /* add to provider list */ -+ return p; -+ } else { -+ m = xcalloc(1, sizeof(*m)); -+ p->module = m; -+ m->refcount++; - } -+ - /* open shared pkcs11-libarary */ -- if ((handle = dlopen(provider_id, RTLD_NOW)) == NULL) { -- error("dlopen %s failed: %s", provider_id, dlerror()); -+ if ((handle = dlopen(provider_module, RTLD_NOW)) == NULL) { -+ error("dlopen %s failed: %s", provider_module, dlerror()); - goto fail; - } - if ((getfunctionlist = dlsym(handle, "C_GetFunctionList")) == NULL) { - error("dlsym(C_GetFunctionList) failed: %s", dlerror()); - goto fail; - } -- p = xcalloc(1, sizeof(*p)); -- p->name = xstrdup(provider_id); -- p->handle = handle; -+ m->handle = handle; - /* setup the pkcs11 callbacks */ - if ((rv = (*getfunctionlist)(&f)) != CKR_OK) { - error("C_GetFunctionList for provider %s failed: %lu", -- provider_id, rv); -+ provider_module, rv); - goto fail; - } -- p->function_list = f; -+ m->function_list = f; - if ((rv = f->C_Initialize(NULL)) != CKR_OK) { - error("C_Initialize for provider %s failed: %lu", -- provider_id, rv); -+ provider_module, rv); - goto fail; - } - need_finalize = 1; -- if ((rv = f->C_GetInfo(&p->info)) != CKR_OK) { -+ if ((rv = f->C_GetInfo(&m->info)) != CKR_OK) { - error("C_GetInfo for provider %s failed: %lu", -- provider_id, rv); -+ provider_module, rv); - goto fail; - } -- rmspace(p->info.manufacturerID, sizeof(p->info.manufacturerID)); -- rmspace(p->info.libraryDescription, sizeof(p->info.libraryDescription)); -+ rmspace(m->info.manufacturerID, sizeof(m->info.manufacturerID)); -+ if (uri->lib_manuf != NULL && -+ strcmp(uri->lib_manuf, m->info.manufacturerID)) { -+ debug("%s: Skipping provider %s not matching library_manufacturer", -+ __func__, m->info.manufacturerID); -+ goto fail; -+ } -+ rmspace(m->info.libraryDescription, sizeof(m->info.libraryDescription)); - debug("provider %s: manufacturerID <%s> cryptokiVersion %d.%d" - " libraryDescription <%s> libraryVersion %d.%d", -- provider_id, -- p->info.manufacturerID, -- p->info.cryptokiVersion.major, -- p->info.cryptokiVersion.minor, -- p->info.libraryDescription, -- p->info.libraryVersion.major, -- p->info.libraryVersion.minor); -- if ((rv = f->C_GetSlotList(CK_TRUE, NULL, &p->nslots)) != CKR_OK) { -+ provider_module, -+ m->info.manufacturerID, -+ m->info.cryptokiVersion.major, -+ m->info.cryptokiVersion.minor, -+ m->info.libraryDescription, -+ m->info.libraryVersion.major, -+ m->info.libraryVersion.minor); -+ -+ if ((rv = f->C_GetSlotList(CK_TRUE, NULL, &m->nslots)) != CKR_OK) { - error("C_GetSlotList failed: %lu", rv); - goto fail; - } -- if (p->nslots == 0) { -+ if (m->nslots == 0) { - debug("%s: provider %s returned no slots", __func__, -- provider_id); -+ provider_module); - goto fail; - } -- p->slotlist = xcalloc(p->nslots, sizeof(CK_SLOT_ID)); -- if ((rv = f->C_GetSlotList(CK_TRUE, p->slotlist, &p->nslots)) -+ m->slotlist = xcalloc(m->nslots, sizeof(CK_SLOT_ID)); -+ if ((rv = f->C_GetSlotList(CK_TRUE, m->slotlist, &m->nslots)) - != CKR_OK) { - error("C_GetSlotList for provider %s failed: %lu", -- provider_id, rv); -+ provider_module, rv); - goto fail; - } -- p->slotinfo = xcalloc(p->nslots, sizeof(struct pkcs11_slotinfo)); -+ m->slotinfo = xcalloc(m->nslots, sizeof(struct pkcs11_slotinfo)); -+ m->valid = 1; - p->valid = 1; -- nkeys = 0; -- for (i = 0; i < p->nslots; i++) { -- token = &p->slotinfo[i].token; -- if ((rv = f->C_GetTokenInfo(p->slotlist[i], token)) -+ -+ for (i = 0; i < m->nslots; i++) { -+ token = &m->slotinfo[i].token; -+ if ((rv = f->C_GetTokenInfo(m->slotlist[i], token)) - != CKR_OK) { - error("C_GetTokenInfo for provider %s slot %lu " -- "failed: %lu", provider_id, (unsigned long)i, rv); -+ "failed: %lu", provider_module, (unsigned long)i, rv); - continue; - } - if ((token->flags & CKF_TOKEN_INITIALIZED) == 0) { -- debug2("%s: ignoring uninitialised token in " -- "provider %s slot %lu", __func__, -- provider_id, (unsigned long)i); - continue; - } - rmspace(token->label, sizeof(token->label)); - rmspace(token->manufacturerID, sizeof(token->manufacturerID)); - rmspace(token->model, sizeof(token->model)); - rmspace(token->serialNumber, sizeof(token->serialNumber)); -+ } -+ m->module_path = provider_module; -+ provider_module = NULL; -+ -+ /* insert unconditionally -- remove if there will be no keys later */ -+ TAILQ_INSERT_TAIL(&pkcs11_providers, p, next); -+ p->refcount++; /* add to provider list */ -+ return p; -+ -+fail: -+ if (need_finalize && (rv = f->C_Finalize(NULL)) != CKR_OK) -+ error("C_Finalize for provider %s failed: %lu", -+ provider_module, rv); -+ free(provider_module); -+ free(p); -+ if (handle) -+ dlclose(handle); -+ return NULL; -+} -+ -+int -+pkcs11_add_provider_by_uri(struct pkcs11_uri *uri, char *pin, struct sshkey ***keyp) -+{ -+ int nkeys; -+ struct pkcs11_provider *p = NULL; -+ CK_TOKEN_INFO *token; -+ CK_ULONG i; -+ char *provider_uri = pkcs11_uri_get(uri); -+ -+ debug("%s: called, provider_uri = %s", __func__, provider_uri); -+ -+ *keyp = NULL; -+ if ((p = pkcs11_provider_initialize(uri)) == NULL) { -+ debug("%s: failed to initialize provider: %s", -+ __func__, provider_uri); -+ goto fail; -+ } -+ -+ nkeys = 0; -+ for (i = 0; i < p->module->nslots; i++) { -+ token = &p->module->slotinfo[i].token; -+ if ((token->flags & CKF_TOKEN_INITIALIZED) == 0) { -+ debug2("%s: ignoring uninitialised token in " -+ "provider %s slot %lu", __func__, -+ provider_uri, (unsigned long)i); -+ continue; -+ } -+ if (uri->token != NULL && -+ strcmp(token->label, uri->token) != 0) { -+ debug2("%s: ignoring token not matching label (%s) " -+ "specified by PKCS#11 URI in slot %lu", __func__, -+ token->label, (unsigned long)i); -+ continue; -+ } -+ if (uri->manuf != NULL && -+ strcmp(token->manufacturerID, uri->manuf) != 0) { -+ debug2("%s: ignoring token not matching requrested " -+ "manufacturerID (%s) specified by PKCS#11 URI in " -+ "slot %lu", __func__, -+ token->manufacturerID, (unsigned long)i); -+ continue; -+ } - debug("provider %s slot %lu: label <%s> manufacturerID <%s> " - "model <%s> serial <%s> flags 0x%lx", -- provider_id, (unsigned long)i, -+ provider_uri, (unsigned long)i, - token->label, token->manufacturerID, token->model, - token->serialNumber, token->flags); -- /* open session, login with pin and retrieve public keys */ -- if (pkcs11_open_session(p, i, pin) == 0) -- pkcs11_fetch_keys(p, i, keyp, &nkeys); -+ /* open session if not yet opened, login with pin -+ * and retrieve public keys */ -+ if ((p->module->slotinfo[i].session != 0) || -+ pkcs11_open_session(p, i, pin) == 0) -+ pkcs11_fetch_keys(p, i, keyp, &nkeys, uri); - } - if (nkeys > 0) { -- TAILQ_INSERT_TAIL(&pkcs11_providers, p, next); -- p->refcount++; /* add to provider list */ -+ free(provider_uri); - return (nkeys); - } -- debug("%s: provider %s returned no keys", __func__, provider_id); -+ debug("%s: provider %s returned no keys", __func__, provider_uri); - /* don't add the provider, since it does not have any keys */ - fail: -- if (need_finalize && (rv = f->C_Finalize(NULL)) != CKR_OK) -- error("C_Finalize for provider %s failed: %lu", -- provider_id, rv); - if (p) { -- free(p->slotlist); -- free(p->slotinfo); -- free(p); -+ pkcs11_provider_unref(p); - } -- if (handle) -- dlclose(handle); -+ free(provider_uri); - return (-1); - } - -diff --git a/ssh-pkcs11.h b/ssh-pkcs11.h -index 0ced74f2..c63a88f6 100644 ---- a/ssh-pkcs11.h -+++ b/ssh-pkcs11.h -@@ -14,10 +14,15 @@ - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ -+ -+#include "ssh-pkcs11-uri.h" -+ - int pkcs11_init(int); - void pkcs11_terminate(void); - int pkcs11_add_provider(char *, char *, struct sshkey ***); -+int pkcs11_add_provider_by_uri(struct pkcs11_uri *, char *, struct sshkey ***); - int pkcs11_del_provider(char *); -+int pkcs11_uri_write(const struct sshkey *, FILE *); - - #if !defined(WITH_OPENSSL) && defined(ENABLE_PKCS11) - #undef ENABLE_PKCS11 -diff --git a/ssh.c b/ssh.c -index d3619fe2..180eb2e0 100644 ---- a/ssh.c -+++ b/ssh.c -@@ -769,6 +769,14 @@ main(int ac, char **av) - options.gss_deleg_creds = 1; - break; - case 'i': -+#ifdef ENABLE_PKCS11 -+ if (strlen(optarg) >= strlen(PKCS11_URI_SCHEME) && -+ strncmp(optarg, PKCS11_URI_SCHEME, -+ strlen(PKCS11_URI_SCHEME)) == 0) { -+ add_identity_file(&options, NULL, optarg, 1); -+ break; -+ } -+#endif - p = tilde_expand_filename(optarg, getuid()); - if (stat(p, &st) < 0) - fprintf(stderr, "Warning: Identity file %s " -@@ -1999,6 +2007,45 @@ ssh_session2(struct ssh *ssh, struct passwd *pw) - options.escape_char : SSH_ESCAPECHAR_NONE, id); - } - -+#ifdef ENABLE_PKCS11 -+static void -+load_pkcs11_identity(char *pkcs11_uri, char *identity_files[], -+ struct sshkey *identity_keys[], int *n_ids) -+{ -+ int nkeys, i; -+ struct sshkey **keys; -+ struct pkcs11_uri *uri; -+ -+ debug("identity file '%s' from pkcs#11", pkcs11_uri); -+ uri = pkcs11_uri_init(); -+ if (uri == NULL) -+ fatal("Failed to init PCKS#11 URI"); -+ -+ if (pkcs11_uri_parse(pkcs11_uri, uri) != 0) -+ fatal("Failed to parse PKCS#11 URI %s", pkcs11_uri); -+ -+ /* we need to merge URI and provider together */ -+ if (options.pkcs11_provider != NULL && uri->module_path == NULL) -+ uri->module_path = strdup(options.pkcs11_provider); -+ -+ if (options.num_identity_files < SSH_MAX_IDENTITY_FILES && -+ (nkeys = pkcs11_add_provider_by_uri(uri, NULL, &keys)) > 0) { -+ for (i = 0; i < nkeys; i++) { -+ if (*n_ids >= SSH_MAX_IDENTITY_FILES) { -+ sshkey_free(keys[i]); -+ continue; -+ } -+ identity_keys[*n_ids] = keys[i]; -+ identity_files[*n_ids] = pkcs11_uri_get(uri); -+ (*n_ids)++; -+ } -+ free(keys); -+ } -+ -+ pkcs11_uri_cleanup(uri); -+} -+#endif /* ENABLE_PKCS11 */ -+ - /* Loads all IdentityFile and CertificateFile keys */ - static void - load_public_identity_files(struct passwd *pw) -@@ -2011,10 +2058,6 @@ load_public_identity_files(struct passwd *pw) - 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; -- int nkeys; --#endif /* PKCS11 */ - - n_ids = n_certs = 0; - memset(identity_files, 0, sizeof(identity_files)); -@@ -2023,32 +2066,46 @@ load_public_identity_files(struct passwd *pw) - 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)) > 0) { -- for (i = 0; i < nkeys; i++) { -- if (n_ids >= SSH_MAX_IDENTITY_FILES) { -- sshkey_free(keys[i]); -- continue; -- } -- identity_keys[n_ids] = keys[i]; -- identity_files[n_ids] = -- xstrdup(options.pkcs11_provider); /* XXX */ -- n_ids++; -- } -- free(keys); -+ /* handle fallback from PKCS11Provider option */ -+ pkcs11_init(!options.batch_mode); -+ -+ if (options.pkcs11_provider != NULL) { -+ struct pkcs11_uri *uri; -+ -+ uri = pkcs11_uri_init(); -+ if (uri == NULL) -+ fatal("Failed to init PCKS#11 URI"); -+ -+ /* Construct simple PKCS#11 URI to simplify access */ -+ uri->module_path = strdup(options.pkcs11_provider); -+ -+ /* Add it as any other IdentityFile */ -+ cp = pkcs11_uri_get(uri); -+ add_identity_file(&options, NULL, cp, 1); -+ free(cp); -+ -+ pkcs11_uri_cleanup(uri); - } - #endif /* ENABLE_PKCS11 */ - for (i = 0; i < options.num_identity_files; i++) { -+ char *name = options.identity_files[i]; - if (n_ids >= SSH_MAX_IDENTITY_FILES || -- strcasecmp(options.identity_files[i], "none") == 0) { -+ strcasecmp(name, "none") == 0) { - free(options.identity_files[i]); - options.identity_files[i] = NULL; - continue; - } -- cp = tilde_expand_filename(options.identity_files[i], getuid()); -+#ifdef ENABLE_PKCS11 -+ if (strlen(name) >= strlen(PKCS11_URI_SCHEME) && -+ strncmp(name, PKCS11_URI_SCHEME, -+ strlen(PKCS11_URI_SCHEME)) == 0) { -+ load_pkcs11_identity(name, identity_files, -+ identity_keys, &n_ids); -+ free(options.identity_files[i]); -+ continue; -+ } -+#endif /* ENABLE_PKCS11 */ -+ cp = tilde_expand_filename(name, getuid()); - filename = percent_expand(cp, "d", pw->pw_dir, - "u", pw->pw_name, "l", thishost, "h", host, - "r", options.user, (char *)NULL); -diff --git a/ssh_config.5 b/ssh_config.5 -index 71705cab..e0266609 100644 ---- a/ssh_config.5 -+++ b/ssh_config.5 -@@ -919,6 +919,19 @@ may also be used in conjunction with - .Cm CertificateFile - in order to provide any certificate also needed for authentication with - the identity. -+.Pp -+The authentication identity can be also specified in a form of PKCS#11 URI -+starting with a string -+.Cm pkcs11: . -+There is supported a subset of the PKCS#11 URI as defined -+in RFC 7512 (implemented path arguments -+.Cm id , -+.Cm manufacturer , -+.Cm object , -+.Cm token -+and query argument -+.Cm module-path -+). The URI can not be in quotes. - .It Cm IgnoreUnknown - Specifies a pattern-list of unknown options to be ignored if they are - encountered in configuration parsing. diff --git a/openssh-7.7p1-fips.patch b/openssh-7.7p1-fips.patch index 084c903..0cbd22f 100644 --- a/openssh-7.7p1-fips.patch +++ b/openssh-7.7p1-fips.patch @@ -1,70 +1,6 @@ -diff -up openssh-7.7p1/cipher.c.fips openssh-7.7p1/cipher.c ---- openssh-7.7p1/cipher.c.fips 2018-08-08 10:08:40.814719906 +0200 -+++ openssh-7.7p1/cipher.c 2018-08-08 10:08:40.821719965 +0200 -@@ -39,6 +39,8 @@ - - #include - -+#include -+ - #include - #include - #include -@@ -90,6 +92,33 @@ static const struct sshcipher ciphers[] - { NULL, 0, 0, 0, 0, 0, NULL } - }; - -+static const struct sshcipher fips_ciphers[] = { -+#ifdef WITH_OPENSSL -+ { "3des-cbc", 8, 24, 0, 0, CFLAG_CBC, EVP_des_ede3_cbc }, -+ { "aes128-cbc", 16, 16, 0, 0, CFLAG_CBC, EVP_aes_128_cbc }, -+ { "aes192-cbc", 16, 24, 0, 0, CFLAG_CBC, EVP_aes_192_cbc }, -+ { "aes256-cbc", 16, 32, 0, 0, CFLAG_CBC, EVP_aes_256_cbc }, -+ { "rijndael-cbc@lysator.liu.se", -+ 16, 32, 0, 0, CFLAG_CBC, EVP_aes_256_cbc }, -+ { "aes128-ctr", 16, 16, 0, 0, 0, EVP_aes_128_ctr }, -+ { "aes192-ctr", 16, 24, 0, 0, 0, EVP_aes_192_ctr }, -+ { "aes256-ctr", 16, 32, 0, 0, 0, EVP_aes_256_ctr }, -+# ifdef OPENSSL_HAVE_EVPGCM -+ { "aes128-gcm@openssh.com", -+ 16, 16, 12, 16, 0, EVP_aes_128_gcm }, -+ { "aes256-gcm@openssh.com", -+ 16, 32, 12, 16, 0, EVP_aes_256_gcm }, -+# endif /* OPENSSL_HAVE_EVPGCM */ -+#else -+ { "aes128-ctr", 16, 16, 0, 0, CFLAG_AESCTR, NULL }, -+ { "aes192-ctr", 16, 24, 0, 0, CFLAG_AESCTR, NULL }, -+ { "aes256-ctr", 16, 32, 0, 0, CFLAG_AESCTR, NULL }, -+#endif -+ { "none", 8, 0, 0, 0, CFLAG_NONE, NULL }, -+ -+ { NULL, 0, 0, 0, 0, 0, NULL } -+}; -+ - /*--*/ - - /* Returns a comma-separated list of supported ciphers. */ -@@ -100,7 +129,7 @@ cipher_alg_list(char sep, int auth_only) - size_t nlen, rlen = 0; - const struct sshcipher *c; - -- for (c = ciphers; c->name != NULL; c++) { -+ for (c = FIPS_mode() ? fips_ciphers : ciphers; c->name != NULL; c++) { - if ((c->flags & CFLAG_INTERNAL) != 0) - continue; - if (auth_only && c->auth_len == 0) -@@ -172,7 +201,7 @@ const struct sshcipher * - cipher_by_name(const char *name) - { - const struct sshcipher *c; -- for (c = ciphers; c->name != NULL; c++) -+ for (c = FIPS_mode() ? fips_ciphers : ciphers; c->name != NULL; c++) - if (strcmp(c->name, name) == 0) - return c; - return NULL; -diff -up openssh-7.7p1/cipher-ctr.c.fips openssh-7.7p1/cipher-ctr.c ---- openssh-7.7p1/cipher-ctr.c.fips 2018-08-08 10:08:40.709719021 +0200 -+++ openssh-7.7p1/cipher-ctr.c 2018-08-08 10:08:40.821719965 +0200 +diff -up openssh-7.9p1/cipher-ctr.c.fips openssh-7.9p1/cipher-ctr.c +--- openssh-7.9p1/cipher-ctr.c.fips 2019-03-11 17:06:37.519877082 +0100 ++++ openssh-7.9p1/cipher-ctr.c 2019-03-11 17:06:37.620878031 +0100 @@ -179,7 +179,8 @@ evp_aes_128_ctr(void) aes_ctr.do_cipher = ssh_aes_ctr; #ifndef SSH_OLD_EVP @@ -75,10 +11,10 @@ diff -up openssh-7.7p1/cipher-ctr.c.fips openssh-7.7p1/cipher-ctr.c #endif return (&aes_ctr); } -diff -up openssh-7.7p1/clientloop.c.fips openssh-7.7p1/clientloop.c ---- openssh-7.7p1/clientloop.c.fips 2018-08-08 10:08:40.769719527 +0200 -+++ openssh-7.7p1/clientloop.c 2018-08-08 10:08:40.822719973 +0200 -@@ -1978,7 +1978,8 @@ key_accepted_by_hostkeyalgs(const struct +diff -up openssh-7.9p1/clientloop.c.fips openssh-7.9p1/clientloop.c +--- openssh-7.9p1/clientloop.c.fips 2019-03-11 17:06:37.523877120 +0100 ++++ openssh-7.9p1/clientloop.c 2019-03-11 17:06:37.620878031 +0100 +@@ -2014,7 +2014,8 @@ key_accepted_by_hostkeyalgs(const struct { const char *ktype = sshkey_ssh_name(key); const char *hostkeyalgs = options.hostkeyalgorithms != NULL ? @@ -88,86 +24,75 @@ diff -up openssh-7.7p1/clientloop.c.fips openssh-7.7p1/clientloop.c if (key == NULL || key->type == KEY_UNSPEC) return 0; -diff -up openssh-7.7p1/dh.h.fips openssh-7.7p1/dh.h ---- openssh-7.7p1/dh.h.fips 2018-04-02 07:38:28.000000000 +0200 -+++ openssh-7.7p1/dh.h 2018-08-08 10:08:40.822719973 +0200 -@@ -51,6 +51,7 @@ u_int dh_estimate(int); - * Miniumum increased in light of DH precomputation attacks. - */ - #define DH_GRP_MIN 2048 -+#define DH_GRP_MIN_FIPS 2048 - #define DH_GRP_MAX 8192 +diff -up openssh-7.9p1/dh.c.fips openssh-7.9p1/dh.c +--- openssh-7.9p1/dh.c.fips 2018-10-17 02:01:20.000000000 +0200 ++++ openssh-7.9p1/dh.c 2019-03-11 17:08:11.769763057 +0100 +@@ -152,6 +152,12 @@ choose_dh(int min, int wantbits, int max + int best, bestcount, which, linenum; + struct dhgroup dhg; - /* -diff -up openssh-7.7p1/entropy.c.fips openssh-7.7p1/entropy.c ---- openssh-7.7p1/entropy.c.fips 2018-08-08 10:08:40.698718928 +0200 -+++ openssh-7.7p1/entropy.c 2018-08-08 10:08:40.822719973 +0200 -@@ -217,6 +217,9 @@ seed_rng(void) - fatal("OpenSSL version mismatch. Built against %lx, you " - "have %lx", (u_long)OPENSSL_VERSION_NUMBER, SSLeay()); - -+ /* clean the PRNG status when exiting the program */ -+ atexit(RAND_cleanup); ++ if (FIPS_mode()) { ++ verbose("Using arbitrary primes is not allowed in FIPS mode." ++ " Falling back to known groups."); ++ return (dh_new_group_fallback(max)); ++ } + - #ifndef OPENSSL_PRNG_ONLY - if (RAND_status() == 1) { - debug3("RNG is ready, skipping seeding"); -diff -up openssh-7.7p1/kex.c.fips openssh-7.7p1/kex.c ---- openssh-7.7p1/kex.c.fips 2018-08-08 10:08:40.815719915 +0200 -+++ openssh-7.7p1/kex.c 2018-08-08 10:11:24.109081924 +0200 -@@ -35,6 +35,7 @@ - #ifdef WITH_OPENSSL - #include - #include -+#include - #endif + if ((f = fopen(_PATH_DH_MODULI, "r")) == NULL) { + logit("WARNING: could not open %s (%s), using fixed modulus", + _PATH_DH_MODULI, strerror(errno)); +@@ -489,4 +495,38 @@ dh_estimate(int bits) + return 8192; + } - #include "ssh2.h" -@@ -122,6 +123,26 @@ static const struct kexalg kexalgs[] = { - { NULL, -1, -1, -1}, - }; - -+static const struct kexalg kexalgs_fips[] = { -+ { 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 }, -+#ifdef HAVE_EVP_SHA256 -+ { KEX_DHGEX_SHA256, KEX_DH_GEX_SHA256, 0, SSH_DIGEST_SHA256 }, -+#endif -+#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 -+#endif -+ { NULL, -1, -1, -1}, -+}; ++/* ++ * Compares the received DH parameters with known-good groups, ++ * which might be either from group14, group16 or group18. ++ */ ++int ++dh_is_known_group(const DH *dh) ++{ ++ const BIGNUM *p, *g; ++ const BIGNUM *known_p, *known_g; ++ DH *known = NULL; ++ int bits = 0, rv = 0; + - char * - kex_alg_list(char sep) - { -@@ -129,7 +150,7 @@ kex_alg_list(char sep) - size_t nlen, rlen = 0; - const struct kexalg *k; ++ DH_get0_pqg(dh, &p, NULL, &g); ++ bits = BN_num_bits(p); ++ ++ if (bits <= 3072) { ++ known = dh_new_group14(); ++ } else if (bits <= 6144) { ++ known = dh_new_group16(); ++ } else { ++ known = dh_new_group18(); ++ } ++ ++ DH_get0_pqg(known, &known_p, NULL, &known_g); ++ ++ if (BN_cmp(g, known_g) == 0 && ++ BN_cmp(p, known_p) == 0) { ++ rv = 1; ++ } ++ ++ DH_free(known); ++ return rv; ++} ++ + #endif /* WITH_OPENSSL */ +diff -up openssh-7.9p1/dh.h.fips openssh-7.9p1/dh.h +--- openssh-7.9p1/dh.h.fips 2018-10-17 02:01:20.000000000 +0200 ++++ openssh-7.9p1/dh.h 2019-03-11 17:08:18.718828381 +0100 +@@ -43,6 +43,7 @@ DH *dh_new_group_fallback(int); -- for (k = kexalgs; k->name != NULL; k++) { -+ for (k = (FIPS_mode() ? kexalgs_fips : kexalgs); k->name != NULL; k++) { - if (ret != NULL) - ret[rlen++] = sep; - nlen = strlen(k->name); -@@ -149,7 +170,7 @@ kex_alg_by_name(const char *name) - { - const struct kexalg *k; + int dh_gen_key(DH *, int); + int dh_pub_is_valid(const DH *, const BIGNUM *); ++int dh_is_known_group(const DH *); -- for (k = kexalgs; k->name != NULL; k++) { -+ for (k = (FIPS_mode() ? kexalgs_fips : kexalgs); k->name != NULL; k++) { - if (strcmp(k->name, name) == 0) - return k; - #ifdef GSSAPI + u_int dh_estimate(int); + +diff -up openssh-7.9p1/kex.c.fips openssh-7.9p1/kex.c +--- openssh-7.9p1/kex.c.fips 2019-03-11 17:06:37.614877975 +0100 ++++ openssh-7.9p1/kex.c 2019-03-11 17:06:37.621878041 +0100 @@ -175,7 +196,10 @@ kex_names_valid(const char *names) for ((p = strsep(&cp, ",")); p && *p != '\0'; (p = strsep(&cp, ","))) { @@ -180,158 +105,39 @@ diff -up openssh-7.7p1/kex.c.fips openssh-7.7p1/kex.c free(s); return 0; } -diff -up openssh-7.7p1/kexgexc.c.fips openssh-7.7p1/kexgexc.c ---- openssh-7.7p1/kexgexc.c.fips 2018-04-02 07:38:28.000000000 +0200 -+++ openssh-7.7p1/kexgexc.c 2018-08-08 10:08:40.822719973 +0200 +diff -up openssh-7.9p1/kexgexc.c.fips openssh-7.9p1/kexgexc.c +--- openssh-7.9p1/kexgexc.c.fips 2018-10-17 02:01:20.000000000 +0200 ++++ openssh-7.9p1/kexgexc.c 2019-03-11 17:06:37.621878041 +0100 @@ -28,6 +28,7 @@ #ifdef WITH_OPENSSL -+#include ++#include #include #include -@@ -63,7 +64,7 @@ kexgex_client(struct ssh *ssh) +@@ -118,6 +119,10 @@ input_kex_dh_gex_group(int type, u_int32 + r = SSH_ERR_ALLOC_FAIL; + goto out; + } ++ if (FIPS_mode() && dh_is_known_group(kex->dh) == 0) { ++ r = SSH_ERR_INVALID_ARGUMENT; ++ goto out; ++ } + p = g = NULL; /* belong to kex->dh now */ - nbits = dh_estimate(kex->dh_need * 8); - -- kex->min = DH_GRP_MIN; -+ kex->min = FIPS_mode() ? DH_GRP_MIN_FIPS : DH_GRP_MIN; - kex->max = DH_GRP_MAX; - kex->nbits = nbits; - if (datafellows & SSH_BUG_DHGEX_LARGE) -diff -up openssh-7.7p1/kexgexs.c.fips openssh-7.7p1/kexgexs.c ---- openssh-7.7p1/kexgexs.c.fips 2018-04-02 07:38:28.000000000 +0200 -+++ openssh-7.7p1/kexgexs.c 2018-08-08 10:08:40.823719982 +0200 -@@ -82,9 +82,9 @@ input_kex_dh_gex_request(int type, u_int - kex->nbits = nbits; - kex->min = min; - kex->max = max; -- min = MAXIMUM(DH_GRP_MIN, min); -+ min = MAXIMUM(FIPS_mode() ? DH_GRP_MIN_FIPS : DH_GRP_MIN, min); - max = MINIMUM(DH_GRP_MAX, max); -- nbits = MAXIMUM(DH_GRP_MIN, nbits); -+ nbits = MAXIMUM(FIPS_mode() ? DH_GRP_MIN_FIPS : DH_GRP_MIN, nbits); - nbits = MINIMUM(DH_GRP_MAX, nbits); - - if (kex->max < kex->min || kex->nbits < kex->min || -diff -up openssh-7.7p1/mac.c.fips openssh-7.7p1/mac.c ---- openssh-7.7p1/mac.c.fips 2018-08-08 10:08:40.815719915 +0200 -+++ openssh-7.7p1/mac.c 2018-08-08 10:11:56.915352642 +0200 -@@ -27,6 +27,8 @@ - - #include - -+#include -+ - #include - #include - -@@ -54,7 +56,7 @@ struct macalg { - int etm; /* Encrypt-then-MAC */ - }; - --static const struct macalg macs[] = { -+static const struct macalg all_macs[] = { - /* Encrypt-and-MAC (encrypt-and-authenticate) variants */ - { "hmac-sha1", SSH_DIGEST, SSH_DIGEST_SHA1, 0, 0, 0, 0 }, - { "hmac-sha1-96", SSH_DIGEST, SSH_DIGEST_SHA1, 96, 0, 0, 0 }, -@@ -82,6 +84,24 @@ static const struct macalg macs[] = { - { NULL, 0, 0, 0, 0, 0, 0 } - }; - -+static const struct macalg fips_macs[] = { -+ /* Encrypt-and-MAC (encrypt-and-authenticate) variants */ -+ { "hmac-sha1", SSH_DIGEST, SSH_DIGEST_SHA1, 0, 0, 0, 0 }, -+#ifdef HAVE_EVP_SHA256 -+ { "hmac-sha2-256", SSH_DIGEST, SSH_DIGEST_SHA256, 0, 0, 0, 0 }, -+ { "hmac-sha2-512", SSH_DIGEST, SSH_DIGEST_SHA512, 0, 0, 0, 0 }, -+#endif -+ -+ /* Encrypt-then-MAC variants */ -+ { "hmac-sha1-etm@openssh.com", SSH_DIGEST, SSH_DIGEST_SHA1, 0, 0, 0, 1 }, -+#ifdef HAVE_EVP_SHA256 -+ { "hmac-sha2-256-etm@openssh.com", SSH_DIGEST, SSH_DIGEST_SHA256, 0, 0, 0, 1 }, -+ { "hmac-sha2-512-etm@openssh.com", SSH_DIGEST, SSH_DIGEST_SHA512, 0, 0, 0, 1 }, -+#endif -+ -+ { NULL, 0, 0, 0, 0, 0, 0 } -+}; -+ - /* Returns a list of supported MACs separated by the specified char. */ - char * - mac_alg_list(char sep) -@@ -90,7 +110,7 @@ mac_alg_list(char sep) - size_t nlen, rlen = 0; - const struct macalg *m; - -- for (m = macs; m->name != NULL; m++) { -+ for (m = FIPS_mode() ? fips_macs : all_macs; m->name != NULL; m++) { - if (ret != NULL) - ret[rlen++] = sep; - nlen = strlen(m->name); -@@ -129,7 +149,7 @@ mac_setup(struct sshmac *mac, char *name - { - const struct macalg *m; - -- for (m = macs; m->name != NULL; m++) { -+ for (m = FIPS_mode() ? fips_macs : all_macs; m->name != NULL; m++) { - if (strcmp(name, m->name) != 0) - continue; - if (mac != NULL) -diff -up openssh-7.7p1/Makefile.in.fips openssh-7.7p1/Makefile.in ---- openssh-7.7p1/Makefile.in.fips 2018-08-08 10:08:40.815719915 +0200 -+++ openssh-7.7p1/Makefile.in 2018-08-08 10:08:40.823719982 +0200 -@@ -179,25 +179,25 @@ libssh.a: $(LIBSSH_OBJS) - $(RANLIB) $@ - - ssh$(EXEEXT): $(LIBCOMPAT) libssh.a $(SSHOBJS) -- $(LD) -o $@ $(SSHOBJS) $(LDFLAGS) -lssh -lopenbsd-compat $(SSHLIBS) $(LIBS) $(GSSLIBS) -+ $(LD) -o $@ $(SSHOBJS) $(LDFLAGS) -lssh -lopenbsd-compat -lfipscheck $(SSHLIBS) $(LIBS) $(GSSLIBS) - - sshd$(EXEEXT): libssh.a $(LIBCOMPAT) $(SSHDOBJS) -- $(LD) -o $@ $(SSHDOBJS) $(LDFLAGS) -lssh -lopenbsd-compat $(SSHDLIBS) $(LIBS) $(GSSLIBS) $(K5LIBS) -+ $(LD) -o $@ $(SSHDOBJS) $(LDFLAGS) -lssh -lopenbsd-compat -lfipscheck $(SSHDLIBS) $(LIBS) $(GSSLIBS) $(K5LIBS) - - scp$(EXEEXT): $(LIBCOMPAT) libssh.a scp.o progressmeter.o - $(LD) -o $@ scp.o progressmeter.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) - - ssh-add$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-add.o -- $(LD) -o $@ ssh-add.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) -+ $(LD) -o $@ ssh-add.o $(LDFLAGS) -lssh -lopenbsd-compat -lfipscheck $(LIBS) - - ssh-agent$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-agent.o ssh-pkcs11-client.o -- $(LD) -o $@ ssh-agent.o ssh-pkcs11-client.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) -+ $(LD) -o $@ ssh-agent.o ssh-pkcs11-client.o $(LDFLAGS) -lssh -lopenbsd-compat -lfipscheck $(LIBS) - - ssh-keygen$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-keygen.o -- $(LD) -o $@ ssh-keygen.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) -+ $(LD) -o $@ ssh-keygen.o $(LDFLAGS) -lssh -lopenbsd-compat -lfipscheck $(LIBS) - - ssh-keysign$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-keysign.o readconf.o uidswap.o -- $(LD) -o $@ ssh-keysign.o readconf.o uidswap.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) -+ $(LD) -o $@ ssh-keysign.o readconf.o uidswap.o $(LDFLAGS) -lssh -lopenbsd-compat -lfipscheck $(LIBS) - - ssh-pkcs11-helper$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-pkcs11-helper.o ssh-pkcs11.o - $(LD) -o $@ ssh-pkcs11-helper.o ssh-pkcs11.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) -@@ -215,7 +215,7 @@ ssh-cavs$(EXEEXT): $(LIBCOMPAT) libssh.a - $(LD) -o $@ ssh-cavs.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) - - ssh-keyscan$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-keyscan.o -- $(LD) -o $@ ssh-keyscan.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh $(LIBS) -+ $(LD) -o $@ ssh-keyscan.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh -lfipscheck $(LIBS) - - sftp-server$(EXEEXT): $(LIBCOMPAT) libssh.a sftp.o sftp-common.o sftp-server.o sftp-server-main.o - $(LD) -o $@ sftp-server.o sftp-common.o sftp-server-main.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) -diff -up openssh-7.7p1/myproposal.h.fips openssh-7.7p1/myproposal.h ---- openssh-7.7p1/myproposal.h.fips 2018-04-02 07:38:28.000000000 +0200 -+++ openssh-7.7p1/myproposal.h 2018-08-08 10:08:40.823719982 +0200 -@@ -114,6 +114,14 @@ + /* generate and send 'e', client DH public key */ +diff -up openssh-7.9p1/myproposal.h.fips openssh-7.9p1/myproposal.h +--- openssh-7.9p1/myproposal.h.fips 2018-10-17 02:01:20.000000000 +0200 ++++ openssh-7.9p1/myproposal.h 2019-03-11 17:06:37.621878041 +0100 +@@ -116,6 +116,16 @@ "rsa-sha2-256," \ "ssh-rsa" +#define KEX_FIPS_PK_ALG \ + HOSTKEY_ECDSA_CERT_METHODS \ ++ "rsa-sha2-512-cert-v01@openssh.com," \ ++ "rsa-sha2-256-cert-v01@openssh.com," \ + "ssh-rsa-cert-v01@openssh.com," \ + HOSTKEY_ECDSA_METHODS \ + "rsa-sha2-512," \ @@ -341,7 +147,7 @@ diff -up openssh-7.7p1/myproposal.h.fips openssh-7.7p1/myproposal.h /* the actual algorithms */ #define KEX_SERVER_ENCRYPT \ -@@ -137,6 +145,38 @@ +@@ -139,6 +147,38 @@ #define KEX_CLIENT_MAC KEX_SERVER_MAC @@ -377,16 +183,16 @@ diff -up openssh-7.7p1/myproposal.h.fips openssh-7.7p1/myproposal.h + "hmac-sha1" +#endif + - #else /* WITH_OPENSSL */ - - #define KEX_SERVER_KEX \ -diff -up openssh-7.7p1/readconf.c.fips openssh-7.7p1/readconf.c ---- openssh-7.7p1/readconf.c.fips 2018-08-08 10:08:40.769719527 +0200 -+++ openssh-7.7p1/readconf.c 2018-08-08 10:08:40.824719990 +0200 -@@ -2081,17 +2081,18 @@ fill_default_options(Options * options) - all_mac = mac_alg_list(','); + /* Not a KEX value, but here so all the algorithm defaults are together */ + #define SSH_ALLOWED_CA_SIGALGS \ + "ecdsa-sha2-nistp256," \ +diff -up openssh-7.9p1/readconf.c.fips openssh-7.9p1/readconf.c +--- openssh-7.9p1/readconf.c.fips 2019-03-11 17:06:37.601877853 +0100 ++++ openssh-7.9p1/readconf.c 2019-03-11 17:06:37.622878050 +0100 +@@ -2178,18 +2178,19 @@ fill_default_options(Options * options) all_kex = kex_alg_list(','); all_key = sshkey_alg_list(0, 0, 1, ','); + all_sig = sshkey_alg_list(0, 1, 1, ','); -#define ASSEMBLE(what, defaults, all) \ +#define ASSEMBLE(what, defaults, fips_defaults, all) \ do { \ @@ -396,22 +202,24 @@ diff -up openssh-7.7p1/readconf.c.fips openssh-7.7p1/readconf.c + all)) != 0) \ fatal("%s: %s: %s", __func__, #what, ssh_err(r)); \ } while (0) -- ASSEMBLE(ciphers, KEX_SERVER_ENCRYPT, all_cipher); -- ASSEMBLE(macs, KEX_SERVER_MAC, all_mac); -- ASSEMBLE(kex_algorithms, KEX_SERVER_KEX, all_kex); +- ASSEMBLE(ciphers, KEX_CLIENT_ENCRYPT, all_cipher); +- ASSEMBLE(macs, KEX_CLIENT_MAC, all_mac); +- ASSEMBLE(kex_algorithms, KEX_CLIENT_KEX, all_kex); - ASSEMBLE(hostbased_key_types, KEX_DEFAULT_PK_ALG, all_key); - ASSEMBLE(pubkey_key_types, KEX_DEFAULT_PK_ALG, all_key); -+ ASSEMBLE(ciphers, KEX_SERVER_ENCRYPT, KEX_FIPS_ENCRYPT, all_cipher); -+ ASSEMBLE(macs, KEX_SERVER_MAC, KEX_FIPS_MAC, all_mac); -+ ASSEMBLE(kex_algorithms, KEX_SERVER_KEX, KEX_DEFAULT_KEX_FIPS, all_kex); +- ASSEMBLE(ca_sign_algorithms, SSH_ALLOWED_CA_SIGALGS, all_sig); ++ ASSEMBLE(ciphers, KEX_CLIENT_ENCRYPT, KEX_FIPS_ENCRYPT, all_cipher); ++ ASSEMBLE(macs, KEX_CLIENT_MAC, KEX_FIPS_MAC, all_mac); ++ ASSEMBLE(kex_algorithms, KEX_CLIENT_KEX, KEX_DEFAULT_KEX_FIPS, all_kex); + ASSEMBLE(hostbased_key_types, KEX_DEFAULT_PK_ALG, KEX_FIPS_PK_ALG, all_key); + ASSEMBLE(pubkey_key_types, KEX_DEFAULT_PK_ALG, KEX_FIPS_PK_ALG, all_key); ++ ASSEMBLE(ca_sign_algorithms, SSH_ALLOWED_CA_SIGALGS, KEX_FIPS_PK_ALG, all_sig); #undef ASSEMBLE free(all_cipher); free(all_mac); -diff -up openssh-7.7p1/sandbox-seccomp-filter.c.fips openssh-7.7p1/sandbox-seccomp-filter.c ---- openssh-7.7p1/sandbox-seccomp-filter.c.fips 2018-08-08 10:08:40.794719737 +0200 -+++ openssh-7.7p1/sandbox-seccomp-filter.c 2018-08-08 10:08:40.824719990 +0200 +diff -up openssh-7.9p1/sandbox-seccomp-filter.c.fips openssh-7.9p1/sandbox-seccomp-filter.c +--- openssh-7.9p1/sandbox-seccomp-filter.c.fips 2019-03-11 17:06:37.586877712 +0100 ++++ openssh-7.9p1/sandbox-seccomp-filter.c 2019-03-11 17:06:37.622878050 +0100 @@ -137,6 +137,9 @@ static const struct sock_filter preauth_ #ifdef __NR_open SC_DENY(__NR_open, EACCES), @@ -422,13 +230,13 @@ diff -up openssh-7.7p1/sandbox-seccomp-filter.c.fips openssh-7.7p1/sandbox-secco #ifdef __NR_openat SC_DENY(__NR_openat, EACCES), #endif -diff -up openssh-7.7p1/servconf.c.fips openssh-7.7p1/servconf.c ---- openssh-7.7p1/servconf.c.fips 2018-08-08 10:08:40.778719603 +0200 -+++ openssh-7.7p1/servconf.c 2018-08-08 10:08:40.824719990 +0200 -@@ -196,17 +196,18 @@ option_clear_or_none(const char *o) - all_mac = mac_alg_list(','); +diff -up openssh-7.9p1/servconf.c.fips openssh-7.9p1/servconf.c +--- openssh-7.9p1/servconf.c.fips 2019-03-11 17:06:37.568877543 +0100 ++++ openssh-7.9p1/servconf.c 2019-03-11 17:06:37.622878050 +0100 +@@ -209,18 +209,19 @@ assemble_algorithms(ServerOptions *o) all_kex = kex_alg_list(','); all_key = sshkey_alg_list(0, 0, 1, ','); + all_sig = sshkey_alg_list(0, 1, 1, ','); -#define ASSEMBLE(what, defaults, all) \ +#define ASSEMBLE(what, defaults, fips_defaults, all) \ do { \ @@ -443,129 +251,65 @@ diff -up openssh-7.7p1/servconf.c.fips openssh-7.7p1/servconf.c - ASSEMBLE(hostkeyalgorithms, KEX_DEFAULT_PK_ALG, all_key); - ASSEMBLE(hostbased_key_types, KEX_DEFAULT_PK_ALG, all_key); - ASSEMBLE(pubkey_key_types, KEX_DEFAULT_PK_ALG, all_key); +- ASSEMBLE(ca_sign_algorithms, SSH_ALLOWED_CA_SIGALGS, all_sig); + ASSEMBLE(ciphers, KEX_SERVER_ENCRYPT, KEX_FIPS_ENCRYPT, all_cipher); + ASSEMBLE(macs, KEX_SERVER_MAC, KEX_FIPS_MAC, all_mac); + ASSEMBLE(kex_algorithms, KEX_SERVER_KEX, KEX_DEFAULT_KEX_FIPS, all_kex); + ASSEMBLE(hostkeyalgorithms, KEX_DEFAULT_PK_ALG, KEX_FIPS_PK_ALG, all_key); + ASSEMBLE(hostbased_key_types, KEX_DEFAULT_PK_ALG, KEX_FIPS_PK_ALG, all_key); + ASSEMBLE(pubkey_key_types, KEX_DEFAULT_PK_ALG, KEX_FIPS_PK_ALG, all_key); ++ ASSEMBLE(ca_sign_algorithms, SSH_ALLOWED_CA_SIGALGS, KEX_FIPS_PK_ALG, all_sig); #undef ASSEMBLE free(all_cipher); free(all_mac); -diff -up openssh-7.7p1/ssh.c.fips openssh-7.7p1/ssh.c ---- openssh-7.7p1/ssh.c.fips 2018-08-08 10:08:40.811719881 +0200 -+++ openssh-7.7p1/ssh.c 2018-08-08 10:08:40.825719999 +0200 -@@ -76,6 +76,8 @@ +diff -up openssh-7.9p1/ssh.c.fips openssh-7.9p1/ssh.c +--- openssh-7.9p1/ssh.c.fips 2019-03-11 17:06:37.602877862 +0100 ++++ openssh-7.9p1/ssh.c 2019-03-11 17:06:37.623878060 +0100 +@@ -76,6 +76,7 @@ #include #include #endif -+#include -+#include ++#include #include "openbsd-compat/openssl-compat.h" #include "openbsd-compat/sys-queue.h" -@@ -579,6 +581,14 @@ main(int ac, char **av) - sanitise_stdfd(); - - __progname = ssh_get_progname(av[0]); -+ SSLeay_add_all_algorithms(); -+ if (access("/etc/system-fips", F_OK) == 0) -+ if (! FIPSCHECK_verify(NULL, NULL)){ -+ if (FIPS_mode()) -+ fatal("FIPS integrity verification test failed."); -+ else -+ logit("FIPS integrity verification test failed."); -+ } - - #ifndef HAVE_SETPROCTITLE - /* Prepare for later setproctitle emulation */ -@@ -1045,7 +1055,6 @@ main(int ac, char **av) - host_arg = xstrdup(host); - - #ifdef WITH_OPENSSL -- OpenSSL_add_all_algorithms(); - ERR_load_crypto_strings(); - #endif - -@@ -1268,6 +1277,10 @@ main(int ac, char **av) - - seed_rng(); - -+ if (FIPS_mode()) { -+ logit("FIPS mode initialized"); -+ } +@@ -1283,6 +1294,10 @@ main(int ac, char **av) + dump_client_config(&options, host); + exit(0); + } + - if (options.user == NULL) - options.user = xstrdup(pw->pw_name); ++ if (FIPS_mode()) { ++ debug("FIPS mode initialized"); ++ } -diff -up openssh-7.7p1/sshconnect2.c.fips openssh-7.7p1/sshconnect2.c ---- openssh-7.7p1/sshconnect2.c.fips 2018-08-08 10:08:40.786719670 +0200 -+++ openssh-7.7p1/sshconnect2.c 2018-08-08 10:08:40.825719999 +0200 + if (muxclient_command != 0 && options.control_path == NULL) + fatal("No ControlPath specified for \"-O\" command"); +diff -up openssh-7.9p1/sshconnect2.c.fips openssh-7.9p1/sshconnect2.c +--- openssh-7.9p1/sshconnect2.c.fips 2019-03-11 17:06:37.580877655 +0100 ++++ openssh-7.9p1/sshconnect2.c 2019-03-11 17:06:37.623878060 +0100 @@ -44,6 +44,8 @@ #include #endif -+#include ++#include + #include "openbsd-compat/sys-queue.h" #include "xmalloc.h" -@@ -235,7 +237,8 @@ order_hostkeyalgs(char *host, struct soc - for (i = 0; i < options.num_system_hostfiles; i++) - load_hostkeys(hostkeys, hostname, options.system_hostfiles[i]); - +@@ -148,7 +150,8 @@ order_hostkeyalgs(char *host, struct soc + * Otherwise, prefer the host key algorithms that match known keys + * while keeping the ordering of HostkeyAlgorithms as much as possible. + */ - oavail = avail = xstrdup(KEX_DEFAULT_PK_ALG); + oavail = avail = xstrdup((FIPS_mode() + ? KEX_FIPS_PK_ALG : KEX_DEFAULT_PK_ALG)); maxlen = strlen(avail) + 1; first = xmalloc(maxlen); last = xmalloc(maxlen); -@@ -290,21 +293,26 @@ ssh_kex2(char *host, struct sockaddr *ho - - #ifdef GSSAPI - if (options.gss_keyex) { -- /* Add the GSSAPI mechanisms currently supported on this -- * client to the key exchange algorithm proposal */ -- orig = options.kex_algorithms; -- -- if (options.gss_trust_dns) -- gss_host = (char *)get_canonical_hostname(active_state, 1); -- else -- gss_host = host; -- -- gss = ssh_gssapi_client_mechanisms(gss_host, -- options.gss_client_identity, options.gss_kex_algorithms); -- if (gss) { -- debug("Offering GSSAPI proposal: %s", gss); -- xasprintf(&options.kex_algorithms, -- "%s,%s", gss, orig); -+ if (FIPS_mode()) { -+ logit("Disabling GSSAPIKeyExchange. Not usable in FIPS mode"); -+ options.gss_keyex = 0; -+ } else { -+ /* Add the GSSAPI mechanisms currently supported on this -+ * client to the key exchange algorithm proposal */ -+ orig = options.kex_algorithms; -+ -+ if (options.gss_trust_dns) -+ gss_host = (char *)get_canonical_hostname(active_state, 1); -+ else -+ gss_host = host; -+ -+ gss = ssh_gssapi_client_mechanisms(gss_host, -+ options.gss_client_identity, options.gss_kex_algorithms); -+ if (gss) { -+ debug("Offering GSSAPI proposal: %s", gss); -+ xasprintf(&options.kex_algorithms, -+ "%s,%s", gss, orig); -+ } - } - } - #endif -@@ -322,14 +330,16 @@ ssh_kex2(char *host, struct sockaddr *ho +@@ -229,14 +232,16 @@ ssh_kex2(struct ssh *ssh, char *host, st if (options.hostkeyalgorithms != NULL) { all_key = sshkey_alg_list(0, 0, 1, ','); - if (kex_assemble_names(&options.hostkeyalgorithms, + if (kex_assemble_names(&options.hostkeyalgorithms, - KEX_DEFAULT_PK_ALG, all_key) != 0) + (FIPS_mode() ? KEX_FIPS_PK_ALG : KEX_DEFAULT_PK_ALG), + all_key) != 0) @@ -581,9 +325,79 @@ diff -up openssh-7.7p1/sshconnect2.c.fips openssh-7.7p1/sshconnect2.c /* Prefer algorithms that we already have keys for */ myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = compat_pkalg_proposal( -diff -up openssh-7.7p1/sshd.c.fips openssh-7.7p1/sshd.c ---- openssh-7.7p1/sshd.c.fips 2018-08-08 10:08:40.818719940 +0200 -+++ openssh-7.7p1/sshd.c 2018-08-08 10:08:40.826720007 +0200 +@@ -201,35 +201,40 @@ ssh_kex2(char *host, struct sockaddr *ho + + #if defined(GSSAPI) && defined(WITH_OPENSSL) + if (options.gss_keyex) { +- /* Add the GSSAPI mechanisms currently supported on this +- * client to the key exchange algorithm proposal */ +- orig = myproposal[PROPOSAL_KEX_ALGS]; +- +- if (options.gss_server_identity) { +- gss_host = xstrdup(options.gss_server_identity); +- } else if (options.gss_trust_dns) { +- gss_host = remote_hostname(ssh); +- /* Fall back to specified host if we are using proxy command +- * and can not use DNS on that socket */ +- if (strcmp(gss_host, "UNKNOWN") == 0) { +- gss_host = xstrdup(host); +- } +- } else { +- gss_host = xstrdup(host); +- } +- +- gss = ssh_gssapi_client_mechanisms(gss_host, +- options.gss_client_identity, options.gss_kex_algorithms); +- if (gss) { +- debug("Offering GSSAPI proposal: %s", gss); +- xasprintf(&myproposal[PROPOSAL_KEX_ALGS], +- "%s,%s", gss, orig); +- +- /* If we've got GSSAPI algorithms, then we also support the +- * 'null' hostkey, as a last resort */ +- orig = myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]; +- xasprintf(&myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS], +- "%s,null", orig); ++ if (FIPS_mode()) { ++ logit("Disabling GSSAPIKeyExchange. Not usable in FIPS mode"); ++ options.gss_keyex = 0; ++ } else { ++ /* Add the GSSAPI mechanisms currently supported on this ++ * client to the key exchange algorithm proposal */ ++ orig = myproposal[PROPOSAL_KEX_ALGS]; ++ ++ if (options.gss_server_identity) { ++ gss_host = xstrdup(options.gss_server_identity); ++ } else if (options.gss_trust_dns) { ++ gss_host = remote_hostname(ssh); ++ /* Fall back to specified host if we are using proxy command ++ * and can not use DNS on that socket */ ++ if (strcmp(gss_host, "UNKNOWN") == 0) { ++ gss_host = xstrdup(host); ++ } ++ } else { ++ gss_host = xstrdup(host); ++ } ++ ++ gss = ssh_gssapi_client_mechanisms(gss_host, ++ options.gss_client_identity, options.gss_kex_algorithms); ++ if (gss) { ++ debug("Offering GSSAPI proposal: %s", gss); ++ xasprintf(&myproposal[PROPOSAL_KEX_ALGS], ++ "%s,%s", gss, orig); ++ ++ /* If we've got GSSAPI algorithms, then we also support the ++ * 'null' hostkey, as a last resort */ ++ orig = myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]; ++ xasprintf(&myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS], ++ "%s,null", orig); ++ } + } + } + #endif +diff -up openssh-7.9p1/sshd.c.fips openssh-7.9p1/sshd.c +--- openssh-7.9p1/sshd.c.fips 2019-03-11 17:06:37.617878003 +0100 ++++ openssh-7.9p1/sshd.c 2019-03-11 17:06:37.624878069 +0100 @@ -66,6 +66,7 @@ #include #include @@ -592,55 +406,34 @@ diff -up openssh-7.7p1/sshd.c.fips openssh-7.7p1/sshd.c #include #include #include -@@ -77,6 +78,8 @@ +@@ -77,6 +78,7 @@ #include #include #include -+#include -+#include ++#include #include "openbsd-compat/openssl-compat.h" #endif -@@ -1534,6 +1537,18 @@ main(int ac, char **av) +@@ -1581,6 +1584,7 @@ main(int ac, char **av) #endif __progname = ssh_get_progname(av[0]); -+ SSLeay_add_all_algorithms(); -+ if (access("/etc/system-fips", F_OK) == 0) -+ if (! FIPSCHECK_verify(NULL, NULL)) { -+ openlog(__progname, LOG_PID, LOG_AUTHPRIV); -+ if (FIPS_mode()) { -+ syslog(LOG_CRIT, "FIPS integrity verification test failed."); -+ cleanup_exit(255); -+ } -+ else -+ syslog(LOG_INFO, "FIPS integrity verification test failed."); -+ closelog(); -+ } ++ OpenSSL_add_all_algorithms(); /* Save argv. Duplicate so setproctitle emulation doesn't clobber it */ saved_argc = ac; rexec_argc = ac; -@@ -1675,7 +1690,7 @@ main(int ac, char **av) - else - closefrom(REEXEC_DEVCRYPTO_RESERVED_FD); - --#ifdef WITH_OPENSSL -+#if 0 /* FIPS */ - OpenSSL_add_all_algorithms(); - #endif - -@@ -1979,6 +1994,10 @@ main(int ac, char **av) +@@ -2036,6 +2051,10 @@ main(int ac, char **av) /* Reinitialize the log (because of the fork above). */ log_init(__progname, options.log_level, options.log_facility, log_stderr); + if (FIPS_mode()) { -+ logit("FIPS mode initialized"); ++ debug("FIPS mode initialized"); + } + /* Chdir to the root directory so that the current disk can be unmounted if desired. */ if (chdir("/") == -1) -@@ -2359,10 +2378,14 @@ do_ssh2_kex(void) +@@ -2412,10 +2431,14 @@ do_ssh2_kex(void) if (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]) == 0) orig = NULL; @@ -659,14 +452,14 @@ diff -up openssh-7.7p1/sshd.c.fips openssh-7.7p1/sshd.c if (gss && orig) xasprintf(&newstr, "%s,%s", gss, orig); -diff -up openssh-7.7p1/sshkey.c.fips openssh-7.7p1/sshkey.c ---- openssh-7.7p1/sshkey.c.fips 2018-08-08 10:08:40.818719940 +0200 -+++ openssh-7.7p1/sshkey.c 2018-08-08 10:08:40.826720007 +0200 +diff -up openssh-7.9p1/sshkey.c.fips openssh-7.9p1/sshkey.c +--- openssh-7.9p1/sshkey.c.fips 2019-03-11 17:06:37.617878003 +0100 ++++ openssh-7.9p1/sshkey.c 2019-03-11 17:06:37.624878069 +0100 @@ -34,6 +34,7 @@ #include #include #include -+#include ++#include #endif #include "crypto_api.h" @@ -678,19 +471,60 @@ diff -up openssh-7.7p1/sshkey.c.fips openssh-7.7p1/sshkey.c #include "xmss_fast.h" -@@ -1526,6 +1528,8 @@ rsa_generate_private_key(u_int bits, RSA +@@ -392,7 +394,8 @@ sshkey_calculate_signature(EVP_PKEY *pkey + { + EVP_MD_CTX *ctx = NULL; + u_char *sig = NULL; +- int ret, slen, len; ++ int ret, slen; ++ size_t len; + + if (sigp == NULL || lenp == NULL) { + return SSH_ERR_INVALID_ARGUMENT; +@@ -411,9 +414,10 @@ sshkey_calculate_signature(EVP_PKEY *pkey + ret = SSH_ERR_ALLOC_FAIL; + goto error; + } +- if (EVP_SignInit_ex(ctx, ssh_digest_to_md(hash_alg), NULL) <= 0 || +- EVP_SignUpdate(ctx, data, datalen) <= 0 || +- EVP_SignFinal(ctx, sig, &len, pkey) <= 0) { ++ if (EVP_DigestSignInit(ctx, NULL, ssh_digest_to_md(hash_alg), ++ NULL, pkey) != 1 || ++ EVP_DigestSignUpdate(ctx, data, datalen) != 1 || ++ EVP_DigestSignFinal(ctx, sig, &len) != 1) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto error; + } +@@ -440,12 +444,13 @@ sshkey_verify_signature(EVP_PKEY *pkey + if ((ctx = EVP_MD_CTX_new()) == NULL) { + return SSH_ERR_ALLOC_FAIL; + } +- if (EVP_VerifyInit_ex(ctx, ssh_digest_to_md(hash_alg), NULL) <= 0 || +- EVP_VerifyUpdate(ctx, data, datalen) <= 0) { ++ if (EVP_DigestVerifyInit(ctx, NULL, ssh_digest_to_md(hash_alg), ++ NULL, pkey) != 1 || ++ EVP_DigestVerifyUpdate(ctx, data, datalen) != 1) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto done; + } +- ret = EVP_VerifyFinal(ctx, sigbuf, siglen, pkey); ++ ret = EVP_DigestVerifyFinal(ctx, sigbuf, siglen); + switch (ret) { + case 1: + ret = 0; +@@ -1514,6 +1516,8 @@ rsa_generate_private_key(u_int bits, RSA } if (!BN_set_word(f4, RSA_F4) || !RSA_generate_key_ex(private, bits, f4, NULL)) { -+ if (FIPS_mode()) -+ logit("%s: the key length might be unsupported by FIPS mode approved key generation method", __func__); ++ if (FIPS_mode()) ++ logit("%s: the key length might be unsupported by FIPS mode approved key generation method", __func__); ret = SSH_ERR_LIBCRYPTO_ERROR; goto out; } -diff -up openssh-7.7p1/ssh-keygen.c.fips openssh-7.7p1/ssh-keygen.c ---- openssh-7.7p1/ssh-keygen.c.fips 2018-08-08 10:08:40.801719797 +0200 -+++ openssh-7.7p1/ssh-keygen.c 2018-08-08 10:08:40.827720016 +0200 -@@ -229,6 +229,12 @@ type_bits_valid(int type, const char *na +diff -up openssh-7.9p1/ssh-keygen.c.fips openssh-7.9p1/ssh-keygen.c +--- openssh-7.9p1/ssh-keygen.c.fips 2019-03-11 17:06:37.590877750 +0100 ++++ openssh-7.9p1/ssh-keygen.c 2019-03-11 17:06:37.625878079 +0100 +@@ -230,6 +230,12 @@ type_bits_valid(int type, const char *na OPENSSL_DSA_MAX_MODULUS_BITS : OPENSSL_RSA_MAX_MODULUS_BITS; if (*bitsp > maxbits) fatal("key bits exceeds maximum %d", maxbits); @@ -703,3 +537,33 @@ diff -up openssh-7.7p1/ssh-keygen.c.fips openssh-7.7p1/ssh-keygen.c switch (type) { case KEY_DSA: if (*bitsp != 1024) +@@ -1029,9 +1035,17 @@ do_gen_all_hostkeys(struct passwd *pw) + first = 1; + printf("%s: generating new host keys: ", __progname); + } ++ type = sshkey_type_from_name(key_types[i].key_type); ++ ++ /* Skip the keys that are not supported in FIPS mode */ ++ if (FIPS_mode() && (type == KEY_DSA || type == KEY_ED25519)) { ++ logit("Skipping %s key in FIPS mode", ++ key_types[i].key_type_display); ++ goto next; ++ } ++ + 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 public key in %s: %s", + prv_tmp, strerror(errno)); +diff -up openssh-8.0p1/sshd_config.xxx openssh-8.0p1/sshd_config +--- openssh-8.0p1/sshd_config.xxx 2023-10-30 13:01:59.150952364 +0100 ++++ openssh-8.0p1/sshd_config 2023-10-30 13:02:56.662231354 +0100 +@@ -21,6 +21,7 @@ + + HostKey /etc/ssh/ssh_host_rsa_key + HostKey /etc/ssh/ssh_host_ecdsa_key ++#In FIPS mode Ed25519 keys are not supported, please comment out the next line + HostKey /etc/ssh/ssh_host_ed25519_key + + # Ciphers and keying diff --git a/openssh-7.7p1-gssapi-new-unique.patch b/openssh-7.7p1-gssapi-new-unique.patch index 7acc58a..2eea250 100644 --- a/openssh-7.7p1-gssapi-new-unique.patch +++ b/openssh-7.7p1-gssapi-new-unique.patch @@ -84,7 +84,7 @@ index a5a81ed2..63f877f2 100644 + /* There is at least one other ccache in collection + * we can switch to */ + krb5_cc_switch(ctx, ccache); -+ } else { ++ } else if (authctxt->krb5_ccname != NULL) { + /* Clean up the collection too */ + strncpy(krb5_ccname, authctxt->krb5_ccname, sizeof(krb5_ccname) - 10); + krb5_ccname_dir_start = strchr(krb5_ccname, ':') + 1; @@ -113,29 +113,12 @@ index a5a81ed2..63f877f2 100644 if (authctxt->krb5_user) { krb5_free_principal(authctxt->krb5_ctx, authctxt->krb5_user); authctxt->krb5_user = NULL; -@@ -237,36 +287,186 @@ krb5_cleanup_proc(Authctxt *authctxt) +@@ -237,36 +281,188 @@ krb5_cleanup_proc(Authctxt *authctxt) } } -#ifndef HEIMDAL --krb5_error_code --ssh_krb5_cc_gen(krb5_context ctx, krb5_ccache *ccache) { -- int tmpfd, ret, oerrno; -- char ccname[40]; -- mode_t old_umask; - -- ret = snprintf(ccname, sizeof(ccname), -- "FILE:/tmp/krb5cc_%d_XXXXXXXXXX", geteuid()); -- if (ret < 0 || (size_t)ret >= sizeof(ccname)) -- return ENOMEM; -- -- old_umask = umask(0177); -- tmpfd = mkstemp(ccname + strlen("FILE:")); -- oerrno = errno; -- umask(old_umask); -- if (tmpfd == -1) { -- logit("mkstemp(): %.100s", strerror(oerrno)); -- return oerrno; ++ +#if !defined(HEIMDAL) +int +ssh_asprintf_append(char **dsc, const char *fmt, ...) { @@ -195,14 +178,13 @@ index a5a81ed2..63f877f2 100644 + continue; + } else { + p_o = strchr(p_n, '}') + 1; -+ p_o = '\0'; ++ *p_o = '\0'; + debug("%s: unsupported token %s in %s", __func__, p_n, template); + /* unknown token, fallback to the default */ + goto cleanup; + } - } - -- if (fchmod(tmpfd,S_IRUSR | S_IWUSR) == -1) { ++ } ++ + if (ssh_asprintf_append(&r, "%s", p_o) == -1) + goto cleanup; + @@ -216,7 +198,10 @@ index a5a81ed2..63f877f2 100644 + return -1; +} + -+krb5_error_code + krb5_error_code +-ssh_krb5_cc_gen(krb5_context ctx, krb5_ccache *ccache) { +- int tmpfd, ret, oerrno; +- char ccname[40]; +ssh_krb5_get_cctemplate(krb5_context ctx, char **ccname) { + profile_t p; + int ret = 0; @@ -241,14 +226,27 @@ index a5a81ed2..63f877f2 100644 +ssh_krb5_cc_new_unique(krb5_context ctx, krb5_ccache *ccache, int *need_environment) { + int tmpfd, ret, oerrno, type_len; + char *ccname = NULL; -+ mode_t old_umask; + mode_t old_umask; + char *type = NULL, *colon = NULL; -+ + +- ret = snprintf(ccname, sizeof(ccname), +- "FILE:/tmp/krb5cc_%d_XXXXXXXXXX", geteuid()); +- if (ret < 0 || (size_t)ret >= sizeof(ccname)) +- return ENOMEM; +- +- old_umask = umask(0177); +- tmpfd = mkstemp(ccname + strlen("FILE:")); +- oerrno = errno; +- umask(old_umask); +- if (tmpfd == -1) { +- logit("mkstemp(): %.100s", strerror(oerrno)); +- return oerrno; +- } + debug3("%s: called", __func__); + if (need_environment) + *need_environment = 0; + ret = ssh_krb5_get_cctemplate(ctx, &ccname); -+ if (ret || !ccname || options.kerberos_unique_ticket) { ++ if (ret || !ccname || options.kerberos_unique_ccache) { + /* Otherwise, go with the old method */ + if (ccname) + free(ccname); @@ -258,7 +256,8 @@ index a5a81ed2..63f877f2 100644 + "FILE:/tmp/krb5cc_%d_XXXXXXXXXX", geteuid()); + if (ret < 0) + return ENOMEM; -+ + +- if (fchmod(tmpfd,S_IRUSR | S_IWUSR) == -1) { + old_umask = umask(0177); + tmpfd = mkstemp(ccname + strlen("FILE:")); oerrno = errno; @@ -307,6 +306,7 @@ index a5a81ed2..63f877f2 100644 + if (krb5_cc_support_switch(ctx, type)) { + debug3("%s: calling cc_new_unique(%s)", __func__, ccname); + ret = krb5_cc_new_unique(ctx, type, NULL, ccache); ++ free(type); + if (ret) + return ret; + @@ -317,6 +317,7 @@ index a5a81ed2..63f877f2 100644 + * it is already unique from above or the type does not support + * collections + */ ++ free(type); + debug3("%s: calling cc_resolve(%s)", __func__, ccname); + return (krb5_cc_resolve(ctx, ccname, ccache)); + } @@ -343,11 +344,10 @@ index 29491df9..fdab5040 100644 +krb5_error_code ssh_krb5_cc_new_unique(krb5_context, krb5_ccache *, int *); #endif #endif -diff --git a/gss-serv-krb5.c b/gss-serv-krb5.c -index 795992d9..0623a107 100644 ---- a/gss-serv-krb5.c -+++ b/gss-serv-krb5.c -@@ -114,7 +114,7 @@ ssh_gssapi_krb5_userok(ssh_gssapi_client *client, char *name) +diff -up openssh-7.9p1/gss-serv-krb5.c.ccache_name openssh-7.9p1/gss-serv-krb5.c +--- openssh-7.9p1/gss-serv-krb5.c.ccache_name 2019-03-01 15:17:42.708611802 +0100 ++++ openssh-7.9p1/gss-serv-krb5.c 2019-03-01 15:17:42.713611844 +0100 +@@ -267,7 +267,7 @@ ssh_gssapi_krb5_cmdok(krb5_principal pri /* This writes out any forwarded credentials from the structure populated * during userauth. Called after we have setuid to the user */ @@ -356,12 +356,9 @@ index 795992d9..0623a107 100644 ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client) { krb5_ccache ccache; -@@ -121,16 +121,17 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client) - krb5_error_code problem; - krb5_principal princ; +@@ -276,14 +276,15 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_cl OM_uint32 maj_status, min_status; -- const char *new_ccname, *new_cctype; -+ int len; + const char *new_ccname, *new_cctype; const char *errmsg; + int set_env = 0; @@ -377,7 +374,7 @@ index 795992d9..0623a107 100644 #ifdef HEIMDAL # ifdef HAVE_KRB5_CC_NEW_UNIQUE -@@ -144,14 +145,14 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client) +@@ -297,14 +298,14 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_cl krb5_get_err_text(krb_context, problem)); # endif krb5_free_error_message(krb_context, errmsg); @@ -396,7 +393,7 @@ index 795992d9..0623a107 100644 } #endif /* #ifdef HEIMDAL */ -@@ -160,7 +161,7 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client) +@@ -313,7 +314,7 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_cl errmsg = krb5_get_error_message(krb_context, problem); logit("krb5_parse_name(): %.100s", errmsg); krb5_free_error_message(krb_context, errmsg); @@ -405,7 +402,7 @@ index 795992d9..0623a107 100644 } if ((problem = krb5_cc_initialize(krb_context, ccache, princ))) { -@@ -169,7 +170,7 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client) +@@ -322,7 +323,7 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_cl krb5_free_error_message(krb_context, errmsg); krb5_free_principal(krb_context, princ); krb5_cc_destroy(krb_context, ccache); @@ -414,7 +411,7 @@ index 795992d9..0623a107 100644 } krb5_free_principal(krb_context, princ); -@@ -178,37 +179,27 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client) +@@ -331,32 +332,21 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_cl client->creds, ccache))) { logit("gss_krb5_copy_ccache() failed"); krb5_cc_destroy(krb_context, ccache); @@ -422,30 +419,29 @@ index 795992d9..0623a107 100644 + return 0; } -- new_cctype = krb5_cc_get_type(krb_context, ccache); -- new_ccname = krb5_cc_get_name(krb_context, ccache); + new_cctype = krb5_cc_get_type(krb_context, ccache); + new_ccname = krb5_cc_get_name(krb_context, ccache); - - client->store.envvar = "KRB5CCNAME"; -#ifdef USE_CCAPI - xasprintf(&client->store.envval, "API:%s", new_ccname); +- client->store.filename = NULL; -#else - if (new_ccname[0] == ':') - new_ccname++; -- xasprintf(&client->store.envval, "%s:%s", new_cctype, new_ccname); + xasprintf(&client->store.envval, "%s:%s", new_cctype, new_ccname); - if (strcmp(new_cctype, "DIR") == 0) { - char *p; - p = strrchr(client->store.envval, '/'); - if (p) - *p = '\0'; -- } --#endif ++ + if (set_env) { -+ const char *filename = krb5_cc_get_name(krb_context, ccache); + client->store.envvar = "KRB5CCNAME"; -+ len = strlen(filename) + 6; -+ client->store.envval = xmalloc(len); -+ snprintf(client->store.envval, len, "FILE:%s", filename); -+ } + } + if ((strcmp(new_cctype, "FILE") == 0) || (strcmp(new_cctype, "DIR") == 0)) + client->store.filename = xstrdup(new_ccname); +-#endif #ifdef USE_PAM - if (options.use_pam) @@ -453,7 +449,7 @@ index 795992d9..0623a107 100644 do_pam_putenv(client->store.envvar, client->store.envval); #endif - krb5_cc_close(krb_context, ccache); +@@ -361,7 +355,7 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_cl client->store.data = krb_context; @@ -484,15 +480,25 @@ index 6cae720e..16e55cbc 100644 } /* This allows GSSAPI methods to do things to the childs environment based -diff --git a/servconf.c b/servconf.c -index cb578658..a6e01df2 100644 ---- a/servconf.c -+++ b/servconf.c -@@ -122,6 +122,7 @@ initialize_server_options(ServerOptions *options) +@@ -498,9 +500,7 @@ ssh_gssapi_rekey_creds() { + char *envstr; + #endif + +- if (gssapi_client.store.filename == NULL && +- gssapi_client.store.envval == NULL && +- gssapi_client.store.envvar == NULL) ++ if (gssapi_client.store.envval == NULL) + return; + + ok = PRIVSEP(ssh_gssapi_update_creds(&gssapi_client.store)); +diff -up openssh-7.9p1/servconf.c.ccache_name openssh-7.9p1/servconf.c +--- openssh-7.9p1/servconf.c.ccache_name 2019-03-01 15:17:42.704611768 +0100 ++++ openssh-7.9p1/servconf.c 2019-03-01 15:17:42.713611844 +0100 +@@ -123,6 +123,7 @@ initialize_server_options(ServerOptions options->kerberos_or_local_passwd = -1; options->kerberos_ticket_cleanup = -1; options->kerberos_get_afs_token = -1; -+ options->kerberos_unique_ticket = -1; ++ options->kerberos_unique_ccache = -1; options->gss_authentication=-1; options->gss_keyex = -1; options->gss_cleanup_creds = -1; @@ -500,8 +506,8 @@ index cb578658..a6e01df2 100644 options->kerberos_ticket_cleanup = 1; if (options->kerberos_get_afs_token == -1) options->kerberos_get_afs_token = 0; -+ if (options->kerberos_unique_ticket == -1) -+ options->kerberos_unique_ticket = 0; ++ if (options->kerberos_unique_ccache == -1) ++ options->kerberos_unique_ccache = 0; if (options->gss_authentication == -1) options->gss_authentication = 0; if (options->gss_keyex == -1) @@ -510,7 +516,7 @@ index cb578658..a6e01df2 100644 sRhostsRSAAuthentication, sRSAAuthentication, sKerberosAuthentication, sKerberosOrLocalPasswd, sKerberosTicketCleanup, - sKerberosGetAFSToken, sChallengeResponseAuthentication, -+ sKerberosGetAFSToken, sKerberosUniqueTicket, ++ sKerberosGetAFSToken, sKerberosUniqueCCache, + sChallengeResponseAuthentication, sPasswordAuthentication, sKbdInteractiveAuthentication, sListenAddress, sAddressFamily, @@ -519,13 +525,13 @@ index cb578658..a6e01df2 100644 #else { "kerberosgetafstoken", sUnsupported, SSHCFG_GLOBAL }, #endif -+ { "kerberosuniqueticket", sKerberosUniqueTicket, SSHCFG_GLOBAL }, ++ { "kerberosuniqueccache", sKerberosUniqueCCache, SSHCFG_GLOBAL }, #else { "kerberosauthentication", sUnsupported, SSHCFG_ALL }, { "kerberosorlocalpasswd", sUnsupported, SSHCFG_GLOBAL }, { "kerberosticketcleanup", sUnsupported, SSHCFG_GLOBAL }, { "kerberosgetafstoken", sUnsupported, SSHCFG_GLOBAL }, -+ { "kerberosuniqueticket", sUnsupported, SSHCFG_GLOBAL }, ++ { "kerberosuniqueccache", sUnsupported, SSHCFG_GLOBAL }, #endif { "kerberostgtpassing", sUnsupported, SSHCFG_GLOBAL }, { "afstokenpassing", sUnsupported, SSHCFG_GLOBAL }, @@ -533,8 +539,8 @@ index cb578658..a6e01df2 100644 intptr = &options->kerberos_get_afs_token; goto parse_flag; -+ case sKerberosUniqueTicket: -+ intptr = &options->kerberos_unique_ticket; ++ case sKerberosUniqueCCache: ++ intptr = &options->kerberos_unique_ccache; + goto parse_flag; + case sGssAuthentication: @@ -544,7 +550,7 @@ index cb578658..a6e01df2 100644 # ifdef USE_AFS dump_cfg_fmtint(sKerberosGetAFSToken, o->kerberos_get_afs_token); # endif -+ dump_cfg_fmtint(sKerberosUniqueTicket, o->kerberos_unique_ticket); ++ dump_cfg_fmtint(sKerberosUniqueCCache, o->kerberos_unique_ccache); #endif #ifdef GSSAPI dump_cfg_fmtint(sGssAuthentication, o->gss_authentication); @@ -556,7 +562,7 @@ index db8362c6..4fa42d64 100644 * file on logout. */ int kerberos_get_afs_token; /* If true, try to get AFS token if * authenticated with Kerberos. */ -+ int kerberos_unique_ticket; /* If true, the aquired ticket will ++ int kerberos_unique_ccache; /* If true, the acquired ticket will + * be stored in per-session ccache */ int gss_authentication; /* If true, permit GSSAPI authentication */ int gss_keyex; /* If true, permit GSSAPI key exchange */ @@ -588,14 +594,6 @@ diff --git a/ssh-gss.h b/ssh-gss.h index 6593e422..245178af 100644 --- a/ssh-gss.h +++ b/ssh-gss.h -@@ -62,7 +62,6 @@ - #define KEX_GSS_GEX_SHA1_ID "gss-gex-sha1-" - - typedef struct { -- char *filename; - char *envvar; - char *envval; - struct passwd *owner; @@ -83,7 +82,7 @@ typedef struct ssh_gssapi_mech_struct { int (*dochild) (ssh_gssapi_client *); int (*userok) (ssh_gssapi_client *, char *); @@ -631,16 +629,18 @@ diff --git a/sshd_config.5 b/sshd_config.5 index c0683d4a..2349f477 100644 --- a/sshd_config.5 +++ b/sshd_config.5 -@@ -860,6 +860,12 @@ Specifies whether to automatically destroy the user's ticket cache +@@ -860,6 +860,14 @@ Specifies whether to automatically destroy the user's ticket cache file on logout. The default is .Cm yes . -+.It Cm KerberosUniqueTicket -+Specifies whether to store the aquired tickets in the per-session credential -+cache or whether to use per-user credential cache, which might overwrite -+tickets aquired in different sessions of the same user. -+The default is -+.Cm no . ++.It Cm KerberosUniqueCCache ++Specifies whether to store the acquired tickets in the per-session credential ++cache under /tmp/ or whether to use per-user credential cache as configured in ++.Pa /etc/krb5.conf . ++The default value ++.Cm no ++can lead to overwriting previous tickets by subseqent connections to the same ++user account. .It Cm KexAlgorithms Specifies the available KEX (Key Exchange) algorithms. Multiple algorithms must be comma-separated. diff --git a/openssh-7.7p1-redhat.patch b/openssh-7.7p1-redhat.patch index 99a4411..1c1d778 100644 --- a/openssh-7.7p1-redhat.patch +++ b/openssh-7.7p1-redhat.patch @@ -1,26 +1,25 @@ diff -up openssh-7.7p1/ssh_config.redhat openssh-7.7p1/ssh_config --- openssh-7.7p1/ssh_config.redhat 2018-04-02 07:38:28.000000000 +0200 +++ openssh-7.7p1/ssh_config 2018-07-03 10:44:06.522245125 +0200 -@@ -44,3 +44,7 @@ +@@ -44,3 +44,8 @@ # VisualHostKey no # ProxyCommand ssh -q -W %h:%p gateway.example.com # RekeyLimit 1G 1h +# ++# This system is following system-wide crypto policy. +# To modify the system-wide ssh configuration, create a *.conf file under +# /etc/ssh/ssh_config.d/ which will be automatically included below +Include /etc/ssh/ssh_config.d/*.conf diff -up openssh-7.7p1/ssh_config_redhat.redhat openssh-7.7p1/ssh_config_redhat --- openssh-7.7p1/ssh_config_redhat.redhat 2018-07-03 10:44:06.522245125 +0200 +++ openssh-7.7p1/ssh_config_redhat 2018-07-03 10:44:06.522245125 +0200 -@@ -0,0 +1,20 @@ -+# Follow system-wide Crypto Policy, if defined: -+Include /etc/crypto-policies/back-ends/openssh.config +@@ -0,0 +1,21 @@ ++# The options here are in the "Match final block" to be applied as the last ++# options and could be potentially overwritten by the user configuration ++Match final all ++ # Follow system-wide Crypto Policy, if defined: ++ Include /etc/crypto-policies/back-ends/openssh.config + -+# Uncomment this if you want to use .local domain -+# Host *.local -+# CheckHostIP no -+ -+Host * + GSSAPIAuthentication yes + +# If this option is set to yes then remote X11 clients will have full access @@ -33,6 +32,10 @@ diff -up openssh-7.7p1/ssh_config_redhat.redhat openssh-7.7p1/ssh_config_redhat + SendEnv LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT + SendEnv LC_IDENTIFICATION LC_ALL LANGUAGE + SendEnv XMODIFIERS ++ ++# Uncomment this if you want to use .local domain ++# Host *.local ++# CheckHostIP no diff -up openssh-7.7p1/sshd_config.0.redhat openssh-7.7p1/sshd_config.0 --- openssh-7.7p1/sshd_config.0.redhat 2018-04-02 07:39:27.000000000 +0200 +++ openssh-7.7p1/sshd_config.0 2018-07-03 10:44:06.523245133 +0200 @@ -64,7 +67,7 @@ diff -up openssh-7.7p1/sshd_config.5.redhat openssh-7.7p1/sshd_config.5 diff -up openssh-7.7p1/sshd_config.redhat openssh-7.7p1/sshd_config --- openssh-7.7p1/sshd_config.redhat 2018-04-02 07:38:28.000000000 +0200 +++ openssh-7.7p1/sshd_config 2018-07-03 10:45:16.950782466 +0200 -@@ -10,20 +10,34 @@ +@@ -10,20 +10,31 @@ # possible, but leave them commented. Uncommented options override the # default value. @@ -87,14 +90,11 @@ diff -up openssh-7.7p1/sshd_config.redhat openssh-7.7p1/sshd_config # Ciphers and keying #RekeyLimit default none -+# System-wide Crypto policy: +# This system is following system-wide crypto policy. The changes to -+# Ciphers, MACs, KexAlgoritms and GSSAPIKexAlgorithsm will not have any -+# effect here. They will be overridden by command-line options passed on -+# the server start up. -+# To opt out, uncomment a line with redefinition of CRYPTO_POLICY= -+# variable in /etc/sysconfig/sshd to overwrite the policy. -+# For more information, see manual page for update-crypto-policies(8). ++# crypto properties (Ciphers, MACs, ...) will not have any effect here. ++# They will be overridden by command-line options passed to the server ++# on command line. ++# Please, check manual pages for update-crypto-policies(8) and sshd_config(5). + # Logging #SyslogFacility AUTH @@ -142,7 +142,7 @@ diff -up openssh-7.7p1/sshd_config.redhat openssh-7.7p1/sshd_config #PermitTTY yes -#PrintMotd yes + -+# It is recommended to use pam_motd in /etc/pam.d/ssh instead of PrintMotd, ++# It is recommended to use pam_motd in /etc/pam.d/sshd instead of PrintMotd, +# as it is more configurable and versatile than the built-in version. +PrintMotd no + diff --git a/openssh-7.8p1-UsePAM-warning.patch b/openssh-7.8p1-UsePAM-warning.patch index da39361..d4ddcf1 100644 --- a/openssh-7.8p1-UsePAM-warning.patch +++ b/openssh-7.8p1-UsePAM-warning.patch @@ -5,13 +5,13 @@ diff --git a/sshd.c b/sshd.c parse_server_config(&options, rexeced_flag ? "rexec" : config_file_name, cfg, NULL); -+ /* 'UsePAM no' is not supported in Fedora */ ++ /* 'UsePAM no' is not supported in RHEL */ + if (! options.use_pam) -+ logit("WARNING: 'UsePAM no' is not supported in Fedora and may cause several problems."); ++ logit("WARNING: 'UsePAM no' is not supported in RHEL and may cause several problems."); + - seed_rng(); - /* Fill in default values for those options not explicitly set. */ + fill_default_server_options(&options); + diff --git a/sshd_config b/sshd_config --- a/sshd_config +++ b/sshd_config @@ -19,7 +19,7 @@ diff --git a/sshd_config b/sshd_config # If you just want the PAM account and session checks to run without # PAM authentication, then enable this but set PasswordAuthentication # and ChallengeResponseAuthentication to 'no'. -+# WARNING: 'UsePAM no' is not supported in Fedora and may cause several ++# WARNING: 'UsePAM no' is not supported in RHEL and may cause several +# problems. UsePAM yes diff --git a/openssh-7.8p1-gsskex.patch b/openssh-7.8p1-gsskex.patch deleted file mode 100644 index 6a350c7..0000000 --- a/openssh-7.8p1-gsskex.patch +++ /dev/null @@ -1,2905 +0,0 @@ -diff -up openssh/auth2.c.gsskex openssh/auth2.c ---- openssh/auth2.c.gsskex 2018-08-22 11:47:33.260216045 +0200 -+++ openssh/auth2.c 2018-08-22 11:47:33.307216424 +0200 -@@ -74,6 +74,7 @@ extern Authmethod method_passwd; - extern Authmethod method_kbdint; - extern Authmethod method_hostbased; - #ifdef GSSAPI -+extern Authmethod method_gsskeyex; - extern Authmethod method_gssapi; - #endif - -@@ -81,6 +82,7 @@ Authmethod *authmethods[] = { - &method_none, - &method_pubkey, - #ifdef GSSAPI -+ &method_gsskeyex, - &method_gssapi, - #endif - &method_passwd, -diff -up openssh/auth2-gss.c.gsskex openssh/auth2-gss.c ---- openssh/auth2-gss.c.gsskex 2018-08-22 11:47:33.260216045 +0200 -+++ openssh/auth2-gss.c 2018-08-22 13:00:48.722680124 +0200 -@@ -31,6 +31,7 @@ - #include - - #include -+#include - - #include "xmalloc.h" - #include "sshkey.h" -@@ -54,6 +55,44 @@ static int input_gssapi_mic(int type, u_ - static int input_gssapi_exchange_complete(int type, u_int32_t plen, struct ssh *ssh); - static int input_gssapi_errtok(int, u_int32_t, struct ssh *); - -+/* -+ * The 'gssapi_keyex' userauth mechanism. -+ */ -+static int -+userauth_gsskeyex(struct ssh *ssh) -+{ -+ Authctxt *authctxt = ssh->authctxt; -+ int authenticated = 0; -+ struct sshbuf *b = NULL; -+ gss_buffer_desc mic, gssbuf; -+ u_int len; -+ -+ mic.value = packet_get_string(&len); -+ mic.length = len; -+ -+ packet_check_eom(); -+ -+ if ((b = sshbuf_new()) == NULL) -+ fatal("%s: sshbuf_new failed", __func__); -+ -+ ssh_gssapi_buildmic(b, authctxt->user, authctxt->service, -+ "gssapi-keyex"); -+ -+ gssbuf.value = sshbuf_mutable_ptr(b); -+ gssbuf.length = sshbuf_len(b); -+ -+ /* gss_kex_context is NULL with privsep, so we can't check it here */ -+ if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gss_kex_context, -+ &gssbuf, &mic)))) -+ authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user, -+ authctxt->pw)); -+ -+ sshbuf_free(b); -+ free(mic.value); -+ -+ return (authenticated); -+} -+ - /* - * We only support those mechanisms that we know about (ie ones that we know - * how to check local user kuserok and the like) -@@ -260,7 +296,8 @@ input_gssapi_exchange_complete(int type, - if ((r = sshpkt_get_end(ssh)) != 0) - fatal("%s: %s", __func__, ssh_err(r)); - -- authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user)); -+ authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user, -+ authctxt->pw)); - - if ((!use_privsep || mm_is_monitor()) && - (displayname = ssh_gssapi_displayname()) != NULL) -@@ -313,7 +350,8 @@ input_gssapi_mic(int type, u_int32_t ple - gssbuf.length = sshbuf_len(b); - - if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gssctxt, &gssbuf, &mic)))) -- authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user)); -+ authenticated = -+ PRIVSEP(ssh_gssapi_userok(authctxt->user, authctxt->pw)); - else - logit("GSSAPI MIC check failed"); - -@@ -335,6 +373,12 @@ input_gssapi_mic(int type, u_int32_t ple - return 0; - } - -+Authmethod method_gsskeyex = { -+ "gssapi-keyex", -+ userauth_gsskeyex, -+ &options.gss_authentication -+}; -+ - Authmethod method_gssapi = { - "gssapi-with-mic", - userauth_gssapi, -diff -up openssh/auth.c.gsskex openssh/auth.c ---- openssh/auth.c.gsskex 2018-08-22 11:47:33.274216158 +0200 -+++ openssh/auth.c 2018-08-22 11:47:33.308216432 +0200 -@@ -395,6 +395,7 @@ auth_root_allowed(struct ssh *ssh, const - case PERMIT_NO_PASSWD: - if (strcmp(method, "publickey") == 0 || - strcmp(method, "hostbased") == 0 || -+ strcmp(method, "gssapi-keyex") == 0 || - strcmp(method, "gssapi-with-mic") == 0) - return 1; - break; -diff -up openssh/clientloop.c.gsskex openssh/clientloop.c ---- openssh/clientloop.c.gsskex 2018-08-20 07:57:29.000000000 +0200 -+++ openssh/clientloop.c 2018-08-22 11:47:33.309216441 +0200 -@@ -112,6 +112,10 @@ - #include "ssherr.h" - #include "hostfile.h" - -+#ifdef GSSAPI -+#include "ssh-gss.h" -+#endif -+ - /* import options */ - extern Options options; - -@@ -1357,9 +1361,18 @@ client_loop(struct ssh *ssh, int have_pt - break; - - /* Do channel operations unless rekeying in progress. */ -- if (!ssh_packet_is_rekeying(ssh)) -+ if (!ssh_packet_is_rekeying(ssh)) { - channel_after_select(ssh, readset, writeset); - -+#ifdef GSSAPI -+ if (options.gss_renewal_rekey && -+ ssh_gssapi_credentials_updated(NULL)) { -+ debug("credentials updated - forcing rekey"); -+ need_rekeying = 1; -+ } -+#endif -+ } -+ - /* Buffer input from the connection. */ - client_process_net_input(readset); - -diff -up openssh/configure.ac.gsskex openssh/configure.ac ---- openssh/configure.ac.gsskex 2018-08-22 11:47:33.296216335 +0200 -+++ openssh/configure.ac 2018-08-22 11:47:33.309216441 +0200 -@@ -673,6 +673,30 @@ main() { if (NSVersionOfRunTimeLibrary(" - [Use tunnel device compatibility to OpenBSD]) - AC_DEFINE([SSH_TUN_PREPEND_AF], [1], - [Prepend the address family to IP tunnel traffic]) -+ AC_MSG_CHECKING(if we have the Security Authorization Session API) -+ AC_TRY_COMPILE([#include ], -+ [SessionCreate(0, 0);], -+ [ac_cv_use_security_session_api="yes" -+ AC_DEFINE(USE_SECURITY_SESSION_API, 1, -+ [platform has the Security Authorization Session API]) -+ LIBS="$LIBS -framework Security" -+ AC_MSG_RESULT(yes)], -+ [ac_cv_use_security_session_api="no" -+ AC_MSG_RESULT(no)]) -+ AC_MSG_CHECKING(if we have an in-memory credentials cache) -+ AC_TRY_COMPILE( -+ [#include ], -+ [cc_context_t c; -+ (void) cc_initialize (&c, 0, NULL, NULL);], -+ [AC_DEFINE(USE_CCAPI, 1, -+ [platform uses an in-memory credentials cache]) -+ LIBS="$LIBS -framework Security" -+ AC_MSG_RESULT(yes) -+ if test "x$ac_cv_use_security_session_api" = "xno"; then -+ AC_MSG_ERROR(*** Need a security framework to use the credentials cache API ***) -+ fi], -+ [AC_MSG_RESULT(no)] -+ ) - m4_pattern_allow([AU_IPv]) - AC_CHECK_DECL([AU_IPv4], [], - AC_DEFINE([AU_IPv4], [0], [System only supports IPv4 audit records]) -diff -up openssh/gss-genr.c.gsskex openssh/gss-genr.c ---- openssh/gss-genr.c.gsskex 2018-08-20 07:57:29.000000000 +0200 -+++ openssh/gss-genr.c 2018-08-22 13:18:47.444383602 +0200 -@@ -35,18 +35,177 @@ - #include - #include - #include -+#include - - #include "xmalloc.h" - #include "ssherr.h" - #include "sshbuf.h" - #include "log.h" - #include "ssh2.h" -+#include "cipher.h" -+#include "sshkey.h" -+#include "kex.h" - - #include "ssh-gss.h" - - extern u_char *session_id2; - extern u_int session_id2_len; - -+typedef struct { -+ char *encoded; -+ gss_OID oid; -+} ssh_gss_kex_mapping; -+ -+/* -+ * XXX - It would be nice to find a more elegant way of handling the -+ * XXX passing of the key exchange context to the userauth routines -+ */ -+ -+Gssctxt *gss_kex_context = NULL; -+ -+static ssh_gss_kex_mapping *gss_enc2oid = NULL; -+ -+int -+ssh_gssapi_oid_table_ok() { -+ return (gss_enc2oid != NULL); -+} -+ -+/* -+ * Return a list of the gss-group1-sha1 mechanisms supported by this program -+ * -+ * We test mechanisms to ensure that we can use them, to avoid starting -+ * a key exchange with a bad mechanism -+ */ -+ -+char * -+ssh_gssapi_client_mechanisms(const char *host, const char *client) { -+ gss_OID_set gss_supported; -+ OM_uint32 min_status; -+ -+ if (GSS_ERROR(gss_indicate_mechs(&min_status, &gss_supported))) -+ return NULL; -+ -+ return(ssh_gssapi_kex_mechs(gss_supported, ssh_gssapi_check_mechanism, -+ host, client)); -+} -+ -+char * -+ssh_gssapi_kex_mechs(gss_OID_set gss_supported, ssh_gssapi_check_fn *check, -+ const char *host, const char *client) { -+ struct sshbuf *buf; -+ size_t i; -+ int oidpos, enclen, r; -+ char *mechs, *encoded; -+ u_char digest[EVP_MAX_MD_SIZE]; -+ char deroid[2]; -+ const EVP_MD *evp_md = EVP_md5(); -+ EVP_MD_CTX md; -+ -+ if (gss_enc2oid != NULL) { -+ for (i = 0; gss_enc2oid[i].encoded != NULL; i++) -+ free(gss_enc2oid[i].encoded); -+ free(gss_enc2oid); -+ } -+ -+ gss_enc2oid = xmalloc(sizeof(ssh_gss_kex_mapping) * -+ (gss_supported->count + 1)); -+ -+ if ((buf = sshbuf_new()) == NULL) -+ fatal("%s: sshbuf_new failed", __func__); -+ -+ oidpos = 0; -+ for (i = 0; i < gss_supported->count; i++) { -+ if (gss_supported->elements[i].length < 128 && -+ (*check)(NULL, &(gss_supported->elements[i]), host, client)) { -+ -+ deroid[0] = SSH_GSS_OIDTYPE; -+ deroid[1] = gss_supported->elements[i].length; -+ -+ EVP_DigestInit(&md, evp_md); -+ EVP_DigestUpdate(&md, deroid, 2); -+ EVP_DigestUpdate(&md, -+ gss_supported->elements[i].elements, -+ gss_supported->elements[i].length); -+ EVP_DigestFinal(&md, digest, NULL); -+ -+ encoded = xmalloc(EVP_MD_size(evp_md) * 2); -+ enclen = __b64_ntop(digest, EVP_MD_size(evp_md), -+ encoded, EVP_MD_size(evp_md) * 2); -+ -+ if (oidpos != 0) -+ if ((r = sshbuf_put_u8(buf, ',')) != 0) -+ fatal("%s: buffer error: %s", __func__, ssh_err(r)); -+ -+ if ((r = sshbuf_put(buf, KEX_GSS_GEX_SHA1_ID, -+ sizeof(KEX_GSS_GEX_SHA1_ID) - 1)) != 0 || -+ (r = sshbuf_put(buf, encoded, enclen)) != 0 || -+ (r = sshbuf_put_u8(buf, ',')) != 0 || -+ (r = sshbuf_put(buf, KEX_GSS_GRP1_SHA1_ID, -+ sizeof(KEX_GSS_GRP1_SHA1_ID) - 1)) != 0 || -+ (r = sshbuf_put(buf, encoded, enclen)) != 0 || -+ (r = sshbuf_put_u8(buf, ',')) != 0 || -+ (r = sshbuf_put(buf, KEX_GSS_GRP14_SHA1_ID, -+ sizeof(KEX_GSS_GRP14_SHA1_ID) - 1)) != 0 || -+ (r = sshbuf_put(buf, encoded, enclen)) != 0) -+ fatal("%s: buffer error: %s", __func__, ssh_err(r)); -+ -+ gss_enc2oid[oidpos].oid = &(gss_supported->elements[i]); -+ gss_enc2oid[oidpos].encoded = encoded; -+ oidpos++; -+ } -+ } -+ gss_enc2oid[oidpos].oid = NULL; -+ gss_enc2oid[oidpos].encoded = NULL; -+ -+ if ((r = sshbuf_put_u8(buf, '\0')) != 0) -+ fatal("%s: buffer error: %s", __func__, ssh_err(r)); -+ -+ mechs = xmalloc(sshbuf_len(buf)); -+ sshbuf_get(buf, mechs, sshbuf_len(buf)); -+ sshbuf_free(buf); -+ -+ if (strlen(mechs) == 0) { -+ free(mechs); -+ mechs = NULL; -+ } -+ -+ return (mechs); -+} -+ -+gss_OID -+ssh_gssapi_id_kex(Gssctxt *ctx, char *name, int kex_type) { -+ int i = 0; -+ -+ switch (kex_type) { -+ case KEX_GSS_GRP1_SHA1: -+ if (strlen(name) < sizeof(KEX_GSS_GRP1_SHA1_ID)) -+ return GSS_C_NO_OID; -+ name += sizeof(KEX_GSS_GRP1_SHA1_ID) - 1; -+ break; -+ case KEX_GSS_GRP14_SHA1: -+ if (strlen(name) < sizeof(KEX_GSS_GRP14_SHA1_ID)) -+ return GSS_C_NO_OID; -+ name += sizeof(KEX_GSS_GRP14_SHA1_ID) - 1; -+ break; -+ case KEX_GSS_GEX_SHA1: -+ if (strlen(name) < sizeof(KEX_GSS_GEX_SHA1_ID)) -+ return GSS_C_NO_OID; -+ name += sizeof(KEX_GSS_GEX_SHA1_ID) - 1; -+ break; -+ default: -+ return GSS_C_NO_OID; -+ } -+ -+ while (gss_enc2oid[i].encoded != NULL && -+ strcmp(name, gss_enc2oid[i].encoded) != 0) -+ i++; -+ -+ if (gss_enc2oid[i].oid != NULL && ctx != NULL) -+ ssh_gssapi_set_oid(ctx, gss_enc2oid[i].oid); -+ -+ return gss_enc2oid[i].oid; -+} -+ - /* sshbuf_get for gss_buffer_desc */ - int - ssh_gssapi_get_buffer_desc(struct sshbuf *b, gss_buffer_desc *g) -@@ -218,7 +373,7 @@ ssh_gssapi_init_ctx(Gssctxt *ctx, int de - } - - ctx->major = gss_init_sec_context(&ctx->minor, -- GSS_C_NO_CREDENTIAL, &ctx->context, ctx->name, ctx->oid, -+ ctx->client_creds, &ctx->context, ctx->name, ctx->oid, - GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG | deleg_flag, - 0, NULL, recv_tok, NULL, send_tok, flags, NULL); - -@@ -248,8 +403,42 @@ ssh_gssapi_import_name(Gssctxt *ctx, con - } - - OM_uint32 -+ssh_gssapi_client_identity(Gssctxt *ctx, const char *name) -+{ -+ gss_buffer_desc gssbuf; -+ gss_name_t gssname; -+ OM_uint32 status; -+ gss_OID_set oidset; -+ -+ gssbuf.value = (void *) name; -+ gssbuf.length = strlen(gssbuf.value); -+ -+ gss_create_empty_oid_set(&status, &oidset); -+ gss_add_oid_set_member(&status, ctx->oid, &oidset); -+ -+ ctx->major = gss_import_name(&ctx->minor, &gssbuf, -+ GSS_C_NT_USER_NAME, &gssname); -+ -+ if (!ctx->major) -+ ctx->major = gss_acquire_cred(&ctx->minor, -+ gssname, 0, oidset, GSS_C_INITIATE, -+ &ctx->client_creds, NULL, NULL); -+ -+ gss_release_name(&status, &gssname); -+ gss_release_oid_set(&status, &oidset); -+ -+ if (ctx->major) -+ ssh_gssapi_error(ctx); -+ -+ return(ctx->major); -+} -+ -+OM_uint32 - ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash) - { -+ if (ctx == NULL) -+ return -1; -+ - if ((ctx->major = gss_get_mic(&ctx->minor, ctx->context, - GSS_C_QOP_DEFAULT, buffer, hash))) - ssh_gssapi_error(ctx); -@@ -257,6 +446,19 @@ ssh_gssapi_sign(Gssctxt *ctx, gss_buffer - return (ctx->major); - } - -+/* Priviledged when used by server */ -+OM_uint32 -+ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic) -+{ -+ if (ctx == NULL) -+ return -1; -+ -+ ctx->major = gss_verify_mic(&ctx->minor, ctx->context, -+ gssbuf, gssmic, NULL); -+ -+ return (ctx->major); -+} -+ - void - ssh_gssapi_buildmic(struct sshbuf *b, const char *user, const char *service, - const char *context) -@@ -273,11 +475,16 @@ ssh_gssapi_buildmic(struct sshbuf *b, co - } - - int --ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host) -+ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host, -+ const char *client) - { - gss_buffer_desc token = GSS_C_EMPTY_BUFFER; - OM_uint32 major, minor; - gss_OID_desc spnego_oid = {6, (void *)"\x2B\x06\x01\x05\x05\x02"}; -+ Gssctxt *intctx = NULL; -+ -+ if (ctx == NULL) -+ ctx = &intctx; - - /* RFC 4462 says we MUST NOT do SPNEGO */ - if (oid->length == spnego_oid.length && -@@ -287,6 +494,10 @@ ssh_gssapi_check_mechanism(Gssctxt **ctx - ssh_gssapi_build_ctx(ctx); - ssh_gssapi_set_oid(*ctx, oid); - major = ssh_gssapi_import_name(*ctx, host); -+ -+ if (!GSS_ERROR(major) && client) -+ major = ssh_gssapi_client_identity(*ctx, client); -+ - if (!GSS_ERROR(major)) { - major = ssh_gssapi_init_ctx(*ctx, 0, GSS_C_NO_BUFFER, &token, - NULL); -@@ -296,10 +507,66 @@ ssh_gssapi_check_mechanism(Gssctxt **ctx - GSS_C_NO_BUFFER); - } - -- if (GSS_ERROR(major)) -+ if (GSS_ERROR(major) || intctx != NULL) - ssh_gssapi_delete_ctx(ctx); - - return (!GSS_ERROR(major)); - } - -+int -+ssh_gssapi_credentials_updated(Gssctxt *ctxt) { -+ static gss_name_t saved_name = GSS_C_NO_NAME; -+ static OM_uint32 saved_lifetime = 0; -+ static gss_OID saved_mech = GSS_C_NO_OID; -+ static gss_name_t name; -+ static OM_uint32 last_call = 0; -+ OM_uint32 lifetime, now, major, minor; -+ int equal; -+ -+ now = time(NULL); -+ -+ if (ctxt) { -+ debug("Rekey has happened - updating saved versions"); -+ -+ if (saved_name != GSS_C_NO_NAME) -+ gss_release_name(&minor, &saved_name); -+ -+ major = gss_inquire_cred(&minor, GSS_C_NO_CREDENTIAL, -+ &saved_name, &saved_lifetime, NULL, NULL); -+ -+ if (!GSS_ERROR(major)) { -+ saved_mech = ctxt->oid; -+ saved_lifetime+= now; -+ } else { -+ /* Handle the error */ -+ } -+ return 0; -+ } -+ -+ if (now - last_call < 10) -+ return 0; -+ -+ last_call = now; -+ -+ if (saved_mech == GSS_C_NO_OID) -+ return 0; -+ -+ major = gss_inquire_cred(&minor, GSS_C_NO_CREDENTIAL, -+ &name, &lifetime, NULL, NULL); -+ if (major == GSS_S_CREDENTIALS_EXPIRED) -+ return 0; -+ else if (GSS_ERROR(major)) -+ return 0; -+ -+ major = gss_compare_name(&minor, saved_name, name, &equal); -+ gss_release_name(&minor, &name); -+ if (GSS_ERROR(major)) -+ return 0; -+ -+ if (equal && (saved_lifetime < lifetime + now - 10)) -+ return 1; -+ -+ return 0; -+} -+ - #endif /* GSSAPI */ -diff -up openssh/gss-serv.c.gsskex openssh/gss-serv.c ---- openssh/gss-serv.c.gsskex 2018-08-20 07:57:29.000000000 +0200 -+++ openssh/gss-serv.c 2018-08-22 11:47:33.310216448 +0200 -@@ -44,17 +44,19 @@ - #include "session.h" - #include "misc.h" - #include "servconf.h" -+#include "uidswap.h" - - #include "ssh-gss.h" -+#include "monitor_wrap.h" - - extern ServerOptions options; - - static ssh_gssapi_client gssapi_client = -- { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER, -- GSS_C_NO_CREDENTIAL, NULL, {NULL, NULL, NULL, NULL}}; -+ { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER, GSS_C_NO_CREDENTIAL, -+ GSS_C_NO_NAME, NULL, {NULL, NULL, NULL, NULL}, 0, 0}; - - ssh_gssapi_mech gssapi_null_mech = -- { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL}; -+ { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL, NULL}; - - #ifdef KRB5 - extern ssh_gssapi_mech gssapi_kerberos_mech; -@@ -141,6 +143,28 @@ ssh_gssapi_server_ctx(Gssctxt **ctx, gss - } - - /* Unprivileged */ -+char * -+ssh_gssapi_server_mechanisms() { -+ if (supported_oids == NULL) -+ ssh_gssapi_prepare_supported_oids(); -+ return (ssh_gssapi_kex_mechs(supported_oids, -+ &ssh_gssapi_server_check_mech, NULL, NULL)); -+} -+ -+/* Unprivileged */ -+int -+ssh_gssapi_server_check_mech(Gssctxt **dum, gss_OID oid, const char *data, -+ const char *dummy) { -+ Gssctxt *ctx = NULL; -+ int res; -+ -+ res = !GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctx, oid))); -+ ssh_gssapi_delete_ctx(&ctx); -+ -+ return (res); -+} -+ -+/* Unprivileged */ - void - ssh_gssapi_supported_oids(gss_OID_set *oidset) - { -@@ -150,7 +174,9 @@ ssh_gssapi_supported_oids(gss_OID_set *o - gss_OID_set supported; - - gss_create_empty_oid_set(&min_status, oidset); -- gss_indicate_mechs(&min_status, &supported); -+ -+ if (GSS_ERROR(gss_indicate_mechs(&min_status, &supported))) -+ return; - - while (supported_mechs[i]->name != NULL) { - if (GSS_ERROR(gss_test_oid_set_member(&min_status, -@@ -276,8 +302,48 @@ OM_uint32 - ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client) - { - int i = 0; -+ int equal = 0; -+ gss_name_t new_name = GSS_C_NO_NAME; -+ gss_buffer_desc ename = GSS_C_EMPTY_BUFFER; -+ -+ if (options.gss_store_rekey && client->used && ctx->client_creds) { -+ if (client->mech->oid.length != ctx->oid->length || -+ (memcmp(client->mech->oid.elements, -+ ctx->oid->elements, ctx->oid->length) !=0)) { -+ debug("Rekeyed credentials have different mechanism"); -+ return GSS_S_COMPLETE; -+ } -+ -+ if ((ctx->major = gss_inquire_cred_by_mech(&ctx->minor, -+ ctx->client_creds, ctx->oid, &new_name, -+ NULL, NULL, NULL))) { -+ ssh_gssapi_error(ctx); -+ return (ctx->major); -+ } -+ -+ ctx->major = gss_compare_name(&ctx->minor, client->name, -+ new_name, &equal); -+ -+ if (GSS_ERROR(ctx->major)) { -+ ssh_gssapi_error(ctx); -+ return (ctx->major); -+ } -+ -+ if (!equal) { -+ debug("Rekeyed credentials have different name"); -+ return GSS_S_COMPLETE; -+ } -+ -+ debug("Marking rekeyed credentials for export"); - -- gss_buffer_desc ename; -+ gss_release_name(&ctx->minor, &client->name); -+ gss_release_cred(&ctx->minor, &client->creds); -+ client->name = new_name; -+ client->creds = ctx->client_creds; -+ ctx->client_creds = GSS_C_NO_CREDENTIAL; -+ client->updated = 1; -+ return GSS_S_COMPLETE; -+ } - - client->mech = NULL; - -@@ -292,6 +358,13 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_g - if (client->mech == NULL) - return GSS_S_FAILURE; - -+ if (ctx->client_creds && -+ (ctx->major = gss_inquire_cred_by_mech(&ctx->minor, -+ ctx->client_creds, ctx->oid, &client->name, NULL, NULL, NULL))) { -+ ssh_gssapi_error(ctx); -+ return (ctx->major); -+ } -+ - if ((ctx->major = gss_display_name(&ctx->minor, ctx->client, - &client->displayname, NULL))) { - ssh_gssapi_error(ctx); -@@ -309,6 +382,8 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_g - return (ctx->major); - } - -+ gss_release_buffer(&ctx->minor, &ename); -+ - /* We can't copy this structure, so we just move the pointer to it */ - client->creds = ctx->client_creds; - ctx->client_creds = GSS_C_NO_CREDENTIAL; -@@ -319,11 +394,20 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_g - void - ssh_gssapi_cleanup_creds(void) - { -- if (gssapi_client.store.filename != NULL) { -- /* Unlink probably isn't sufficient */ -- debug("removing gssapi cred file\"%s\"", -- gssapi_client.store.filename); -- unlink(gssapi_client.store.filename); -+ krb5_ccache ccache = NULL; -+ krb5_error_code problem; -+ -+ if (gssapi_client.store.data != NULL) { -+ if ((problem = krb5_cc_resolve(gssapi_client.store.data, gssapi_client.store.envval, &ccache))) { -+ debug("%s: krb5_cc_resolve(): %.100s", __func__, -+ krb5_get_err_text(gssapi_client.store.data, problem)); -+ } else if ((problem = krb5_cc_destroy(gssapi_client.store.data, ccache))) { -+ debug("%s: krb5_cc_resolve(): %.100s", __func__, -+ krb5_get_err_text(gssapi_client.store.data, problem)); -+ } else { -+ krb5_free_context(gssapi_client.store.data); -+ gssapi_client.store.data = NULL; -+ } - } - } - -@@ -356,7 +440,7 @@ ssh_gssapi_do_child(char ***envp, u_int - - /* Privileged */ - int --ssh_gssapi_userok(char *user) -+ssh_gssapi_userok(char *user, struct passwd *pw) - { - OM_uint32 lmin; - -@@ -366,9 +450,11 @@ ssh_gssapi_userok(char *user) - return 0; - } - if (gssapi_client.mech && gssapi_client.mech->userok) -- if ((*gssapi_client.mech->userok)(&gssapi_client, user)) -+ if ((*gssapi_client.mech->userok)(&gssapi_client, user)) { -+ gssapi_client.used = 1; -+ gssapi_client.store.owner = pw; - return 1; -- else { -+ } else { - /* Destroy delegated credentials if userok fails */ - gss_release_buffer(&lmin, &gssapi_client.displayname); - gss_release_buffer(&lmin, &gssapi_client.exportedname); -@@ -382,14 +468,89 @@ ssh_gssapi_userok(char *user) - return (0); - } - --/* Privileged */ --OM_uint32 --ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic) -+/* These bits are only used for rekeying. The unpriviledged child is running -+ * as the user, the monitor is root. -+ * -+ * In the child, we want to : -+ * *) Ask the monitor to store our credentials into the store we specify -+ * *) If it succeeds, maybe do a PAM update -+ */ -+ -+/* Stuff for PAM */ -+ -+#ifdef USE_PAM -+static int ssh_gssapi_simple_conv(int n, const struct pam_message **msg, -+ struct pam_response **resp, void *data) - { -- ctx->major = gss_verify_mic(&ctx->minor, ctx->context, -- gssbuf, gssmic, NULL); -+ return (PAM_CONV_ERR); -+} -+#endif - -- return (ctx->major); -+void -+ssh_gssapi_rekey_creds() { -+ int ok; -+ int ret; -+#ifdef USE_PAM -+ pam_handle_t *pamh = NULL; -+ struct pam_conv pamconv = {ssh_gssapi_simple_conv, NULL}; -+ char *envstr; -+#endif -+ -+ if (gssapi_client.store.envval == NULL && -+ gssapi_client.store.envvar == NULL) -+ return; -+ -+ ok = PRIVSEP(ssh_gssapi_update_creds(&gssapi_client.store)); -+ -+ if (!ok) -+ return; -+ -+ debug("Rekeyed credentials stored successfully"); -+ -+ /* Actually managing to play with the ssh pam stack from here will -+ * be next to impossible. In any case, we may want different options -+ * for rekeying. So, use our own :) -+ */ -+#ifdef USE_PAM -+ if (!use_privsep) { -+ debug("Not even going to try and do PAM with privsep disabled"); -+ return; -+ } -+ -+ ret = pam_start("sshd-rekey", gssapi_client.store.owner->pw_name, -+ &pamconv, &pamh); -+ if (ret) -+ return; -+ -+ xasprintf(&envstr, "%s=%s", gssapi_client.store.envvar, -+ gssapi_client.store.envval); -+ -+ ret = pam_putenv(pamh, envstr); -+ if (!ret) -+ pam_setcred(pamh, PAM_REINITIALIZE_CRED); -+ pam_end(pamh, PAM_SUCCESS); -+#endif -+} -+ -+int -+ssh_gssapi_update_creds(ssh_gssapi_ccache *store) { -+ int ok = 0; -+ -+ /* Check we've got credentials to store */ -+ if (!gssapi_client.updated) -+ return 0; -+ -+ gssapi_client.updated = 0; -+ -+ temporarily_use_uid(gssapi_client.store.owner); -+ if (gssapi_client.mech && gssapi_client.mech->updatecreds) -+ ok = (*gssapi_client.mech->updatecreds)(store, &gssapi_client); -+ else -+ debug("No update function for this mechanism"); -+ -+ restore_uid(); -+ -+ return ok; - } - - /* Privileged */ -diff -up openssh/gss-serv-krb5.c.gsskex openssh/gss-serv-krb5.c ---- openssh/gss-serv-krb5.c.gsskex 2018-08-20 07:57:29.000000000 +0200 -+++ openssh/gss-serv-krb5.c 2018-08-22 11:47:33.311216457 +0200 -@@ -120,7 +120,7 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_cl - krb5_error_code problem; - krb5_principal princ; - OM_uint32 maj_status, min_status; -- int len; -+ const char *new_ccname, *new_cctype; - const char *errmsg; - - if (client->creds == NULL) { -@@ -180,11 +180,23 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_cl - return; - } - -- client->store.filename = xstrdup(krb5_cc_get_name(krb_context, ccache)); -+ new_cctype = krb5_cc_get_type(krb_context, ccache); -+ new_ccname = krb5_cc_get_name(krb_context, ccache); -+ - client->store.envvar = "KRB5CCNAME"; -- len = strlen(client->store.filename) + 6; -- client->store.envval = xmalloc(len); -- snprintf(client->store.envval, len, "FILE:%s", client->store.filename); -+#ifdef USE_CCAPI -+ xasprintf(&client->store.envval, "API:%s", new_ccname); -+#else -+ if (new_ccname[0] == ':') -+ new_ccname++; -+ xasprintf(&client->store.envval, "%s:%s", new_cctype, new_ccname); -+ if (strcmp(new_cctype, "DIR") == 0) { -+ char *p; -+ p = strrchr(client->store.envval, '/'); -+ if (p) -+ *p = '\0'; -+ } -+#endif - - #ifdef USE_PAM - if (options.use_pam) -@@ -193,9 +205,76 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_cl - - krb5_cc_close(krb_context, ccache); - -+ client->store.data = krb_context; -+ - return; - } - -+int -+ssh_gssapi_krb5_updatecreds(ssh_gssapi_ccache *store, -+ ssh_gssapi_client *client) -+{ -+ krb5_ccache ccache = NULL; -+ krb5_principal principal = NULL; -+ char *name = NULL; -+ krb5_error_code problem; -+ OM_uint32 maj_status, min_status; -+ -+ if ((problem = krb5_cc_resolve(krb_context, store->envval, &ccache))) { -+ logit("krb5_cc_resolve(): %.100s", -+ krb5_get_err_text(krb_context, problem)); -+ return 0; -+ } -+ -+ /* Find out who the principal in this cache is */ -+ if ((problem = krb5_cc_get_principal(krb_context, ccache, -+ &principal))) { -+ logit("krb5_cc_get_principal(): %.100s", -+ krb5_get_err_text(krb_context, problem)); -+ krb5_cc_close(krb_context, ccache); -+ return 0; -+ } -+ -+ if ((problem = krb5_unparse_name(krb_context, principal, &name))) { -+ logit("krb5_unparse_name(): %.100s", -+ krb5_get_err_text(krb_context, problem)); -+ krb5_free_principal(krb_context, principal); -+ krb5_cc_close(krb_context, ccache); -+ return 0; -+ } -+ -+ -+ if (strcmp(name,client->exportedname.value)!=0) { -+ debug("Name in local credentials cache differs. Not storing"); -+ krb5_free_principal(krb_context, principal); -+ krb5_cc_close(krb_context, ccache); -+ krb5_free_unparsed_name(krb_context, name); -+ return 0; -+ } -+ krb5_free_unparsed_name(krb_context, name); -+ -+ /* Name matches, so lets get on with it! */ -+ -+ if ((problem = krb5_cc_initialize(krb_context, ccache, principal))) { -+ logit("krb5_cc_initialize(): %.100s", -+ krb5_get_err_text(krb_context, problem)); -+ krb5_free_principal(krb_context, principal); -+ krb5_cc_close(krb_context, ccache); -+ return 0; -+ } -+ -+ krb5_free_principal(krb_context, principal); -+ -+ if ((maj_status = gss_krb5_copy_ccache(&min_status, client->creds, -+ ccache))) { -+ logit("gss_krb5_copy_ccache() failed. Sorry!"); -+ krb5_cc_close(krb_context, ccache); -+ return 0; -+ } -+ -+ return 1; -+} -+ - ssh_gssapi_mech gssapi_kerberos_mech = { - "toWM5Slw5Ew8Mqkay+al2g==", - "Kerberos", -@@ -203,7 +282,8 @@ ssh_gssapi_mech gssapi_kerberos_mech = { - NULL, - &ssh_gssapi_krb5_userok, - NULL, -- &ssh_gssapi_krb5_storecreds -+ &ssh_gssapi_krb5_storecreds, -+ &ssh_gssapi_krb5_updatecreds - }; - - #endif /* KRB5 */ -diff -up openssh/kex.c.gsskex openssh/kex.c ---- openssh/kex.c.gsskex 2018-08-20 07:57:29.000000000 +0200 -+++ openssh/kex.c 2018-08-22 11:47:33.311216457 +0200 -@@ -54,6 +54,10 @@ - #include "sshbuf.h" - #include "digest.h" - -+#ifdef GSSAPI -+#include "ssh-gss.h" -+#endif -+ - /* prototype */ - static int kex_choose_conf(struct ssh *); - static int kex_input_newkeys(int, u_int32_t, struct ssh *); -@@ -103,6 +107,11 @@ static const struct kexalg kexalgs[] = { - { KEX_CURVE25519_SHA256, KEX_C25519_SHA256, 0, SSH_DIGEST_SHA256 }, - { KEX_CURVE25519_SHA256_OLD, KEX_C25519_SHA256, 0, SSH_DIGEST_SHA256 }, - #endif /* HAVE_EVP_SHA256 || !WITH_OPENSSL */ -+#ifdef GSSAPI -+ { KEX_GSS_GEX_SHA1_ID, KEX_GSS_GEX_SHA1, 0, SSH_DIGEST_SHA1 }, -+ { KEX_GSS_GRP1_SHA1_ID, KEX_GSS_GRP1_SHA1, 0, SSH_DIGEST_SHA1 }, -+ { KEX_GSS_GRP14_SHA1_ID, KEX_GSS_GRP14_SHA1, 0, SSH_DIGEST_SHA1 }, -+#endif - { NULL, -1, -1, -1}, - }; - -@@ -136,6 +145,12 @@ kex_alg_by_name(const char *name) - for (k = kexalgs; k->name != NULL; k++) { - if (strcmp(k->name, name) == 0) - return k; -+#ifdef GSSAPI -+ if (strncmp(name, "gss-", 4) == 0) { -+ if (strncmp(k->name, name, strlen(k->name)) == 0) -+ return k; -+ } -+#endif - } - return NULL; - } -diff -up openssh/kexgssc.c.gsskex openssh/kexgssc.c ---- openssh/kexgssc.c.gsskex 2018-08-22 11:47:33.311216457 +0200 -+++ openssh/kexgssc.c 2018-08-22 11:47:33.311216457 +0200 -@@ -0,0 +1,338 @@ -+/* -+ * Copyright (c) 2001-2009 Simon Wilkinson. 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" -+ -+#ifdef GSSAPI -+ -+#include "includes.h" -+ -+#include -+#include -+ -+#include -+ -+#include "xmalloc.h" -+#include "sshbuf.h" -+#include "ssh2.h" -+#include "sshkey.h" -+#include "cipher.h" -+#include "kex.h" -+#include "log.h" -+#include "packet.h" -+#include "dh.h" -+#include "digest.h" -+ -+#include "ssh-gss.h" -+ -+int -+kexgss_client(struct ssh *ssh) { -+ gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; -+ gss_buffer_desc recv_tok, gssbuf, msg_tok, *token_ptr; -+ Gssctxt *ctxt; -+ OM_uint32 maj_status, min_status, ret_flags; -+ u_int klen, kout, slen = 0, strlen; -+ DH *dh; -+ BIGNUM *dh_server_pub = NULL; -+ BIGNUM *shared_secret = NULL; -+ BIGNUM *p = NULL; -+ BIGNUM *g = NULL; -+ u_char *kbuf; -+ u_char *serverhostkey = NULL; -+ u_char *empty = ""; -+ char *msg; -+ char *lang; -+ int type = 0; -+ int first = 1; -+ int nbits = 0, min = DH_GRP_MIN, max = DH_GRP_MAX; -+ u_char hash[SSH_DIGEST_MAX_LENGTH]; -+ size_t hashlen; -+ -+ /* Initialise our GSSAPI world */ -+ ssh_gssapi_build_ctx(&ctxt); -+ if (ssh_gssapi_id_kex(ctxt, ssh->kex->name, ssh->kex->kex_type) -+ == GSS_C_NO_OID) -+ fatal("Couldn't identify host exchange"); -+ -+ if (ssh_gssapi_import_name(ctxt, ssh->kex->gss_host)) -+ fatal("Couldn't import hostname"); -+ -+ if (ssh->kex->gss_client && -+ ssh_gssapi_client_identity(ctxt, ssh->kex->gss_client)) -+ fatal("Couldn't acquire client credentials"); -+ -+ switch (ssh->kex->kex_type) { -+ case KEX_GSS_GRP1_SHA1: -+ dh = dh_new_group1(); -+ break; -+ case KEX_GSS_GRP14_SHA1: -+ dh = dh_new_group14(); -+ break; -+ case KEX_GSS_GEX_SHA1: -+ debug("Doing group exchange\n"); -+ nbits = dh_estimate(ssh->kex->we_need * 8); -+ packet_start(SSH2_MSG_KEXGSS_GROUPREQ); -+ packet_put_int(min); -+ packet_put_int(nbits); -+ packet_put_int(max); -+ -+ packet_send(); -+ -+ packet_read_expect(SSH2_MSG_KEXGSS_GROUP); -+ -+ if ((p = BN_new()) == NULL) -+ fatal("BN_new() failed"); -+ packet_get_bignum2(p); -+ if ((g = BN_new()) == NULL) -+ fatal("BN_new() failed"); -+ packet_get_bignum2(g); -+ packet_check_eom(); -+ -+ if (BN_num_bits(p) < min || BN_num_bits(p) > max) -+ fatal("GSSGRP_GEX group out of range: %d !< %d !< %d", -+ min, BN_num_bits(p), max); -+ -+ dh = dh_new_group(g, p); -+ break; -+ default: -+ fatal("%s: Unexpected KEX type %d", __func__, ssh->kex->kex_type); -+ } -+ -+ /* Step 1 - e is dh->pub_key */ -+ dh_gen_key(dh, ssh->kex->we_need * 8); -+ -+ /* This is f, we initialise it now to make life easier */ -+ dh_server_pub = BN_new(); -+ if (dh_server_pub == NULL) -+ fatal("dh_server_pub == NULL"); -+ -+ token_ptr = GSS_C_NO_BUFFER; -+ -+ do { -+ debug("Calling gss_init_sec_context"); -+ -+ maj_status = ssh_gssapi_init_ctx(ctxt, -+ ssh->kex->gss_deleg_creds, token_ptr, &send_tok, -+ &ret_flags); -+ -+ if (GSS_ERROR(maj_status)) { -+ if (send_tok.length != 0) { -+ packet_start(SSH2_MSG_KEXGSS_CONTINUE); -+ packet_put_string(send_tok.value, -+ send_tok.length); -+ } -+ fatal("gss_init_context failed"); -+ } -+ -+ /* If we've got an old receive buffer get rid of it */ -+ if (token_ptr != GSS_C_NO_BUFFER) -+ free(recv_tok.value); -+ -+ if (maj_status == GSS_S_COMPLETE) { -+ /* If mutual state flag is not true, kex fails */ -+ if (!(ret_flags & GSS_C_MUTUAL_FLAG)) -+ fatal("Mutual authentication failed"); -+ -+ /* If integ avail flag is not true kex fails */ -+ if (!(ret_flags & GSS_C_INTEG_FLAG)) -+ fatal("Integrity check failed"); -+ } -+ -+ /* -+ * If we have data to send, then the last message that we -+ * received cannot have been a 'complete'. -+ */ -+ if (send_tok.length != 0) { -+ if (first) { -+ packet_start(SSH2_MSG_KEXGSS_INIT); -+ packet_put_string(send_tok.value, -+ send_tok.length); -+ packet_put_bignum2(dh->pub_key); -+ first = 0; -+ } else { -+ packet_start(SSH2_MSG_KEXGSS_CONTINUE); -+ packet_put_string(send_tok.value, -+ send_tok.length); -+ } -+ packet_send(); -+ gss_release_buffer(&min_status, &send_tok); -+ -+ /* If we've sent them data, they should reply */ -+ do { -+ type = packet_read(); -+ if (type == SSH2_MSG_KEXGSS_HOSTKEY) { -+ debug("Received KEXGSS_HOSTKEY"); -+ if (serverhostkey) -+ fatal("Server host key received more than once"); -+ serverhostkey = -+ packet_get_string(&slen); -+ } -+ } while (type == SSH2_MSG_KEXGSS_HOSTKEY); -+ -+ switch (type) { -+ case SSH2_MSG_KEXGSS_CONTINUE: -+ debug("Received GSSAPI_CONTINUE"); -+ if (maj_status == GSS_S_COMPLETE) -+ fatal("GSSAPI Continue received from server when complete"); -+ recv_tok.value = packet_get_string(&strlen); -+ recv_tok.length = strlen; -+ break; -+ case SSH2_MSG_KEXGSS_COMPLETE: -+ debug("Received GSSAPI_COMPLETE"); -+ packet_get_bignum2(dh_server_pub); -+ msg_tok.value = packet_get_string(&strlen); -+ msg_tok.length = strlen; -+ -+ /* Is there a token included? */ -+ if (packet_get_char()) { -+ recv_tok.value= -+ packet_get_string(&strlen); -+ recv_tok.length = strlen; -+ /* If we're already complete - protocol error */ -+ if (maj_status == GSS_S_COMPLETE) -+ packet_disconnect("Protocol error: received token when complete"); -+ } else { -+ /* No token included */ -+ if (maj_status != GSS_S_COMPLETE) -+ packet_disconnect("Protocol error: did not receive final token"); -+ } -+ break; -+ case SSH2_MSG_KEXGSS_ERROR: -+ debug("Received Error"); -+ maj_status = packet_get_int(); -+ min_status = packet_get_int(); -+ msg = packet_get_string(NULL); -+ lang = packet_get_string(NULL); -+ fatal("GSSAPI Error: \n%.400s",msg); -+ default: -+ packet_disconnect("Protocol error: didn't expect packet type %d", -+ type); -+ } -+ token_ptr = &recv_tok; -+ } else { -+ /* No data, and not complete */ -+ if (maj_status != GSS_S_COMPLETE) -+ fatal("Not complete, and no token output"); -+ } -+ } while (maj_status & GSS_S_CONTINUE_NEEDED); -+ -+ /* -+ * We _must_ have received a COMPLETE message in reply from the -+ * server, which will have set dh_server_pub and msg_tok -+ */ -+ -+ if (type != SSH2_MSG_KEXGSS_COMPLETE) -+ fatal("Didn't receive a SSH2_MSG_KEXGSS_COMPLETE when I expected it"); -+ -+ /* Check f in range [1, p-1] */ -+ if (!dh_pub_is_valid(dh, dh_server_pub)) -+ packet_disconnect("bad server public DH value"); -+ -+ /* compute K=f^x mod p */ -+ klen = DH_size(dh); -+ kbuf = xmalloc(klen); -+ kout = DH_compute_key(kbuf, dh_server_pub, dh); -+ if ((int)kout < 0) -+ fatal("DH_compute_key: failed"); -+ -+ shared_secret = BN_new(); -+ if (shared_secret == NULL) -+ fatal("kexgss_client: BN_new failed"); -+ -+ if (BN_bin2bn(kbuf, kout, shared_secret) == NULL) -+ fatal("kexdh_client: BN_bin2bn failed"); -+ -+ memset(kbuf, 0, klen); -+ free(kbuf); -+ -+ hashlen = sizeof(hash); -+ switch (ssh->kex->kex_type) { -+ case KEX_GSS_GRP1_SHA1: -+ case KEX_GSS_GRP14_SHA1: -+ kex_dh_hash(ssh->kex->hash_alg, ssh->kex->client_version_string, -+ ssh->kex->server_version_string, -+ sshbuf_ptr(ssh->kex->my), sshbuf_len(ssh->kex->my), -+ sshbuf_ptr(ssh->kex->peer), sshbuf_len(ssh->kex->peer), -+ (serverhostkey ? serverhostkey : empty), slen, -+ dh->pub_key, /* e */ -+ dh_server_pub, /* f */ -+ shared_secret, /* K */ -+ hash, &hashlen -+ ); -+ break; -+ case KEX_GSS_GEX_SHA1: -+ kexgex_hash( -+ ssh->kex->hash_alg, -+ ssh->kex->client_version_string, -+ ssh->kex->server_version_string, -+ sshbuf_ptr(ssh->kex->my), sshbuf_len(ssh->kex->my), -+ sshbuf_ptr(ssh->kex->peer), sshbuf_len(ssh->kex->peer), -+ (serverhostkey ? serverhostkey : empty), slen, -+ min, nbits, max, -+ dh->p, dh->g, -+ dh->pub_key, -+ dh_server_pub, -+ shared_secret, -+ hash, &hashlen -+ ); -+ break; -+ default: -+ fatal("%s: Unexpected KEX type %d", __func__, ssh->kex->kex_type); -+ } -+ -+ gssbuf.value = hash; -+ gssbuf.length = hashlen; -+ -+ /* Verify that the hash matches the MIC we just got. */ -+ if (GSS_ERROR(ssh_gssapi_checkmic(ctxt, &gssbuf, &msg_tok))) -+ packet_disconnect("Hash's MIC didn't verify"); -+ -+ free(msg_tok.value); -+ -+ DH_free(dh); -+ if (serverhostkey) -+ free(serverhostkey); -+ BN_clear_free(dh_server_pub); -+ -+ /* save session id */ -+ if (ssh->kex->session_id == NULL) { -+ ssh->kex->session_id_len = hashlen; -+ ssh->kex->session_id = xmalloc(ssh->kex->session_id_len); -+ memcpy(ssh->kex->session_id, hash, ssh->kex->session_id_len); -+ } -+ -+ if (ssh->kex->gss_deleg_creds) -+ ssh_gssapi_credentials_updated(ctxt); -+ -+ if (gss_kex_context == NULL) -+ gss_kex_context = ctxt; -+ else -+ ssh_gssapi_delete_ctx(&ctxt); -+ -+ kex_derive_keys_bn(ssh, hash, hashlen, shared_secret); -+ BN_clear_free(shared_secret); -+ return kex_send_newkeys(ssh); -+} -+ -+#endif /* GSSAPI */ -diff -up openssh/kexgsss.c.gsskex openssh/kexgsss.c ---- openssh/kexgsss.c.gsskex 2018-08-22 11:47:33.311216457 +0200 -+++ openssh/kexgsss.c 2018-08-22 11:47:33.311216457 +0200 -@@ -0,0 +1,297 @@ -+/* -+ * Copyright (c) 2001-2009 Simon Wilkinson. 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" -+ -+#ifdef GSSAPI -+ -+#include -+ -+#include -+#include -+ -+#include "xmalloc.h" -+#include "sshbuf.h" -+#include "ssh2.h" -+#include "sshkey.h" -+#include "cipher.h" -+#include "kex.h" -+#include "log.h" -+#include "packet.h" -+#include "dh.h" -+#include "ssh-gss.h" -+#include "monitor_wrap.h" -+#include "misc.h" /* servconf.h needs misc.h for struct ForwardOptions */ -+#include "servconf.h" -+#include "ssh-gss.h" -+#include "digest.h" -+ -+extern ServerOptions options; -+ -+int -+kexgss_server(struct ssh *ssh) -+{ -+ OM_uint32 maj_status, min_status; -+ -+ /* -+ * Some GSSAPI implementations use the input value of ret_flags (an -+ * output variable) as a means of triggering mechanism specific -+ * features. Initializing it to zero avoids inadvertently -+ * activating this non-standard behaviour. -+ */ -+ -+ OM_uint32 ret_flags = 0; -+ gss_buffer_desc gssbuf, recv_tok, msg_tok; -+ gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; -+ Gssctxt *ctxt = NULL; -+ u_int slen, klen, kout; -+ u_char *kbuf; -+ DH *dh; -+ int min = -1, max = -1, nbits = -1; -+ int cmin = -1, cmax = -1; /* client proposal */ -+ BIGNUM *shared_secret = NULL; -+ BIGNUM *dh_client_pub = NULL; -+ int type = 0; -+ gss_OID oid; -+ char *mechs; -+ u_char hash[SSH_DIGEST_MAX_LENGTH]; -+ size_t hashlen; -+ -+ /* Initialise GSSAPI */ -+ -+ /* If we're rekeying, privsep means that some of the private structures -+ * in the GSSAPI code are no longer available. This kludges them back -+ * into life -+ */ -+ if (!ssh_gssapi_oid_table_ok()) -+ if ((mechs = ssh_gssapi_server_mechanisms())) -+ free(mechs); -+ -+ debug2("%s: Identifying %s", __func__, ssh->kex->name); -+ oid = ssh_gssapi_id_kex(NULL, ssh->kex->name, ssh->kex->kex_type); -+ if (oid == GSS_C_NO_OID) -+ fatal("Unknown gssapi mechanism"); -+ -+ debug2("%s: Acquiring credentials", __func__); -+ -+ if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, oid)))) -+ fatal("Unable to acquire credentials for the server"); -+ -+ switch (ssh->kex->kex_type) { -+ case KEX_GSS_GRP1_SHA1: -+ dh = dh_new_group1(); -+ break; -+ case KEX_GSS_GRP14_SHA1: -+ dh = dh_new_group14(); -+ break; -+ case KEX_GSS_GEX_SHA1: -+ debug("Doing group exchange"); -+ packet_read_expect(SSH2_MSG_KEXGSS_GROUPREQ); -+ /* store client proposal to provide valid signature */ -+ cmin = packet_get_int(); -+ nbits = packet_get_int(); -+ cmax = packet_get_int(); -+ min = MAX(DH_GRP_MIN, cmin); -+ max = MIN(DH_GRP_MAX, cmax); -+ packet_check_eom(); -+ if (max < min || nbits < min || max < nbits) -+ fatal("GSS_GEX, bad parameters: %d !< %d !< %d", -+ min, nbits, max); -+ dh = PRIVSEP(choose_dh(min, nbits, max)); -+ if (dh == NULL) -+ packet_disconnect("Protocol error: no matching group found"); -+ -+ packet_start(SSH2_MSG_KEXGSS_GROUP); -+ packet_put_bignum2(dh->p); -+ packet_put_bignum2(dh->g); -+ packet_send(); -+ -+ packet_write_wait(); -+ break; -+ default: -+ fatal("%s: Unexpected KEX type %d", __func__, ssh->kex->kex_type); -+ } -+ -+ dh_gen_key(dh, ssh->kex->we_need * 8); -+ -+ do { -+ debug("Wait SSH2_MSG_GSSAPI_INIT"); -+ type = packet_read(); -+ switch(type) { -+ case SSH2_MSG_KEXGSS_INIT: -+ if (dh_client_pub != NULL) -+ fatal("Received KEXGSS_INIT after initialising"); -+ recv_tok.value = packet_get_string(&slen); -+ recv_tok.length = slen; -+ -+ if ((dh_client_pub = BN_new()) == NULL) -+ fatal("dh_client_pub == NULL"); -+ -+ packet_get_bignum2(dh_client_pub); -+ -+ /* Send SSH_MSG_KEXGSS_HOSTKEY here, if we want */ -+ break; -+ case SSH2_MSG_KEXGSS_CONTINUE: -+ recv_tok.value = packet_get_string(&slen); -+ recv_tok.length = slen; -+ break; -+ default: -+ packet_disconnect( -+ "Protocol error: didn't expect packet type %d", -+ type); -+ } -+ -+ maj_status = PRIVSEP(ssh_gssapi_accept_ctx(ctxt, &recv_tok, -+ &send_tok, &ret_flags)); -+ -+ free(recv_tok.value); -+ -+ if (maj_status != GSS_S_COMPLETE && send_tok.length == 0) -+ fatal("Zero length token output when incomplete"); -+ -+ if (dh_client_pub == NULL) -+ fatal("No client public key"); -+ -+ if (maj_status & GSS_S_CONTINUE_NEEDED) { -+ debug("Sending GSSAPI_CONTINUE"); -+ packet_start(SSH2_MSG_KEXGSS_CONTINUE); -+ packet_put_string(send_tok.value, send_tok.length); -+ packet_send(); -+ gss_release_buffer(&min_status, &send_tok); -+ } -+ } while (maj_status & GSS_S_CONTINUE_NEEDED); -+ -+ if (GSS_ERROR(maj_status)) { -+ if (send_tok.length > 0) { -+ packet_start(SSH2_MSG_KEXGSS_CONTINUE); -+ packet_put_string(send_tok.value, send_tok.length); -+ packet_send(); -+ } -+ fatal("accept_ctx died"); -+ } -+ -+ if (!(ret_flags & GSS_C_MUTUAL_FLAG)) -+ fatal("Mutual Authentication flag wasn't set"); -+ -+ if (!(ret_flags & GSS_C_INTEG_FLAG)) -+ fatal("Integrity flag wasn't set"); -+ -+ if (!dh_pub_is_valid(dh, dh_client_pub)) -+ packet_disconnect("bad client public DH value"); -+ -+ klen = DH_size(dh); -+ kbuf = xmalloc(klen); -+ kout = DH_compute_key(kbuf, dh_client_pub, dh); -+ if ((int)kout < 0) -+ fatal("DH_compute_key: failed"); -+ -+ shared_secret = BN_new(); -+ if (shared_secret == NULL) -+ fatal("kexgss_server: BN_new failed"); -+ -+ if (BN_bin2bn(kbuf, kout, shared_secret) == NULL) -+ fatal("kexgss_server: BN_bin2bn failed"); -+ -+ memset(kbuf, 0, klen); -+ free(kbuf); -+ -+ hashlen = sizeof(hash); -+ switch (ssh->kex->kex_type) { -+ case KEX_GSS_GRP1_SHA1: -+ case KEX_GSS_GRP14_SHA1: -+ kex_dh_hash(ssh->kex->hash_alg, -+ ssh->kex->client_version_string, ssh->kex->server_version_string, -+ sshbuf_ptr(ssh->kex->peer), sshbuf_len(ssh->kex->peer), -+ sshbuf_ptr(ssh->kex->my), sshbuf_len(ssh->kex->my), -+ NULL, 0, /* Change this if we start sending host keys */ -+ dh_client_pub, dh->pub_key, shared_secret, -+ hash, &hashlen -+ ); -+ break; -+ case KEX_GSS_GEX_SHA1: -+ kexgex_hash( -+ ssh->kex->hash_alg, -+ ssh->kex->client_version_string, ssh->kex->server_version_string, -+ sshbuf_ptr(ssh->kex->peer), sshbuf_len(ssh->kex->peer), -+ sshbuf_ptr(ssh->kex->my), sshbuf_len(ssh->kex->my), -+ NULL, 0, -+ cmin, nbits, cmax, -+ dh->p, dh->g, -+ dh_client_pub, -+ dh->pub_key, -+ shared_secret, -+ hash, &hashlen -+ ); -+ break; -+ default: -+ fatal("%s: Unexpected KEX type %d", __func__, ssh->kex->kex_type); -+ } -+ -+ BN_clear_free(dh_client_pub); -+ -+ if (ssh->kex->session_id == NULL) { -+ ssh->kex->session_id_len = hashlen; -+ ssh->kex->session_id = xmalloc(ssh->kex->session_id_len); -+ memcpy(ssh->kex->session_id, hash, ssh->kex->session_id_len); -+ } -+ -+ gssbuf.value = hash; -+ gssbuf.length = hashlen; -+ -+ if (GSS_ERROR(PRIVSEP(ssh_gssapi_sign(ctxt,&gssbuf,&msg_tok)))) -+ fatal("Couldn't get MIC"); -+ -+ packet_start(SSH2_MSG_KEXGSS_COMPLETE); -+ packet_put_bignum2(dh->pub_key); -+ packet_put_string(msg_tok.value,msg_tok.length); -+ -+ if (send_tok.length != 0) { -+ packet_put_char(1); /* true */ -+ packet_put_string(send_tok.value, send_tok.length); -+ } else { -+ packet_put_char(0); /* false */ -+ } -+ packet_send(); -+ -+ gss_release_buffer(&min_status, &send_tok); -+ gss_release_buffer(&min_status, &msg_tok); -+ -+ if (gss_kex_context == NULL) -+ gss_kex_context = ctxt; -+ else -+ ssh_gssapi_delete_ctx(&ctxt); -+ -+ DH_free(dh); -+ -+ kex_derive_keys_bn(ssh, hash, hashlen, shared_secret); -+ BN_clear_free(shared_secret); -+ kex_send_newkeys(ssh); -+ -+ /* If this was a rekey, then save out any delegated credentials we -+ * just exchanged. */ -+ if (options.gss_store_rekey) -+ ssh_gssapi_rekey_creds(); -+ return 0; -+} -+#endif /* GSSAPI */ -diff -up openssh/kex.h.gsskex openssh/kex.h ---- openssh/kex.h.gsskex 2018-08-20 07:57:29.000000000 +0200 -+++ openssh/kex.h 2018-08-22 11:47:33.311216457 +0200 -@@ -100,6 +100,11 @@ enum kex_exchange { - KEX_DH_GEX_SHA256, - KEX_ECDH_SHA2, - KEX_C25519_SHA256, -+#ifdef GSSAPI -+ KEX_GSS_GRP1_SHA1, -+ KEX_GSS_GRP14_SHA1, -+ KEX_GSS_GEX_SHA1, -+#endif - KEX_MAX - }; - -@@ -148,6 +153,12 @@ struct kex { - u_int flags; - int hash_alg; - int ec_nid; -+#ifdef GSSAPI -+ int gss_deleg_creds; -+ int gss_trust_dns; -+ char *gss_host; -+ char *gss_client; -+#endif - char *client_version_string; - char *server_version_string; - char *failed_choice; -@@ -197,6 +208,10 @@ int kexecdh_client(struct ssh *); - int kexecdh_server(struct ssh *); - int kexc25519_client(struct ssh *); - int kexc25519_server(struct ssh *); -+#ifdef GSSAPI -+int kexgss_client(struct ssh *); -+int kexgss_server(struct ssh *); -+#endif - - int kex_dh_hash(int, const char *, const char *, - const u_char *, size_t, const u_char *, size_t, const u_char *, size_t, -diff -up openssh/Makefile.in.gsskex openssh/Makefile.in ---- openssh/Makefile.in.gsskex 2018-08-22 11:47:33.312216465 +0200 -+++ openssh/Makefile.in 2018-08-22 13:19:54.955928277 +0200 -@@ -100,6 +100,7 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \ - readpass.o ttymodes.o xmalloc.o addrmatch.o \ - atomicio.o dispatch.o mac.o uuencode.o misc.o utf8.o \ - monitor_fdpass.o rijndael.o ssh-dss.o ssh-ecdsa.o ssh-rsa.o dh.o \ -+ kexgssc.o \ - msg.o progressmeter.o dns.o entropy.o gss-genr.o umac.o umac128.o \ - ssh-pkcs11.o smult_curve25519_ref.o \ - poly1305.o chacha.o cipher-chachapoly.o \ -@@ -121,7 +122,7 @@ SSHDOBJS=sshd.o auth-rhosts.o auth-passw - auth-bsdauth.o auth2-hostbased.o auth2-kbdint.o \ - auth2-none.o auth2-passwd.o auth2-pubkey.o \ - monitor.o monitor_wrap.o auth-krb5.o \ -- auth2-gss.o gss-serv.o gss-serv-krb5.o \ -+ auth2-gss.o gss-serv.o gss-serv-krb5.o kexgsss.o \ - loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \ - sftp-server.o sftp-common.o \ - sandbox-null.o sandbox-rlimit.o sandbox-systrace.o sandbox-darwin.o \ -diff -up openssh/monitor.c.gsskex openssh/monitor.c ---- openssh/monitor.c.gsskex 2018-08-22 11:47:33.263216069 +0200 -+++ openssh/monitor.c 2018-08-22 13:22:19.589095240 +0200 -@@ -146,6 +146,8 @@ int mm_answer_gss_setup_ctx(int, struct - int mm_answer_gss_accept_ctx(int, struct sshbuf *); - int mm_answer_gss_userok(int, struct sshbuf *); - int mm_answer_gss_checkmic(int, struct sshbuf *); -+int mm_answer_gss_sign(int, struct sshbuf *); -+int mm_answer_gss_updatecreds(int, struct sshbuf *); - #endif - - #ifdef SSH_AUDIT_EVENTS -@@ -219,11 +221,18 @@ struct mon_table mon_dispatch_proto20[] - {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}, -+ {MONITOR_REQ_GSSSIGN, MON_ONCE, mm_answer_gss_sign}, - #endif - {0, 0, NULL} - }; - - struct mon_table mon_dispatch_postauth20[] = { -+#ifdef GSSAPI -+ {MONITOR_REQ_GSSSETUP, 0, mm_answer_gss_setup_ctx}, -+ {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx}, -+ {MONITOR_REQ_GSSSIGN, 0, mm_answer_gss_sign}, -+ {MONITOR_REQ_GSSUPCREDS, 0, mm_answer_gss_updatecreds}, -+#endif - #ifdef WITH_OPENSSL - {MONITOR_REQ_MODULI, 0, mm_answer_moduli}, - #endif -@@ -293,6 +302,10 @@ monitor_child_preauth(Authctxt *_authctx - /* Permit requests for moduli and signatures */ - monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1); - monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); -+#ifdef GSSAPI -+ /* and for the GSSAPI key exchange */ -+ monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1); -+#endif - - /* The first few requests do not require asynchronous access */ - while (!authenticated) { -@@ -405,6 +418,10 @@ monitor_child_postauth(struct monitor *p - monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1); - monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); - monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1); -+#ifdef GSSAPI -+ /* and for the GSSAPI key exchange */ -+ monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1); -+#endif - - if (auth_opts->permit_pty_flag) { - monitor_permit(mon_dispatch, MONITOR_REQ_PTY, 1); -@@ -1695,6 +1712,13 @@ monitor_apply_keystate(struct monitor *p - # endif - #endif /* WITH_OPENSSL */ - kex->kex[KEX_C25519_SHA256] = kexc25519_server; -+#ifdef GSSAPI -+ if (options.gss_keyex) { -+ kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server; -+ kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server; -+ kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server; -+ } -+#endif - 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; -@@ -1785,7 +1809,7 @@ mm_answer_gss_setup_ctx(int sock, struct - u_char *p; - int r; - -- if (!options.gss_authentication) -+ if (!options.gss_authentication && !options.gss_keyex) - fatal("%s: GSSAPI authentication not enabled", __func__); - - if ((r = sshbuf_get_string(m, &p, &len)) != 0) -@@ -1818,7 +1842,7 @@ mm_answer_gss_accept_ctx(int sock, struc - OM_uint32 flags = 0; /* GSI needs this */ - int r; - -- if (!options.gss_authentication) -+ if (!options.gss_authentication && !options.gss_keyex) - fatal("%s: GSSAPI authentication not enabled", __func__); - - if ((r = ssh_gssapi_get_buffer_desc(m, &in)) != 0) -@@ -1839,6 +1863,7 @@ mm_answer_gss_accept_ctx(int sock, struc - monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP, 0); - monitor_permit(mon_dispatch, MONITOR_REQ_GSSUSEROK, 1); - monitor_permit(mon_dispatch, MONITOR_REQ_GSSCHECKMIC, 1); -+ monitor_permit(mon_dispatch, MONITOR_REQ_GSSSIGN, 1); - } - return (0); - } -@@ -1850,7 +1875,7 @@ mm_answer_gss_checkmic(int sock, struct - OM_uint32 ret; - int r; - -- if (!options.gss_authentication) -+ if (!options.gss_authentication && !options.gss_keyex) - fatal("%s: GSSAPI authentication not enabled", __func__); - - if ((r = ssh_gssapi_get_buffer_desc(m, &gssbuf)) != 0 || -@@ -1880,10 +1905,11 @@ mm_answer_gss_userok(int sock, struct ss - int r, authenticated; - const char *displayname; - -- if (!options.gss_authentication) -+ if (!options.gss_authentication && !options.gss_keyex) - fatal("%s: GSSAPI authentication not enabled", __func__); - -- authenticated = authctxt->valid && ssh_gssapi_userok(authctxt->user); -+ authenticated = authctxt->valid && -+ ssh_gssapi_userok(authctxt->user, authctxt->pw); - - sshbuf_reset(m); - if ((r = sshbuf_put_u32(m, authenticated)) != 0) -@@ -1900,5 +1926,74 @@ mm_answer_gss_userok(int sock, struct ss - /* Monitor loop will terminate if authenticated */ - return (authenticated); - } -+ -+int -+mm_answer_gss_sign(int socket, struct sshbuf *m) -+{ -+ gss_buffer_desc data; -+ gss_buffer_desc hash = GSS_C_EMPTY_BUFFER; -+ OM_uint32 major, minor; -+ int r; -+ -+ if (!options.gss_authentication && !options.gss_keyex) -+ fatal("In GSSAPI monitor when GSSAPI is disabled"); -+ -+ if ((r = sshbuf_get_string(m, (u_char **)&data.value, &data.length)) != 0) -+ fatal("%s: buffer error: %s", __func__, ssh_err(r)); -+ if (data.length != 20) -+ fatal("%s: data length incorrect: %d", __func__, -+ (int) data.length); -+ -+ /* Save the session ID on the first time around */ -+ if (session_id2_len == 0) { -+ session_id2_len = data.length; -+ session_id2 = xmalloc(session_id2_len); -+ memcpy(session_id2, data.value, session_id2_len); -+ } -+ major = ssh_gssapi_sign(gsscontext, &data, &hash); -+ -+ free(data.value); -+ -+ sshbuf_reset(m); -+ if ((r = sshbuf_put_u32(m, major)) != 0 || -+ (r = sshbuf_put_string(m, hash.value, hash.length)) != 0) -+ fatal("%s: buffer error: %s", __func__, ssh_err(r)); -+ -+ mm_request_send(socket, MONITOR_ANS_GSSSIGN, m); -+ -+ gss_release_buffer(&minor, &hash); -+ -+ /* Turn on getpwnam permissions */ -+ monitor_permit(mon_dispatch, MONITOR_REQ_PWNAM, 1); -+ -+ /* And credential updating, for when rekeying */ -+ monitor_permit(mon_dispatch, MONITOR_REQ_GSSUPCREDS, 1); -+ -+ return (0); -+} -+ -+int -+mm_answer_gss_updatecreds(int socket, struct sshbuf *m) { -+ ssh_gssapi_ccache store; -+ int ok, r; -+ -+ if ((r = sshbuf_get_cstring(m, &store.envvar, NULL)) != 0 || -+ (r = sshbuf_get_cstring(m, &store.envval, NULL)) != 0) -+ fatal("%s: buffer error: %s", __func__, ssh_err(r)); -+ -+ ok = ssh_gssapi_update_creds(&store); -+ -+ free(store.envvar); -+ free(store.envval); -+ -+ sshbuf_reset(m); -+ if ((r = sshbuf_put_u32(m, ok)) != 0) -+ fatal("%s: buffer error: %s", __func__, ssh_err(r)); -+ -+ mm_request_send(socket, MONITOR_ANS_GSSUPCREDS, m); -+ -+ return(0); -+} -+ - #endif /* GSSAPI */ - -diff -up openssh/monitor.h.gsskex openssh/monitor.h ---- openssh/monitor.h.gsskex 2018-08-22 11:47:33.263216069 +0200 -+++ openssh/monitor.h 2018-08-22 11:47:33.313216473 +0200 -@@ -58,6 +58,8 @@ enum monitor_reqtype { - #ifdef WITH_SELINUX - MONITOR_REQ_AUTHROLE = 80, - #endif -+ MONITOR_REQ_GSSSIGN = 82, MONITOR_ANS_GSSSIGN = 83, -+ MONITOR_REQ_GSSUPCREDS = 84, MONITOR_ANS_GSSUPCREDS = 85, - - MONITOR_REQ_PAM_START = 100, - MONITOR_REQ_PAM_ACCOUNT = 102, MONITOR_ANS_PAM_ACCOUNT = 103, -diff -up openssh/monitor_wrap.c.gsskex openssh/monitor_wrap.c ---- openssh/monitor_wrap.c.gsskex 2018-08-22 11:47:33.313216473 +0200 -+++ openssh/monitor_wrap.c 2018-08-22 13:27:38.665669643 +0200 -@@ -1004,7 +1004,7 @@ mm_ssh_gssapi_checkmic(Gssctxt *ctx, gss - } - - int --mm_ssh_gssapi_userok(char *user) -+mm_ssh_gssapi_userok(char *user, struct passwd *pw) - { - struct sshbuf *m; - int r, authenticated = 0; -@@ -1023,4 +1023,52 @@ mm_ssh_gssapi_userok(char *user) - debug3("%s: user %sauthenticated",__func__, authenticated ? "" : "not "); - return (authenticated); - } -+ -+OM_uint32 -+mm_ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_desc *data, gss_buffer_desc *hash) -+{ -+ struct sshbuf *m; -+ OM_uint32 major; -+ int r; -+ -+ if ((m = sshbuf_new()) == NULL) -+ fatal("%s: sshbuf_new failed", __func__); -+ if ((r = sshbuf_put_string(m, data->value, data->length)) != 0) -+ fatal("%s: buffer error: %s", __func__, ssh_err(r)); -+ -+ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSSIGN, m); -+ mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSSIGN, m); -+ -+ if ((r = sshbuf_get_u32(m, &major)) != 0 || -+ (r = sshbuf_get_string(m, (u_char **)&hash->value, &hash->length)) != 0) -+ fatal("%s: buffer error: %s", __func__, ssh_err(r)); -+ -+ sshbuf_free(m); -+ -+ return(major); -+} -+ -+int -+mm_ssh_gssapi_update_creds(ssh_gssapi_ccache *store) -+{ -+ struct sshbuf *m; -+ int ok, r; -+ -+ if ((m = sshbuf_new()) == NULL) -+ fatal("%s: sshbuf_new failed", __func__); -+ -+ if ((r = sshbuf_put_cstring(m, store->envvar ? store->envvar : "")) != 0 || -+ (r = sshbuf_put_cstring(m, store->envval ? store->envval : "")) != 0) -+ fatal("%s: buffer error: %s", __func__, ssh_err(r)); -+ -+ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSUPCREDS, m); -+ mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSUPCREDS, m); -+ -+ if ((r = sshbuf_get_u32(m, &ok)) != 0) -+ fatal("%s: buffer error: %s", __func__, ssh_err(r)); -+ -+ sshbuf_free(m); -+ -+ return (ok); -+} - #endif /* GSSAPI */ -diff -up openssh/monitor_wrap.h.gsskex openssh/monitor_wrap.h ---- openssh/monitor_wrap.h.gsskex 2018-08-22 11:47:33.263216069 +0200 -+++ openssh/monitor_wrap.h 2018-08-22 11:47:33.313216473 +0200 -@@ -63,8 +63,10 @@ int mm_sshkey_verify(const struct sshkey - OM_uint32 mm_ssh_gssapi_server_ctx(Gssctxt **, gss_OID); - OM_uint32 mm_ssh_gssapi_accept_ctx(Gssctxt *, - gss_buffer_desc *, gss_buffer_desc *, OM_uint32 *); --int mm_ssh_gssapi_userok(char *user); -+int mm_ssh_gssapi_userok(char *user, struct passwd *); - OM_uint32 mm_ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t); -+OM_uint32 mm_ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t); -+int mm_ssh_gssapi_update_creds(ssh_gssapi_ccache *); - #endif - - #ifdef USE_PAM -diff -up openssh/readconf.c.gsskex openssh/readconf.c ---- openssh/readconf.c.gsskex 2018-08-20 07:57:29.000000000 +0200 -+++ openssh/readconf.c 2018-08-22 13:28:17.487982869 +0200 -@@ -161,6 +161,8 @@ typedef enum { - oClearAllForwardings, oNoHostAuthenticationForLocalhost, - oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout, - oAddressFamily, oGssAuthentication, oGssDelegateCreds, -+ oGssTrustDns, oGssKeyEx, oGssClientIdentity, oGssRenewalRekey, -+ oGssServerIdentity, - oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly, - oSendEnv, oSetEnv, oControlPath, oControlMaster, oControlPersist, - oHashKnownHosts, -@@ -201,10 +203,19 @@ static struct { - /* Sometimes-unsupported options */ - #if defined(GSSAPI) - { "gssapiauthentication", oGssAuthentication }, -+ { "gssapikeyexchange", oGssKeyEx }, - { "gssapidelegatecredentials", oGssDelegateCreds }, -+ { "gssapitrustdns", oGssTrustDns }, -+ { "gssapiclientidentity", oGssClientIdentity }, -+ { "gssapiserveridentity", oGssServerIdentity }, -+ { "gssapirenewalforcesrekey", oGssRenewalRekey }, - # else - { "gssapiauthentication", oUnsupported }, -+ { "gssapikeyexchange", oUnsupported }, - { "gssapidelegatecredentials", oUnsupported }, -+ { "gssapitrustdns", oUnsupported }, -+ { "gssapiclientidentity", oUnsupported }, -+ { "gssapirenewalforcesrekey", oUnsupported }, - #endif - #ifdef ENABLE_PKCS11 - { "smartcarddevice", oPKCS11Provider }, -@@ -973,10 +984,30 @@ parse_time: - intptr = &options->gss_authentication; - goto parse_flag; - -+ case oGssKeyEx: -+ intptr = &options->gss_keyex; -+ goto parse_flag; -+ - case oGssDelegateCreds: - intptr = &options->gss_deleg_creds; - goto parse_flag; - -+ case oGssTrustDns: -+ intptr = &options->gss_trust_dns; -+ goto parse_flag; -+ -+ case oGssClientIdentity: -+ charptr = &options->gss_client_identity; -+ goto parse_string; -+ -+ case oGssServerIdentity: -+ charptr = &options->gss_server_identity; -+ goto parse_string; -+ -+ case oGssRenewalRekey: -+ intptr = &options->gss_renewal_rekey; -+ goto parse_flag; -+ - case oBatchMode: - intptr = &options->batch_mode; - goto parse_flag; -@@ -1817,7 +1848,12 @@ initialize_options(Options * options) - options->pubkey_authentication = -1; - options->challenge_response_authentication = -1; - options->gss_authentication = -1; -+ options->gss_keyex = -1; - options->gss_deleg_creds = -1; -+ options->gss_trust_dns = -1; -+ options->gss_renewal_rekey = -1; -+ options->gss_client_identity = NULL; -+ options->gss_server_identity = NULL; - options->password_authentication = -1; - options->kbd_interactive_authentication = -1; - options->kbd_interactive_devices = NULL; -@@ -1962,8 +1998,14 @@ fill_default_options(Options * options) - options->challenge_response_authentication = 1; - if (options->gss_authentication == -1) - options->gss_authentication = 0; -+ if (options->gss_keyex == -1) -+ options->gss_keyex = 0; - if (options->gss_deleg_creds == -1) - options->gss_deleg_creds = 0; -+ if (options->gss_trust_dns == -1) -+ options->gss_trust_dns = 0; -+ if (options->gss_renewal_rekey == -1) -+ options->gss_renewal_rekey = 0; - if (options->password_authentication == -1) - options->password_authentication = 1; - if (options->kbd_interactive_authentication == -1) -diff -up openssh/readconf.h.gsskex openssh/readconf.h ---- openssh/readconf.h.gsskex 2018-08-20 07:57:29.000000000 +0200 -+++ openssh/readconf.h 2018-08-22 11:47:33.314216481 +0200 -@@ -40,7 +40,12 @@ typedef struct { - int challenge_response_authentication; - /* Try S/Key or TIS, authentication. */ - int gss_authentication; /* Try GSS authentication */ -+ int gss_keyex; /* Try GSS key exchange */ - int gss_deleg_creds; /* Delegate GSS credentials */ -+ int gss_trust_dns; /* Trust DNS for GSS canonicalization */ -+ int gss_renewal_rekey; /* Credential renewal forces rekey */ -+ char *gss_client_identity; /* Principal to initiate GSSAPI with */ -+ char *gss_server_identity; /* GSSAPI target principal */ - int password_authentication; /* Try password - * authentication. */ - int kbd_interactive_authentication; /* Try keyboard-interactive auth. */ -diff -up openssh/regress/cert-hostkey.sh.gsskex openssh/regress/cert-hostkey.sh ---- openssh/regress/cert-hostkey.sh.gsskex 2018-08-20 07:57:29.000000000 +0200 -+++ openssh/regress/cert-hostkey.sh 2018-08-22 11:47:33.314216481 +0200 -@@ -66,7 +66,7 @@ touch $OBJ/host_revoked_plain - touch $OBJ/host_revoked_cert - cat $OBJ/host_ca_key.pub $OBJ/host_ca_key2.pub > $OBJ/host_revoked_ca - --PLAIN_TYPES=`$SSH -Q key-plain | sed 's/^ssh-dss/ssh-dsa/g;s/^ssh-//'` -+PLAIN_TYPES=`$SSH -Q key-plain | grep -v null | sed 's/^ssh-dss/ssh-dsa/g;s/^ssh-//'` - - if echo "$PLAIN_TYPES" | grep '^rsa$' >/dev/null 2>&1 ; then - PLAIN_TYPES="$PLAIN_TYPES rsa-sha2-256 rsa-sha2-512" -diff -up openssh/regress/cert-userkey.sh.gsskex openssh/regress/cert-userkey.sh ---- openssh/regress/cert-userkey.sh.gsskex 2018-08-20 07:57:29.000000000 +0200 -+++ openssh/regress/cert-userkey.sh 2018-08-22 11:47:33.314216481 +0200 -@@ -7,7 +7,7 @@ rm -f $OBJ/authorized_keys_$USER $OBJ/us - cp $OBJ/sshd_proxy $OBJ/sshd_proxy_bak - cp $OBJ/ssh_proxy $OBJ/ssh_proxy_bak - --PLAIN_TYPES=`$SSH -Q key-plain | sed 's/^ssh-dss/ssh-dsa/;s/^ssh-//'` -+PLAIN_TYPES=`$SSH -Q key-plain | grep -v null | sed 's/^ssh-dss/ssh-dsa/;s/^ssh-//'` - EXTRA_TYPES="" - - if echo "$PLAIN_TYPES" | grep '^rsa$' >/dev/null 2>&1 ; then -diff -up openssh/regress/kextype.sh.gsskex openssh/regress/kextype.sh ---- openssh/regress/kextype.sh.gsskex 2018-08-20 07:57:29.000000000 +0200 -+++ openssh/regress/kextype.sh 2018-08-22 11:47:33.315216489 +0200 -@@ -14,6 +14,9 @@ echo "KexAlgorithms=$KEXOPT" >> $OBJ/ssh - - tries="1 2 3 4" - for k in `${SSH} -Q kex`; do -+ if [ $k = "gss-gex-sha1-" -o $k = "gss-group1-sha1-" -o $k = "gss-group14-sha1-" ]; then -+ continue -+ fi - verbose "kex $k" - for i in $tries; do - ${SSH} -F $OBJ/ssh_proxy -o KexAlgorithms=$k x true -diff -up openssh/regress/rekey.sh.gsskex openssh/regress/rekey.sh ---- openssh/regress/rekey.sh.gsskex 2018-08-20 07:57:29.000000000 +0200 -+++ openssh/regress/rekey.sh 2018-08-22 11:47:33.315216489 +0200 -@@ -38,6 +38,9 @@ increase_datafile_size 300 - - opts="" - for i in `${SSH} -Q kex`; do -+ if [ $i = "gss-gex-sha1-" -o $i = "gss-group1-sha1-" -o $i = "gss-group14-sha1-" ]; then -+ continue -+ fi - opts="$opts KexAlgorithms=$i" - done - for i in `${SSH} -Q cipher`; do -@@ -56,6 +59,9 @@ done - if ${SSH} -Q cipher-auth | grep '^.*$' >/dev/null 2>&1 ; then - for c in `${SSH} -Q cipher-auth`; do - for kex in `${SSH} -Q kex`; do -+ if [ $kex = "gss-gex-sha1-" -o $kex = "gss-group1-sha1-" -o $kex = "gss-group14-sha1-" ]; then -+ continue -+ fi - verbose "client rekey $c $kex" - ssh_data_rekeying "KexAlgorithms=$kex" -oRekeyLimit=256k -oCiphers=$c - done -diff -up openssh/servconf.c.gsskex openssh/servconf.c ---- openssh/servconf.c.gsskex 2018-08-22 11:47:33.296216335 +0200 -+++ openssh/servconf.c 2018-08-22 13:28:41.905179879 +0200 -@@ -124,8 +124,10 @@ initialize_server_options(ServerOptions - options->kerberos_ticket_cleanup = -1; - options->kerberos_get_afs_token = -1; - options->gss_authentication=-1; -+ options->gss_keyex = -1; - options->gss_cleanup_creds = -1; - options->gss_strict_acceptor = -1; -+ options->gss_store_rekey = -1; - options->password_authentication = -1; - options->kbd_interactive_authentication = -1; - options->challenge_response_authentication = -1; -@@ -334,10 +336,14 @@ fill_default_server_options(ServerOption - options->kerberos_get_afs_token = 0; - if (options->gss_authentication == -1) - options->gss_authentication = 0; -+ if (options->gss_keyex == -1) -+ options->gss_keyex = 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->gss_store_rekey == -1) -+ options->gss_store_rekey = 0; - if (options->password_authentication == -1) - options->password_authentication = 1; - if (options->kbd_interactive_authentication == -1) -@@ -484,7 +490,7 @@ typedef enum { - sHostKeyAlgorithms, - sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile, - sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor, -- sAcceptEnv, sSetEnv, sPermitTunnel, -+ sGssKeyEx, sGssStoreRekey, sAcceptEnv, sSetEnv, sPermitTunnel, - sMatch, sPermitOpen, sPermitListen, sForceCommand, sChrootDirectory, - sUsePrivilegeSeparation, sAllowAgentForwarding, - sHostCertificate, -@@ -559,11 +565,17 @@ static struct { - { "gssapiauthentication", sGssAuthentication, SSHCFG_ALL }, - { "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_GLOBAL }, - { "gssapistrictacceptorcheck", sGssStrictAcceptor, SSHCFG_GLOBAL }, -+ { "gssapikeyexchange", sGssKeyEx, SSHCFG_GLOBAL }, -+ { "gssapistorecredentialsonrekey", sGssStoreRekey, SSHCFG_GLOBAL }, - #else - { "gssapiauthentication", sUnsupported, SSHCFG_ALL }, - { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL }, - { "gssapistrictacceptorcheck", sUnsupported, SSHCFG_GLOBAL }, -+ { "gssapikeyexchange", sUnsupported, SSHCFG_GLOBAL }, -+ { "gssapistorecredentialsonrekey", sUnsupported, SSHCFG_GLOBAL }, - #endif -+ { "gssusesessionccache", sUnsupported, SSHCFG_GLOBAL }, -+ { "gssapiusesessioncredcache", sUnsupported, SSHCFG_GLOBAL }, - { "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL }, - { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL }, - { "challengeresponseauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL }, -@@ -1463,6 +1475,10 @@ process_server_config_line(ServerOptions - intptr = &options->gss_authentication; - goto parse_flag; - -+ case sGssKeyEx: -+ intptr = &options->gss_keyex; -+ goto parse_flag; -+ - case sGssCleanupCreds: - intptr = &options->gss_cleanup_creds; - goto parse_flag; -@@ -1471,6 +1487,10 @@ process_server_config_line(ServerOptions - intptr = &options->gss_strict_acceptor; - goto parse_flag; - -+ case sGssStoreRekey: -+ intptr = &options->gss_store_rekey; -+ goto parse_flag; -+ - case sPasswordAuthentication: - intptr = &options->password_authentication; - goto parse_flag; -@@ -2560,6 +2580,9 @@ dump_config(ServerOptions *o) - #ifdef GSSAPI - dump_cfg_fmtint(sGssAuthentication, o->gss_authentication); - dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds); -+ dump_cfg_fmtint(sGssKeyEx, o->gss_keyex); -+ dump_cfg_fmtint(sGssStrictAcceptor, o->gss_strict_acceptor); -+ dump_cfg_fmtint(sGssStoreRekey, o->gss_store_rekey); - #endif - dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication); - dump_cfg_fmtint(sKbdInteractiveAuthentication, -diff -up openssh/servconf.h.gsskex openssh/servconf.h ---- openssh/servconf.h.gsskex 2018-08-22 11:47:33.296216335 +0200 -+++ openssh/servconf.h 2018-08-22 11:47:33.316216497 +0200 -@@ -124,8 +124,10 @@ typedef struct { - 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_keyex; /* If true, permit GSSAPI key exchange */ - int gss_cleanup_creds; /* If true, destroy cred cache on logout */ - int gss_strict_acceptor; /* If true, restrict the GSSAPI acceptor name */ -+ int gss_store_rekey; - int password_authentication; /* If true, permit password - * authentication. */ - int kbd_interactive_authentication; /* If true, permit */ -diff -up openssh/ssh_config.5.gsskex openssh/ssh_config.5 ---- openssh/ssh_config.5.gsskex 2018-08-20 07:57:29.000000000 +0200 -+++ openssh/ssh_config.5 2018-08-22 11:47:33.316216497 +0200 -@@ -718,10 +718,40 @@ The default is - Specifies whether user authentication based on GSSAPI is allowed. - The default is - .Cm no . -+.It Cm GSSAPIClientIdentity -+If set, specifies the GSSAPI client identity that ssh should use when -+connecting to the server. The default is unset, which means that the default -+identity will be used. - .It Cm GSSAPIDelegateCredentials - Forward (delegate) credentials to the server. - The default is - .Cm no . -+.It Cm GSSAPIKeyExchange -+Specifies whether key exchange based on GSSAPI may be used. When using -+GSSAPI key exchange the server need not have a host key. -+The default is -+.Dq no . -+.It Cm GSSAPIRenewalForcesRekey -+If set to -+.Dq yes -+then renewal of the client's GSSAPI credentials will force the rekeying of the -+ssh connection. With a compatible server, this can delegate the renewed -+credentials to a session on the server. -+The default is -+.Dq no . -+.It Cm GSSAPIServerIdentity -+If set, specifies the GSSAPI server identity that ssh should expect when -+connecting to the server. The default is unset, which means that the -+expected GSSAPI server identity will be determined from the target -+hostname. -+.It Cm GSSAPITrustDns -+Set to -+.Dq yes to indicate that the DNS is trusted to securely canonicalize -+the name of the host being connected to. If -+.Dq no, the hostname entered on the -+command line will be passed untouched to the GSSAPI library. -+The default is -+.Dq no . - .It Cm HashKnownHosts - Indicates that - .Xr ssh 1 -diff -up openssh/ssh_config.gsskex openssh/ssh_config ---- openssh/ssh_config.gsskex 2018-08-22 11:47:33.289216279 +0200 -+++ openssh/ssh_config 2018-08-22 11:47:33.316216497 +0200 -@@ -24,6 +24,8 @@ - # HostbasedAuthentication no - # GSSAPIAuthentication no - # GSSAPIDelegateCredentials no -+# GSSAPIKeyExchange no -+# GSSAPITrustDNS no - # BatchMode no - # CheckHostIP yes - # AddressFamily any -diff -up openssh/sshconnect2.c.gsskex openssh/sshconnect2.c ---- openssh/sshconnect2.c.gsskex 2018-08-20 07:57:29.000000000 +0200 -+++ openssh/sshconnect2.c 2018-08-22 13:33:01.674275795 +0200 -@@ -82,6 +82,124 @@ extern char *client_version_string; - extern char *server_version_string; - extern Options options; - -+/* XXX from auth.h -- refactoring move these useful functions away of client context*/ -+ -+/* -+ * Returns the remote DNS hostname as a string. The returned string must not -+ * be freed. NB. this will usually trigger a DNS query the first time it is -+ * called. -+ * This function does additional checks on the hostname to mitigate some -+ * attacks on legacy rhosts-style authentication. -+ * XXX is RhostsRSAAuthentication vulnerable to these? -+ * XXX Can we remove these checks? (or if not, remove RhostsRSAAuthentication?) -+ */ -+ -+static char * -+remote_hostname(struct ssh *ssh) -+{ -+ struct sockaddr_storage from; -+ socklen_t fromlen; -+ struct addrinfo hints, *ai, *aitop; -+ char name[NI_MAXHOST], ntop2[NI_MAXHOST]; -+ const char *ntop = ssh_remote_ipaddr(ssh); -+ -+ /* Get IP address of client. */ -+ fromlen = sizeof(from); -+ memset(&from, 0, sizeof(from)); -+ if (getpeername(ssh_packet_get_connection_in(ssh), -+ (struct sockaddr *)&from, &fromlen) < 0) { -+ debug("getpeername failed: %.100s", strerror(errno)); -+ return strdup(ntop); -+ } -+ -+ ipv64_normalise_mapped(&from, &fromlen); -+ if (from.ss_family == AF_INET6) -+ fromlen = sizeof(struct sockaddr_in6); -+ -+ debug3("Trying to reverse map address %.100s.", ntop); -+ /* Map the IP address to a host name. */ -+ if (getnameinfo((struct sockaddr *)&from, fromlen, name, sizeof(name), -+ NULL, 0, NI_NAMEREQD) != 0) { -+ /* Host name not found. Use ip address. */ -+ return strdup(ntop); -+ } -+ -+ /* -+ * if reverse lookup result looks like a numeric hostname, -+ * someone is trying to trick us by PTR record like following: -+ * 1.1.1.10.in-addr.arpa. IN PTR 2.3.4.5 -+ */ -+ memset(&hints, 0, sizeof(hints)); -+ hints.ai_socktype = SOCK_DGRAM; /*dummy*/ -+ hints.ai_flags = AI_NUMERICHOST; -+ if (getaddrinfo(name, NULL, &hints, &ai) == 0) { -+ logit("Nasty PTR record \"%s\" is set up for %s, ignoring", -+ name, ntop); -+ freeaddrinfo(ai); -+ return strdup(ntop); -+ } -+ -+ /* Names are stored in lowercase. */ -+ lowercase(name); -+ -+ /* -+ * Map it back to an IP address and check that the given -+ * address actually is an address of this host. This is -+ * necessary because anyone with access to a name server can -+ * define arbitrary names for an IP address. Mapping from -+ * name to IP address can be trusted better (but can still be -+ * fooled if the intruder has access to the name server of -+ * the domain). -+ */ -+ memset(&hints, 0, sizeof(hints)); -+ hints.ai_family = from.ss_family; -+ hints.ai_socktype = SOCK_STREAM; -+ if (getaddrinfo(name, NULL, &hints, &aitop) != 0) { -+ logit("reverse mapping checking getaddrinfo for %.700s " -+ "[%s] failed.", name, ntop); -+ return strdup(ntop); -+ } -+ /* Look for the address from the list of addresses. */ -+ for (ai = aitop; ai; ai = ai->ai_next) { -+ if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop2, -+ sizeof(ntop2), NULL, 0, NI_NUMERICHOST) == 0 && -+ (strcmp(ntop, ntop2) == 0)) -+ break; -+ } -+ freeaddrinfo(aitop); -+ /* If we reached the end of the list, the address was not there. */ -+ if (ai == NULL) { -+ /* Address not found for the host name. */ -+ logit("Address %.100s maps to %.600s, but this does not " -+ "map back to the address.", ntop, name); -+ return strdup(ntop); -+ } -+ return strdup(name); -+} -+ -+/* -+ * Return the canonical name of the host in the other side of the current -+ * connection. The host name is cached, so it is efficient to call this -+ * several times. -+ */ -+ -+const char * -+get_canonical_hostname(struct ssh *ssh, int use_dns) -+{ -+ static char *dnsname; -+ -+ if (!use_dns) -+ return ssh_remote_ipaddr(ssh); -+ else if (dnsname != NULL) -+ return dnsname; -+ else { -+ dnsname = remote_hostname(ssh); -+ return dnsname; -+ } -+} -+ -+ -+ - /* - * SSH2 key exchange - */ -@@ -162,9 +280,34 @@ ssh_kex2(char *host, struct sockaddr *ho - struct kex *kex; - int r; - -+#ifdef GSSAPI -+ char *orig = NULL, *gss = NULL; -+ char *gss_host = NULL; -+#endif -+ - xxx_host = host; - xxx_hostaddr = hostaddr; - -+#ifdef GSSAPI -+ if (options.gss_keyex) { -+ /* Add the GSSAPI mechanisms currently supported on this -+ * client to the key exchange algorithm proposal */ -+ orig = options.kex_algorithms; -+ -+ if (options.gss_trust_dns) -+ gss_host = (char *)get_canonical_hostname(active_state, 1); -+ else -+ gss_host = host; -+ -+ gss = ssh_gssapi_client_mechanisms(gss_host, options.gss_client_identity); -+ if (gss) { -+ debug("Offering GSSAPI proposal: %s", gss); -+ xasprintf(&options.kex_algorithms, -+ "%s,%s", gss, orig); -+ } -+ } -+#endif -+ - if ((s = kex_names_cat(options.kex_algorithms, "ext-info-c")) == NULL) - fatal("%s: kex_names_cat", __func__); - myproposal[PROPOSAL_KEX_ALGS] = compat_kex_proposal(s); -@@ -194,6 +337,17 @@ ssh_kex2(char *host, struct sockaddr *ho - order_hostkeyalgs(host, hostaddr, port)); - } - -+#ifdef GSSAPI -+ /* If we've got GSSAPI algorithms, then we also support the -+ * 'null' hostkey, as a last resort */ -+ if (options.gss_keyex && gss) { -+ orig = myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]; -+ xasprintf(&myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS], -+ "%s,null", orig); -+ free(gss); -+ } -+#endif -+ - if (options.rekey_limit || options.rekey_interval) - packet_set_rekey_limits(options.rekey_limit, - options.rekey_interval); -@@ -214,11 +368,31 @@ ssh_kex2(char *host, struct sockaddr *ho - kex->kex[KEX_ECDH_SHA2] = kexecdh_client; - # endif - #endif -+#ifdef GSSAPI -+ if (options.gss_keyex) { -+ kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_client; -+ kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_client; -+ kex->kex[KEX_GSS_GEX_SHA1] = kexgss_client; -+ } -+#endif - kex->kex[KEX_C25519_SHA256] = kexc25519_client; - kex->client_version_string=client_version_string; - kex->server_version_string=server_version_string; - kex->verify_host_key=&verify_host_key_callback; - -+#ifdef GSSAPI -+ if (options.gss_keyex) { -+ kex->gss_deleg_creds = options.gss_deleg_creds; -+ kex->gss_trust_dns = options.gss_trust_dns; -+ kex->gss_client = options.gss_client_identity; -+ if (options.gss_server_identity) { -+ kex->gss_host = options.gss_server_identity; -+ } else { -+ kex->gss_host = gss_host; -+ } -+ } -+#endif -+ - ssh_dispatch_run_fatal(active_state, DISPATCH_BLOCK, &kex->done); - - /* remove ext-info from the KEX proposals for rekeying */ -@@ -314,6 +488,7 @@ int input_gssapi_token(int type, u_int32 - int input_gssapi_hash(int type, u_int32_t, struct ssh *); - int input_gssapi_error(int, u_int32_t, struct ssh *); - int input_gssapi_errtok(int, u_int32_t, struct ssh *); -+int userauth_gsskeyex(Authctxt *authctxt); - #endif - - void userauth(Authctxt *, char *); -@@ -330,6 +505,11 @@ static char *authmethods_get(void); - - Authmethod authmethods[] = { - #ifdef GSSAPI -+ {"gssapi-keyex", -+ userauth_gsskeyex, -+ NULL, -+ &options.gss_authentication, -+ NULL}, - {"gssapi-with-mic", - userauth_gssapi, - NULL, -@@ -657,19 +837,31 @@ userauth_gssapi(Authctxt *authctxt) - static u_int mech = 0; - OM_uint32 min; - int r, ok = 0; -+ const char *gss_host; -+ -+ if (options.gss_server_identity) -+ gss_host = options.gss_server_identity; -+ else if (options.gss_trust_dns) -+ gss_host = get_canonical_hostname(active_state, 1); -+ else -+ gss_host = authctxt->host; - - /* Try one GSSAPI method at a time, rather than sending them all at - * once. */ - - if (gss_supported == NULL) -- gss_indicate_mechs(&min, &gss_supported); -+ if (GSS_ERROR(gss_indicate_mechs(&min, &gss_supported))) { -+ gss_supported = NULL; -+ return 0; -+ } - - /* Check to see if the mechanism is usable before we offer it */ - while (mech < gss_supported->count && !ok) { - /* My DER encoding requires length<128 */ - if (gss_supported->elements[mech].length < 128 && - ssh_gssapi_check_mechanism(&gssctxt, -- &gss_supported->elements[mech], authctxt->host)) { -+ &gss_supported->elements[mech], gss_host, -+ options.gss_client_identity)) { - ok = 1; /* Mechanism works */ - } else { - mech++; -@@ -906,6 +1098,51 @@ input_gssapi_error(int type, u_int32_t p - free(lang); - return r; - } -+ -+int -+userauth_gsskeyex(Authctxt *authctxt) -+{ -+ struct sshbuf *b = NULL; -+ gss_buffer_desc gssbuf; -+ gss_buffer_desc mic = GSS_C_EMPTY_BUFFER; -+ OM_uint32 ms; -+ -+ static int attempt = 0; -+ if (attempt++ >= 1) -+ return (0); -+ -+ if (gss_kex_context == NULL) { -+ debug("No valid Key exchange context"); -+ return (0); -+ } -+ -+ if ((b = sshbuf_new()) == NULL) -+ fatal("%s: sshbuf_new failed", __func__); -+ -+ ssh_gssapi_buildmic(b, authctxt->server_user, authctxt->service, -+ "gssapi-keyex"); -+ -+ gssbuf.value = sshbuf_mutable_ptr(b); -+ gssbuf.length = sshbuf_len(b); -+ -+ if (GSS_ERROR(ssh_gssapi_sign(gss_kex_context, &gssbuf, &mic))) { -+ sshbuf_free(b); -+ return (0); -+ } -+ -+ packet_start(SSH2_MSG_USERAUTH_REQUEST); -+ packet_put_cstring(authctxt->server_user); -+ packet_put_cstring(authctxt->service); -+ packet_put_cstring(authctxt->method->name); -+ packet_put_string(mic.value, mic.length); -+ packet_send(); -+ -+ sshbuf_free(b); -+ gss_release_buffer(&ms, &mic); -+ -+ return (1); -+} -+ - #endif /* GSSAPI */ - - int -diff -up openssh/sshd.c.gsskex openssh/sshd.c ---- openssh/sshd.c.gsskex 2018-08-22 11:47:33.299216360 +0200 -+++ openssh/sshd.c 2018-08-22 13:34:28.455975954 +0200 -@@ -537,8 +537,7 @@ privsep_preauth_child(void) - - #ifdef GSSAPI - /* Cache supported mechanism OIDs for later use */ -- if (options.gss_authentication) -- ssh_gssapi_prepare_supported_oids(); -+ ssh_gssapi_prepare_supported_oids(); - #endif - - reseed_prngs(); -@@ -887,8 +887,9 @@ notify_hostkeys(struct ssh *ssh) - } - debug3("%s: sent %u hostkeys", __func__, nkeys); - if (nkeys == 0) -- fatal("%s: no hostkeys", __func__); -- packet_send(); -+ debug3("%s: no hostkeys", __func__); -+ else -+ packet_send(); - sshbuf_free(buf); - } - -@@ -1841,7 +1842,8 @@ main(int ac, char **av) - free(fp); - } - accumulate_host_timing_secret(cfg, NULL); -- if (!sensitive_data.have_ssh2_key) { -+ /* The GSSAPI key exchange can run without a host key */ -+ if (!sensitive_data.have_ssh2_key && !options.gss_keyex) { - logit("sshd: no hostkeys available -- exiting."); - exit(1); - } -@@ -2321,6 +2323,48 @@ do_ssh2_kex(void) - myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = compat_pkalg_proposal( - list_hostkey_types()); - -+#ifdef GSSAPI -+ { -+ char *orig; -+ char *gss = NULL; -+ char *newstr = NULL; -+ orig = myproposal[PROPOSAL_KEX_ALGS]; -+ -+ /* -+ * If we don't have a host key, then there's no point advertising -+ * the other key exchange algorithms -+ */ -+ -+ if (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]) == 0) -+ orig = NULL; -+ -+ if (options.gss_keyex) -+ gss = ssh_gssapi_server_mechanisms(); -+ else -+ gss = NULL; -+ -+ if (gss && orig) -+ xasprintf(&newstr, "%s,%s", gss, orig); -+ else if (gss) -+ newstr = gss; -+ else if (orig) -+ newstr = orig; -+ -+ /* -+ * If we've got GSSAPI mechanisms, then we've got the 'null' host -+ * key alg, but we can't tell people about it unless its the only -+ * host key algorithm we support -+ */ -+ if (gss && (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS])) == 0) -+ myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = "null"; -+ -+ if (newstr) -+ myproposal[PROPOSAL_KEX_ALGS] = newstr; -+ else -+ fatal("No supported key exchange algorithms"); -+ } -+#endif -+ - /* start key exchange */ - if ((r = kex_setup(active_state, myproposal)) != 0) - fatal("kex_setup: %s", ssh_err(r)); -@@ -2338,6 +2382,13 @@ do_ssh2_kex(void) - # endif - #endif - kex->kex[KEX_C25519_SHA256] = kexc25519_server; -+#ifdef GSSAPI -+ if (options.gss_keyex) { -+ kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server; -+ kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server; -+ kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server; -+ } -+#endif - kex->server = 1; - kex->client_version_string=client_version_string; - kex->server_version_string=server_version_string; -diff -up openssh/sshd_config.5.gsskex openssh/sshd_config.5 ---- openssh/sshd_config.5.gsskex 2018-08-22 11:47:33.297216344 +0200 -+++ openssh/sshd_config.5 2018-08-22 13:35:05.531275099 +0200 -@@ -642,6 +642,11 @@ Specifies whether to automatically destr - on logout. - The default is - .Cm yes . -+.It Cm GSSAPIKeyExchange -+Specifies whether key exchange based on GSSAPI is allowed. GSSAPI key exchange -+doesn't rely on ssh keys to verify host identity. -+The default is -+.Dq no . - .It Cm GSSAPIStrictAcceptorCheck - Determines whether to be strict about the identity of the GSSAPI acceptor - a client authenticates against. -@@ -656,6 +661,11 @@ machine's default store. - This facility is provided to assist with operation on multi homed machines. - The default is - .Cm yes . -+.It Cm GSSAPIStoreCredentialsOnRekey -+Controls whether the user's GSSAPI credentials should be updated following a -+successful connection rekeying. This option can be used to accepted renewed -+or updated credentials from a compatible client. The default is -+.Dq no . - .It Cm HostbasedAcceptedKeyTypes - Specifies the key types that will be accepted for hostbased authentication - as a list of comma-separated patterns. -diff -up openssh/sshd_config.gsskex openssh/sshd_config ---- openssh/sshd_config.gsskex 2018-08-22 11:47:33.299216360 +0200 -+++ openssh/sshd_config 2018-08-22 11:47:33.318216513 +0200 -@@ -85,6 +85,8 @@ ChallengeResponseAuthentication no - # GSSAPI options - GSSAPIAuthentication yes - GSSAPICleanupCredentials no -+#GSSAPIStrictAcceptorCheck yes -+#GSSAPIKeyExchange no - - # Set this to 'yes' to enable PAM authentication, account processing, - # and session processing. If this is enabled, PAM authentication will -diff -up openssh/ssh-gss.h.gsskex openssh/ssh-gss.h ---- openssh/ssh-gss.h.gsskex 2018-08-20 07:57:29.000000000 +0200 -+++ openssh/ssh-gss.h 2018-08-22 13:36:44.773075793 +0200 -@@ -1,6 +1,6 @@ - /* $OpenBSD: ssh-gss.h,v 1.14 2018/07/10 09:13:30 djm Exp $ */ - /* -- * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. -+ * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions -@@ -61,10 +61,22 @@ - - #define SSH_GSS_OIDTYPE 0x06 - -+#define SSH2_MSG_KEXGSS_INIT 30 -+#define SSH2_MSG_KEXGSS_CONTINUE 31 -+#define SSH2_MSG_KEXGSS_COMPLETE 32 -+#define SSH2_MSG_KEXGSS_HOSTKEY 33 -+#define SSH2_MSG_KEXGSS_ERROR 34 -+#define SSH2_MSG_KEXGSS_GROUPREQ 40 -+#define SSH2_MSG_KEXGSS_GROUP 41 -+#define KEX_GSS_GRP1_SHA1_ID "gss-group1-sha1-" -+#define KEX_GSS_GRP14_SHA1_ID "gss-group14-sha1-" -+#define KEX_GSS_GEX_SHA1_ID "gss-gex-sha1-" -+ - typedef struct { - char *filename; - char *envvar; - char *envval; -+ struct passwd *owner; - void *data; - } ssh_gssapi_ccache; - -@@ -72,8 +84,11 @@ typedef struct { - gss_buffer_desc displayname; - gss_buffer_desc exportedname; - gss_cred_id_t creds; -+ gss_name_t name; - struct ssh_gssapi_mech_struct *mech; - ssh_gssapi_ccache store; -+ int used; -+ int updated; - } ssh_gssapi_client; - - typedef struct ssh_gssapi_mech_struct { -@@ -84,6 +99,7 @@ typedef struct ssh_gssapi_mech_struct { - int (*userok) (ssh_gssapi_client *, char *); - int (*localname) (ssh_gssapi_client *, char **); - void (*storecreds) (ssh_gssapi_client *); -+ int (*updatecreds) (ssh_gssapi_ccache *, ssh_gssapi_client *); - } ssh_gssapi_mech; - - typedef struct { -@@ -94,10 +110,11 @@ typedef struct { - gss_OID oid; /* client */ - gss_cred_id_t creds; /* server */ - gss_name_t client; /* server */ -- gss_cred_id_t client_creds; /* server */ -+ gss_cred_id_t client_creds; /* both */ - } Gssctxt; - - extern ssh_gssapi_mech *supported_mechs[]; -+extern Gssctxt *gss_kex_context; - - int ssh_gssapi_check_oid(Gssctxt *, void *, size_t); - void ssh_gssapi_set_oid_data(Gssctxt *, void *, size_t); -@@ -123,17 +140,33 @@ void ssh_gssapi_delete_ctx(Gssctxt **); - OM_uint32 ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t); - void ssh_gssapi_buildmic(struct sshbuf *, const char *, - const char *, const char *); --int ssh_gssapi_check_mechanism(Gssctxt **, gss_OID, const char *); -+int ssh_gssapi_check_mechanism(Gssctxt **, gss_OID, const char *, const char *); -+OM_uint32 ssh_gssapi_client_identity(Gssctxt *, const char *); -+int ssh_gssapi_credentials_updated(Gssctxt *); - - /* In the server */ -+typedef int ssh_gssapi_check_fn(Gssctxt **, gss_OID, const char *, -+ const char *); -+char *ssh_gssapi_client_mechanisms(const char *, const char *); -+char *ssh_gssapi_kex_mechs(gss_OID_set, ssh_gssapi_check_fn *, const char *, -+ const char *); -+gss_OID ssh_gssapi_id_kex(Gssctxt *, char *, int); -+int ssh_gssapi_server_check_mech(Gssctxt **,gss_OID, const char *, -+ const char *); - OM_uint32 ssh_gssapi_server_ctx(Gssctxt **, gss_OID); --int ssh_gssapi_userok(char *name); -+int ssh_gssapi_userok(char *name, struct passwd *); - OM_uint32 ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t); - void ssh_gssapi_do_child(char ***, u_int *); - void ssh_gssapi_cleanup_creds(void); - void ssh_gssapi_storecreds(void); - const char *ssh_gssapi_displayname(void); - -+char *ssh_gssapi_server_mechanisms(void); -+int ssh_gssapi_oid_table_ok(); -+ -+int ssh_gssapi_update_creds(ssh_gssapi_ccache *store); -+ -+void ssh_gssapi_rekey_creds(void); - #endif /* GSSAPI */ - - #endif /* _SSH_GSS_H */ -diff -up openssh/sshkey.c.gsskex openssh/sshkey.c ---- openssh/sshkey.c.gsskex 2018-08-22 11:47:33.319216521 +0200 -+++ openssh/sshkey.c 2018-08-22 13:37:18.979351804 +0200 -@@ -140,6 +140,7 @@ static const struct keytype keytypes[] = - # endif /* OPENSSL_HAS_NISTP521 */ - # endif /* OPENSSL_HAS_ECC */ - #endif /* WITH_OPENSSL */ -+ { "null", "null", NULL, KEY_NULL, 0, 0, 1 }, - { NULL, NULL, NULL, -1, -1, 0, 0 } - }; - -diff -up openssh/sshkey.h.gsskex openssh/sshkey.h ---- openssh/sshkey.h.gsskex 2018-08-20 07:57:29.000000000 +0200 -+++ openssh/sshkey.h 2018-08-22 11:47:33.320216529 +0200 -@@ -63,6 +63,7 @@ enum sshkey_types { - KEY_ED25519_CERT, - KEY_XMSS, - KEY_XMSS_CERT, -+ KEY_NULL, - KEY_UNSPEC - }; - ---- openssh/sshconnect2.c.orig 2017-01-04 19:47:10.000000000 +0100 -+++ openssh/sshconnect2.c 2017-01-05 04:13:08.977425272 +0100 -@@ -344,7 +344,6 @@ - orig = myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]; - xasprintf(&myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS], - "%s,null", orig); -- free(gss); - } - #endif - -@@ -398,6 +397,16 @@ - /* remove ext-info from the KEX proposals for rekeying */ - myproposal[PROPOSAL_KEX_ALGS] = - compat_kex_proposal(options.kex_algorithms); -+#ifdef GSSAPI -+ /* repair myproposal after it was crumpled by the */ -+ /* ext-info removal above */ -+ if (gss) { -+ orig = myproposal[PROPOSAL_KEX_ALGS]; -+ xasprintf(&myproposal[PROPOSAL_KEX_ALGS], -+ "%s,%s", gss, orig); -+ free(gss); -+ } -+#endif - if ((r = kex_prop2buf(kex->my, myproposal)) != 0) - fatal("kex_prop2buf: %s", ssh_err(r)); - diff --git a/openssh-7.8p1-ip-port-config-parser.patch b/openssh-7.8p1-ip-port-config-parser.patch deleted file mode 100644 index 6ca0cf5..0000000 --- a/openssh-7.8p1-ip-port-config-parser.patch +++ /dev/null @@ -1,72 +0,0 @@ -diff -up openssh/misc.c.config openssh/misc.c ---- openssh/misc.c.config 2018-08-22 13:58:54.922807799 +0200 -+++ openssh/misc.c 2018-08-22 13:58:55.000808428 +0200 -@@ -485,7 +485,7 @@ put_host_port(const char *host, u_short - * The delimiter char, if present, is stored in delim. - * If this is the last field, *cp is set to NULL. - */ --static char * -+char * - hpdelim2(char **cp, char *delim) - { - char *s, *old; -diff -up openssh/misc.h.config openssh/misc.h ---- openssh/misc.h.config 2018-08-20 07:57:29.000000000 +0200 -+++ openssh/misc.h 2018-08-22 13:58:55.001808436 +0200 -@@ -54,6 +54,7 @@ int set_rdomain(int, const char *); - 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 *); -diff -up openssh/servconf.c.config openssh/servconf.c ---- openssh/servconf.c.config 2018-08-22 13:58:54.989808340 +0200 -+++ openssh/servconf.c 2018-08-22 14:18:49.235443937 +0200 -@@ -886,7 +886,7 @@ process_permitopen_list(struct ssh *ssh, - { - u_int i; - int port; -- char *host, *arg, *oarg; -+ char *host, *arg, *oarg, ch; - int where = opcode == sPermitOpen ? FORWARD_LOCAL : FORWARD_REMOTE; - const char *what = lookup_opcode_name(opcode); - -@@ -904,8 +904,8 @@ process_permitopen_list(struct ssh *ssh, - /* 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) -+ host = hpdelim2(&arg, &ch); -+ if (host == NULL || ch == '/') - fatal("%s: missing host in %s", __func__, what); - host = cleanhostname(host); - if (arg == NULL || ((port = permitopen_port(arg)) < 0)) -@@ -1323,8 +1323,10 @@ process_server_config_line(ServerOptions - port = 0; - p = arg; - } else { -- p = hpdelim(&arg); -- if (p == NULL) -+ char ch; -+ arg2 = NULL; -+ p = hpdelim2(&arg, &ch); -+ if (p == NULL || ch == '/') - fatal("%s line %d: bad address:port usage", - filename, linenum); - p = cleanhostname(p); -@@ -1965,9 +1967,10 @@ process_server_config_line(ServerOptions - */ - xasprintf(&arg2, "*:%s", arg); - } else { -+ char ch; - arg2 = xstrdup(arg); -- p = hpdelim(&arg); -- if (p == NULL) { -+ p = hpdelim2(&arg, &ch); -+ if (p == NULL || ch == '/') { - fatal("%s line %d: missing host in %s", - filename, linenum, - lookup_opcode_name(opcode)); diff --git a/openssh-7.8p1-role-mls.patch b/openssh-7.8p1-role-mls.patch index 4c58d71..c3bec2a 100644 --- a/openssh-7.8p1-role-mls.patch +++ b/openssh-7.8p1-role-mls.patch @@ -4,11 +4,11 @@ diff -up openssh/auth2.c.role-mls openssh/auth2.c @@ -256,6 +256,9 @@ input_userauth_request(int type, u_int32 Authctxt *authctxt = ssh->authctxt; Authmethod *m = NULL; - char *user, *service, *method, *style = NULL; + char *user = NULL, *service = NULL, *method = NULL, *style = NULL; +#ifdef WITH_SELINUX + char *role = NULL; +#endif - int authenticated = 0; + int r, authenticated = 0; double tstart = monotime_double(); @@ -268,6 +271,11 @@ input_userauth_request(int type, u_int32 @@ -37,9 +37,9 @@ diff -up openssh/auth2.c.role-mls openssh/auth2.c + mm_inform_authrole(role); +#endif + } - userauth_banner(); + userauth_banner(ssh); if (auth2_setup_methods_lists(authctxt) != 0) - packet_disconnect("no authentication methods enabled"); + ssh_packet_disconnect(ssh, diff -up openssh/auth2-gss.c.role-mls openssh/auth2-gss.c --- openssh/auth2-gss.c.role-mls 2018-08-20 07:57:29.000000000 +0200 +++ openssh/auth2-gss.c 2018-08-22 11:15:42.459799171 +0200 @@ -57,7 +57,7 @@ diff -up openssh/auth2-gss.c.role-mls openssh/auth2-gss.c mic.length = len; - ssh_gssapi_buildmic(b, authctxt->user, authctxt->service, +#ifdef WITH_SELINUX -+ if (authctxt->role && (strlen(authctxt->role) > 0)) ++ if (authctxt->role && authctxt->role[0] != 0) + xasprintf(&micuser, "%s/%s", authctxt->user, authctxt->role); + else +#endif @@ -197,15 +197,15 @@ diff -up openssh/monitor.c.role-mls openssh/monitor.c --- openssh/monitor.c.role-mls 2018-08-20 07:57:29.000000000 +0200 +++ openssh/monitor.c 2018-08-22 11:19:56.006844867 +0200 @@ -115,6 +115,9 @@ int mm_answer_sign(int, struct sshbuf *) - int mm_answer_pwnamallow(int, struct sshbuf *); - int mm_answer_auth2_read_banner(int, struct sshbuf *); - int mm_answer_authserv(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 *); +#ifdef WITH_SELINUX -+int mm_answer_authrole(int, struct sshbuf *); ++int mm_answer_authrole(struct ssh *, int, struct sshbuf *); +#endif - int mm_answer_authpassword(int, struct sshbuf *); - int mm_answer_bsdauthquery(int, struct sshbuf *); - int mm_answer_bsdauthrespond(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 *); @@ -189,6 +192,9 @@ struct mon_table mon_dispatch_proto20[] {MONITOR_REQ_SIGN, MON_ONCE, mm_answer_sign}, {MONITOR_REQ_PWNAM, MON_ONCE, mm_answer_pwnamallow}, @@ -227,12 +227,12 @@ diff -up openssh/monitor.c.role-mls openssh/monitor.c #ifdef USE_PAM @@ -842,6 +851,26 @@ mm_answer_authserv(int sock, struct sshb - return (0); + return found; } +#ifdef WITH_SELINUX +int -+mm_answer_authrole(int sock, struct sshbuf *m) ++mm_answer_authrole(struct ssh *ssh, int sock, struct sshbuf *m) +{ + int r; + monitor_permit_authentications(1); @@ -251,7 +251,7 @@ diff -up openssh/monitor.c.role-mls openssh/monitor.c +#endif + int - mm_answer_authpassword(int sock, struct sshbuf *m) + mm_answer_authpassword(struct ssh *ssh, int sock, struct sshbuf *m) { @@ -1218,7 +1247,7 @@ monitor_valid_userblob(u_char *data, u_i { @@ -284,7 +284,7 @@ diff -up openssh/monitor.c.role-mls openssh/monitor.c fail++; if ((r = sshbuf_get_cstring(b, &cp, NULL)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); -+ if ((s = strchr(p, '/')) != NULL) ++ if ((s = strchr(cp, '/')) != NULL) + *s = '\0'; xasprintf(&userstyle, "%s%s%s", authctxt->user, authctxt->style ? ":" : "", @@ -338,13 +338,13 @@ diff -up openssh/monitor_wrap.h.role-mls openssh/monitor_wrap.h --- openssh/monitor_wrap.h.role-mls 2018-08-22 11:14:56.818430941 +0200 +++ openssh/monitor_wrap.h 2018-08-22 11:22:10.439929513 +0200 @@ -44,6 +44,9 @@ DH *mm_choose_dh(int, int, int); - int mm_sshkey_sign(struct sshkey *, u_char **, size_t *, const u_char *, size_t, - const char *, u_int compat); + int mm_sshkey_sign(struct ssh *, struct sshkey *, u_char **, size_t *, + const u_char *, size_t, const char *, u_int compat); void mm_inform_authserv(char *, char *); +#ifdef WITH_SELINUX +void mm_inform_authrole(char *); +#endif - struct passwd *mm_getpwnamallow(const char *); + struct passwd *mm_getpwnamallow(struct ssh *, const char *); char *mm_auth2_read_banner(void); int mm_auth_password(struct ssh *, char *); diff -up openssh/openbsd-compat/Makefile.in.role-mls openssh/openbsd-compat/Makefile.in diff --git a/openssh-7.9p1-ssh-copy-id.patch b/openssh-7.9p1-ssh-copy-id.patch new file mode 100644 index 0000000..6ff2b25 --- /dev/null +++ b/openssh-7.9p1-ssh-copy-id.patch @@ -0,0 +1,42 @@ +diff -up openssh-7.9p1/contrib/ssh-copy-id.ssh-copy-id openssh-7.9p1/contrib/ssh-copy-id +--- openssh-7.9p1/contrib/ssh-copy-id.ssh-copy-id 2018-10-17 02:01:20.000000000 +0200 ++++ openssh-7.9p1/contrib/ssh-copy-id 2019-01-23 20:49:30.513393667 +0100 +@@ -112,7 +112,8 @@ do + usage + } + +- OPT= OPTARG= ++ OPT= ++ OPTARG= + # implement something like getopt to avoid Solaris pain + case "$1" in + -i?*|-o?*|-p?*) +@@ -185,8 +185,8 @@ + usage + fi + +-# drop trailing colon +-USER_HOST=$(printf "%s\n" "$1" | sed 's/:$//') ++# don't drop trailing colon because it can be a valid ipv6 address ++USER_HOST=$(printf "%s\n" "$1") + # tack the hostname onto SSH_OPTS + SSH_OPTS="${SSH_OPTS:+$SSH_OPTS }'$(quote "$USER_HOST")'" + # and populate "$@" for later use (only way to get proper quoting of options) +@@ -261,7 +262,7 @@ populate_new_ids() { + fi + if [ -z "$NEW_IDS" ] ; then + printf '\n%s: WARNING: All keys were skipped because they already exist on the remote system.\n' "$0" >&2 +- printf '\t\t(if you think this is a mistake, you may want to use -f option)\n\n' "$0" >&2 ++ printf '\t\t(if you think this is a mistake, you may want to use -f option)\n\n' >&2 + exit 0 + fi + printf '%s: INFO: %d key(s) remain to be installed -- if you are prompted now it is to install the new keys\n' "$0" "$(printf '%s\n' "$NEW_IDS" | wc -l)" >&2 +@@ -296,7 +297,7 @@ case "$REMOTE_VERSION" in + # in ssh below - to defend against quirky remote shells: use 'exec sh -c' to get POSIX; + # 'cd' to be at $HOME; add a newline if it's missing; and all on one line, because tcsh. + [ "$DRY_RUN" ] || printf '%s\n' "$NEW_IDS" | \ +- ssh "$@" "exec sh -c 'cd ; umask 077 ; mkdir -p .ssh && { [ -z "'`tail -1c .ssh/authorized_keys 2>/dev/null`'" ] || echo >> .ssh/authorized_keys ; } && cat >> .ssh/authorized_keys || exit 1 ; if type restorecon >/dev/null 2>&1 ; then restorecon -F .ssh .ssh/authorized_keys ; fi'" \ ++ ssh "$@" "exec sh -c 'cd ; umask 077 ; mkdir -p .ssh && { [ -z "'`tail -1c .ssh/authorized_keys 2>/dev/null`'" ] || echo >> .ssh/authorized_keys || exit 1; } && cat >> .ssh/authorized_keys || exit 1 ; if type restorecon >/dev/null 2>&1 ; then restorecon -F .ssh .ssh/authorized_keys ; fi'" \ + || exit 1 + ADDED=$(printf '%s\n' "$NEW_IDS" | wc -l) + ;; diff --git a/openssh-8.0p1-avoidkillall.patch b/openssh-8.0p1-avoidkillall.patch new file mode 100644 index 0000000..77331e8 --- /dev/null +++ b/openssh-8.0p1-avoidkillall.patch @@ -0,0 +1,20 @@ +diff --git a/sftp.c b/sftp.c +index b66037f1..54538ff9 100644 +--- a/sftp.c ++++ b/sftp.c +@@ -220,9 +220,12 @@ static const struct CMD cmds[] = { + static void + killchild(int signo) + { +- if (sshpid > 1) { +- kill(sshpid, SIGTERM); +- waitpid(sshpid, NULL, 0); ++ pid_t pid; ++ ++ pid = sshpid; ++ if (pid > 1) { ++ kill(pid, SIGTERM); ++ (void)waitpid(pid, NULL, 0); + } + + _exit(1); diff --git a/openssh-8.0p1-bigsshdconfig.patch b/openssh-8.0p1-bigsshdconfig.patch new file mode 100644 index 0000000..2158ffe --- /dev/null +++ b/openssh-8.0p1-bigsshdconfig.patch @@ -0,0 +1,13 @@ +diff --git a/msg.c b/msg.c +index 99c25cd2..574a566e 100644 +--- a/msg.c ++++ b/msg.c +@@ -77,7 +77,7 @@ ssh_msg_recv(int fd, struct sshbuf *m) + return (-1); + } + msg_len = get_u32(buf); +- if (msg_len > 256 * 1024) { ++ if (msg_len > sshbuf_max_size(m)) { + error("ssh_msg_recv: read: bad msg_len %u", msg_len); + return (-1); + } diff --git a/openssh-8.0p1-channel-limits.patch b/openssh-8.0p1-channel-limits.patch new file mode 100644 index 0000000..47e1f87 --- /dev/null +++ b/openssh-8.0p1-channel-limits.patch @@ -0,0 +1,33 @@ +diff -up openssh-8.0p1/channels.c.channel-limits openssh-8.0p1/channels.c +--- openssh-8.0p1/channels.c.channel-limits 2021-03-16 12:17:58.905576511 +0100 ++++ openssh-8.0p1/channels.c 2021-03-16 12:17:58.925576667 +0100 +@@ -354,6 +354,7 @@ channel_new(struct ssh *ssh, char *ctype + struct ssh_channels *sc = ssh->chanctxt; + u_int i, found; + Channel *c; ++ int r; + + /* Try to find a free slot where to put the new channel. */ + for (i = 0; i < sc->channels_alloc; i++) { +@@ -383,6 +384,8 @@ channel_new(struct ssh *ssh, char *ctype + (c->output = sshbuf_new()) == NULL || + (c->extended = sshbuf_new()) == NULL) + fatal("%s: sshbuf_new failed", __func__); ++ if ((r = sshbuf_set_max_size(c->input, CHAN_INPUT_MAX)) != 0) ++ fatal("%s: sshbuf_set_max_size: %s", __func__, ssh_err(r)); + c->ostate = CHAN_OUTPUT_OPEN; + c->istate = CHAN_INPUT_OPEN; + channel_register_fds(ssh, c, rfd, wfd, efd, extusage, nonblock, 0); +diff -up openssh-8.0p1/channels.h.channel-limits openssh-8.0p1/channels.h +--- openssh-8.0p1/channels.h.channel-limits 2021-03-16 12:17:58.868576223 +0100 ++++ openssh-8.0p1/channels.h 2021-03-16 12:17:58.907576527 +0100 +@@ -215,6 +215,9 @@ struct Channel { + /* Read buffer size */ + #define CHAN_RBUF (16*1024) + ++/* Maximum channel input buffer size */ ++#define CHAN_INPUT_MAX (16*1024*1024) ++ + /* Hard limit on number of channels */ + #define CHANNELS_MAX_CHANNELS (16*1024) + diff --git a/openssh-8.0p1-client_alive_count_max.patch b/openssh-8.0p1-client_alive_count_max.patch new file mode 100644 index 0000000..e0f272f --- /dev/null +++ b/openssh-8.0p1-client_alive_count_max.patch @@ -0,0 +1,28 @@ +diff --git a/serverloop.c b/serverloop.c +index e16eabe2..a8c99e2e 100644 +--- a/serverloop.c ++++ b/serverloop.c +@@ -184,7 +184,8 @@ client_alive_check(struct ssh *ssh) + int r, channel_id; + + /* timeout, check to see how many we have had */ +- if (ssh_packet_inc_alive_timeouts(ssh) > ++ 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); +diff --git a/sshd_config.5 b/sshd_config.5 +index d47cb0d2..2cddbd59 100644 +--- a/sshd_config.5 ++++ b/sshd_config.5 +@@ -519,6 +519,9 @@ is set to 15, and + .Cm ClientAliveCountMax + is left at the default, unresponsive SSH clients + will be disconnected after approximately 45 seconds. ++Setting a zero ++.Cm ClientAliveCountMax ++disables connection termination. + .It Cm ClientAliveInterval + Sets a timeout interval in seconds after which if no data has been received + from the client, diff --git a/openssh-8.0p1-crypto-policies.patch b/openssh-8.0p1-crypto-policies.patch new file mode 100644 index 0000000..5dc2289 --- /dev/null +++ b/openssh-8.0p1-crypto-policies.patch @@ -0,0 +1,424 @@ +diff -up openssh-8.0p1/ssh_config.5.crypto-policies openssh-8.0p1/ssh_config.5 +--- openssh-8.0p1/ssh_config.5.crypto-policies 2020-03-24 17:32:54.821789205 +0100 ++++ openssh-8.0p1/ssh_config.5 2020-03-24 17:59:58.174122920 +0100 +@@ -357,17 +357,17 @@ or + .Qq *.c.example.com + domains. + .It Cm CASignatureAlgorithms ++The default is handled system-wide by ++.Xr crypto-policies 7 . ++To see the current defaults and how to modify them, see manual page ++.Xr update-crypto-policies 8 . ++.Pp + Specifies which algorithms are allowed for signing of certificates + by certificate authorities (CAs). +-The default is: +-.Bd -literal -offset indent +-ecdsa-sha2-nistp256.ecdsa-sha2-nistp384,ecdsa-sha2-nistp521, +-ssh-ed25519,rsa-sha2-512,rsa-sha2-256,ssh-rsa +-.Ed +-.Pp + .Xr ssh 1 + will not accept host certificates signed using algorithms other than those + specified. ++.Pp + .It Cm CertificateFile + Specifies a file from which the user's certificate is read. + A corresponding private key must be provided separately in order +@@ -420,16 +420,21 @@ If the option is set to + .Cm no , + the check will not be executed. + .It Cm Ciphers ++The default is handled system-wide by ++.Xr crypto-policies 7 . ++To see the current defaults and how to modify them, see manual page ++.Xr update-crypto-policies 8 . ++.Pp + Specifies the ciphers allowed and their order of preference. + Multiple ciphers must be comma-separated. + If the specified value begins with a + .Sq + +-character, then the specified ciphers will be appended to the default set ++character, then the specified ciphers will be appended to the built-in default set + instead of replacing them. + If the specified value begins with a + .Sq - + character, then the specified ciphers (including wildcards) will be removed +-from the default set instead of replacing them. ++from the built-in default set instead of replacing them. + .Pp + The supported ciphers are: + .Bd -literal -offset indent +@@ -445,13 +450,6 @@ 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 +@@ -800,6 +798,11 @@ command line will be passed untouched to + The default is + .Dq no . + .It Cm GSSAPIKexAlgorithms ++The default is handled system-wide by ++.Xr crypto-policies 7 . ++To see the current defaults and how to modify them, see manual page ++.Xr update-crypto-policies 8 . ++.Pp + The list of key exchange algorithms that are offered for GSSAPI + key exchange. Possible values are + .Bd -literal -offset 3n +@@ -812,9 +815,8 @@ gss-nistp256-sha256-, + gss-curve25519-sha256- + .Ed + .Pp +-The default is +-.Dq gss-group14-sha256-,gss-group16-sha512-,gss-nistp256-sha256-,gss-curve25519-sha256-,gss-group14-sha1-,gss-gex-sha1- . + This option only applies to connections using GSSAPI. ++.Pp + .It Cm HashKnownHosts + Indicates that + .Xr ssh 1 +@@ -1114,26 +1115,21 @@ it may be zero or more of: + and + .Cm pam . + .It Cm KexAlgorithms ++The default is handled system-wide by ++.Xr crypto-policies 7 . ++To see the current defaults and how to modify them, see manual page ++.Xr update-crypto-policies 8 . ++.Pp + Specifies the available KEX (Key Exchange) algorithms. + Multiple algorithms must be comma-separated. + Alternately if the specified value begins with a + .Sq + +-character, then the specified methods will be appended to the default set ++character, then the specified methods will be appended to the built-in default set + instead of replacing them. + If the specified value begins with a + .Sq - + character, then the specified methods (including wildcards) will be removed +-from the default set instead of replacing them. +-The default is: +-.Bd -literal -offset indent +-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, +-diffie-hellman-group14-sha1 +-.Ed ++from the built-in default set instead of replacing them. + .Pp + The list of available key exchange algorithms may also be obtained using + .Qq ssh -Q kex . +@@ -1193,33 +1189,29 @@ The default is INFO. + DEBUG and DEBUG1 are equivalent. + DEBUG2 and DEBUG3 each specify higher levels of verbose output. + .It Cm MACs ++The default is handled system-wide by ++.Xr crypto-policies 7 . ++To see the current defaults and how to modify them, see manual page ++.Xr update-crypto-policies 8 . ++.Pp + 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 value begins with a + .Sq + +-character, then the specified algorithms will be appended to the default set ++character, then the specified algorithms will be appended to the built-in default set + instead of replacing them. + If the specified value begins with a + .Sq - + character, then the specified algorithms (including wildcards) will be removed +-from the default set instead of replacing them. ++from the built-in default set instead of replacing them. + .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 +@@ -1352,27 +1344,21 @@ instead of continuing to execute and pas + The default is + .Cm no . + .It Cm PubkeyAcceptedKeyTypes ++The default is handled system-wide by ++.Xr crypto-policies 7 . ++To see the current defaults and how to modify them, see manual page ++.Xr update-crypto-policies 8 . ++.Pp + Specifies the key types that will be used for public key authentication + as a comma-separated list of patterns. + Alternately if the specified value begins with a + .Sq + +-character, then the key types after it will be appended to the default ++character, then the key types after it will be appended to the built-in default + instead of replacing it. + If the specified value begins with a + .Sq - + character, then the specified key types (including wildcards) will be removed +-from the default set instead of replacing them. +-The default for this option is: +-.Bd -literal -offset 3n +-ecdsa-sha2-nistp256-cert-v01@openssh.com, +-ecdsa-sha2-nistp384-cert-v01@openssh.com, +-ecdsa-sha2-nistp521-cert-v01@openssh.com, +-ssh-ed25519-cert-v01@openssh.com, +-rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com, +-ssh-rsa-cert-v01@openssh.com, +-ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521, +-ssh-ed25519,rsa-sha2-512,rsa-sha2-256,ssh-rsa +-.Ed ++from the built-in default set instead of replacing them. + .Pp + The list of available key types may also be obtained using + .Qq ssh -Q key . +diff -up openssh-8.0p1/sshd_config.5.crypto-policies openssh-8.0p1/sshd_config.5 +--- openssh-8.0p1/sshd_config.5.crypto-policies 2020-03-24 17:32:54.802788908 +0100 ++++ openssh-8.0p1/sshd_config.5 2020-03-24 17:54:13.347740176 +0100 +@@ -383,16 +383,16 @@ If the argument is + then no banner is displayed. + By default, no banner is displayed. + .It Cm CASignatureAlgorithms ++The default is handled system-wide by ++.Xr crypto-policies 7 . ++To see the current defaults and how to modify them, see manual page ++.Xr update-crypto-policies 8 . ++.Pp + Specifies which algorithms are allowed for signing of certificates + by certificate authorities (CAs). +-The default is: +-.Bd -literal -offset indent +-ecdsa-sha2-nistp256.ecdsa-sha2-nistp384,ecdsa-sha2-nistp521, +-ssh-ed25519,rsa-sha2-512,rsa-sha2-256,ssh-rsa +-.Ed +-.Pp + Certificates signed using other algorithms will not be accepted for + public key or host-based authentication. ++.Pp + .It Cm ChallengeResponseAuthentication + Specifies whether challenge-response authentication is allowed (e.g. via + PAM or through authentication styles supported in +@@ -454,16 +454,21 @@ The default is + indicating not to + .Xr chroot 2 . + .It Cm Ciphers ++The default is handled system-wide by ++.Xr crypto-policies 7 . ++To see the current defaults and how to modify them, see manual page ++.Xr update-crypto-policies 8 . ++.Pp + Specifies the ciphers allowed. + Multiple ciphers must be comma-separated. + If the specified value begins with a + .Sq + +-character, then the specified ciphers will be appended to the default set ++character, then the specified ciphers will be appended to the built-in default set + instead of replacing them. + If the specified value begins with a + .Sq - + character, then the specified ciphers (including wildcards) will be removed +-from the default set instead of replacing them. ++from the built-in default set instead of replacing them. + .Pp + The supported ciphers are: + .Pp +@@ -490,13 +495,6 @@ aes256-gcm@openssh.com + chacha20-poly1305@openssh.com + .El + .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 ClientAliveCountMax +@@ -688,6 +686,11 @@ For this to work + .Cm GSSAPIKeyExchange + needs to be enabled in the server and also used by the client. + .It Cm GSSAPIKexAlgorithms ++The default is handled system-wide by ++.Xr crypto-policies 7 . ++To see the current defaults and how to modify them, see manual page ++.Xr update-crypto-policies 8 . ++.Pp + The list of key exchange algorithms that are accepted by GSSAPI + key exchange. Possible values are + .Bd -literal -offset 3n +@@ -700,8 +703,6 @@ gss-nistp256-sha256-, + gss-curve25519-sha256- + .Ed + .Pp +-The default is +-.Dq gss-group14-sha256-,gss-group16-sha512-,gss-nistp256-sha256-,gss-curve25519-sha256-,gss-group14-sha1-,gss-gex-sha1- . + This option only applies to connections using GSSAPI. + .It Cm HostbasedAcceptedKeyTypes + Specifies the key types that will be accepted for hostbased authentication +@@ -791,19 +791,13 @@ is specified, the location of the socket + .Ev SSH_AUTH_SOCK + environment variable. + .It Cm HostKeyAlgorithms ++The default is handled system-wide by ++.Xr crypto-policies 7 . ++To see the current defaults and how to modify them, see manual page ++.Xr update-crypto-policies 8 . ++.Pp + Specifies the host key algorithms + that the server offers. +-The default for this option is: +-.Bd -literal -offset 3n +-ecdsa-sha2-nistp256-cert-v01@openssh.com, +-ecdsa-sha2-nistp384-cert-v01@openssh.com, +-ecdsa-sha2-nistp521-cert-v01@openssh.com, +-ssh-ed25519-cert-v01@openssh.com, +-rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com, +-ssh-rsa-cert-v01@openssh.com, +-ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521, +-ssh-ed25519,rsa-sha2-512,rsa-sha2-256,ssh-rsa +-.Ed + .Pp + The list of available key types may also be obtained using + .Qq ssh -Q key . +@@ -922,16 +916,21 @@ Specifies whether to look at .k5login fi + The default is + .Cm yes . + .It Cm KexAlgorithms ++The default is handled system-wide by ++.Xr crypto-policies 7 . ++To see the current defaults and how to modify them, see manual page ++.Xr update-crypto-policies 8 . ++.Pp + Specifies the available KEX (Key Exchange) algorithms. + Multiple algorithms must be comma-separated. + Alternately if the specified value begins with a + .Sq + +-character, then the specified methods will be appended to the default set ++character, then the specified methods will be appended to the built-in default set + instead of replacing them. + If the specified value begins with a + .Sq - + character, then the specified methods (including wildcards) will be removed +-from the default set instead of replacing them. ++from the built-in default set instead of replacing them. + The supported algorithms are: + .Pp + .Bl -item -compact -offset indent +@@ -961,15 +960,6 @@ ecdh-sha2-nistp384 + ecdh-sha2-nistp521 + .El + .Pp +-The default is: +-.Bd -literal -offset indent +-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,diffie-hellman-group14-sha1 +-.Ed +-.Pp + The list of available key exchange algorithms may also be obtained using + .Qq ssh -Q kex . + .It Cm ListenAddress +@@ -1038,17 +1028,22 @@ DEBUG and DEBUG1 are equivalent. + DEBUG2 and DEBUG3 each specify higher levels of debugging output. + Logging with a DEBUG level violates the privacy of users and is not recommended. + .It Cm MACs ++The default is handled system-wide by ++.Xr crypto-policies 7 . ++To see the current defaults and how to modify them, see manual page ++.Xr update-crypto-policies 8 . ++.Pp + Specifies the available MAC (message authentication code) algorithms. + The MAC algorithm is used for data integrity protection. + Multiple algorithms must be comma-separated. + If the specified value begins with a + .Sq + +-character, then the specified algorithms will be appended to the default set ++character, then the specified algorithms will be appended to the built-in default set + instead of replacing them. + If the specified value begins with a + .Sq - + character, then the specified algorithms (including wildcards) will be removed +-from the default set instead of replacing them. ++from the built-in default set instead of replacing them. + .Pp + The algorithms that contain + .Qq -etm +@@ -1091,15 +1086,6 @@ umac-64-etm@openssh.com + umac-128-etm@openssh.com + .El + .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 Match +@@ -1446,27 +1432,21 @@ or equivalent.) + The default is + .Cm yes . + .It Cm PubkeyAcceptedKeyTypes ++The default is handled system-wide by ++.Xr crypto-policies 7 . ++To see the current defaults and how to modify them, see manual page ++.Xr update-crypto-policies 8 . ++.Pp + Specifies the key types that will be accepted for public key authentication + as a list of comma-separated patterns. + Alternately if the specified value begins with a + .Sq + +-character, then the specified key types will be appended to the default set ++character, then the specified key types will be appended to the built-in default set + instead of replacing them. + If the specified value begins with a + .Sq - + character, then the specified key types (including wildcards) will be removed +-from the default set instead of replacing them. +-The default for this option is: +-.Bd -literal -offset 3n +-ecdsa-sha2-nistp256-cert-v01@openssh.com, +-ecdsa-sha2-nistp384-cert-v01@openssh.com, +-ecdsa-sha2-nistp521-cert-v01@openssh.com, +-ssh-ed25519-cert-v01@openssh.com, +-rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com, +-ssh-rsa-cert-v01@openssh.com, +-ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521, +-ssh-ed25519,rsa-sha2-512,rsa-sha2-256,ssh-rsa +-.Ed ++from the built-in default set instead of replacing them. + .Pp + The list of available key types may also be obtained using + .Qq ssh -Q key . diff --git a/openssh-8.0p1-crypto-policy-doc.patch b/openssh-8.0p1-crypto-policy-doc.patch new file mode 100644 index 0000000..483f1c8 --- /dev/null +++ b/openssh-8.0p1-crypto-policy-doc.patch @@ -0,0 +1,25 @@ +diff --color -ru a/sshd.8 b/sshd.8 +--- a/sshd.8 2022-05-31 13:39:10.231843926 +0200 ++++ b/sshd.8 2022-05-31 14:34:01.460815420 +0200 +@@ -78,6 +78,7 @@ + .Xr sshd_config 5 ) ; + command-line options override values specified in the + configuration file. ++This mechanism is used by systemd to apply system-wide crypto-policies to ssh server. + .Nm + rereads its configuration file when it receives a hangup signal, + .Dv SIGHUP , +@@ -207,6 +208,13 @@ + rules may be applied by specifying the connection parameters using one or more + .Fl C + options. ++The configuration does not contain the system-wide crypto-policy configuration. ++To show the most accurate runtime configuration, use: ++.Bd -literal -offset 3n ++source /etc/crypto-policies/back-ends/opensshserver.config ++source /etc/sysconfig/sshd ++sshd -T $OPTIONS $CRYPTO_POLICY ++.Ed + .It Fl t + Test mode. + Only check the validity of the configuration file and sanity of the keys. diff --git a/openssh-8.0p1-cve-2020-14145.patch b/openssh-8.0p1-cve-2020-14145.patch new file mode 100644 index 0000000..c296bc4 --- /dev/null +++ b/openssh-8.0p1-cve-2020-14145.patch @@ -0,0 +1,127 @@ +diff -up openssh-8.0p1/hostfile.c.cve-2020-14145 openssh-8.0p1/hostfile.c +--- openssh-8.0p1/hostfile.c.cve-2020-14145 2019-04-18 00:52:57.000000000 +0200 ++++ openssh-8.0p1/hostfile.c 2021-05-17 16:53:38.694577251 +0200 +@@ -409,6 +409,18 @@ lookup_key_in_hostkeys_by_type(struct ho + found) == HOST_FOUND); + } + ++int ++lookup_marker_in_hostkeys(struct hostkeys *hostkeys, int want_marker) ++{ ++ u_int i; ++ ++ for (i = 0; i < hostkeys->num_entries; i++) { ++ if (hostkeys->entries[i].marker == (HostkeyMarker)want_marker) ++ return 1; ++ } ++ return 0; ++} ++ + static int + write_host_entry(FILE *f, const char *host, const char *ip, + const struct sshkey *key, int store_hash) +diff -up openssh-8.0p1/hostfile.h.cve-2020-14145 openssh-8.0p1/hostfile.h +--- openssh-8.0p1/hostfile.h.cve-2020-14145 2019-04-18 00:52:57.000000000 +0200 ++++ openssh-8.0p1/hostfile.h 2021-05-17 16:53:38.694577251 +0200 +@@ -39,6 +39,7 @@ HostStatus check_key_in_hostkeys(struct + const struct hostkey_entry **); + int lookup_key_in_hostkeys_by_type(struct hostkeys *, int, + const struct hostkey_entry **); ++int lookup_marker_in_hostkeys(struct hostkeys *, int); + + int hostfile_read_key(char **, u_int *, struct sshkey *); + int add_host_to_hostfile(const char *, const char *, +diff -up openssh-8.0p1/sshconnect2.c.cve-2020-14145 openssh-8.0p1/sshconnect2.c +--- openssh-8.0p1/sshconnect2.c.cve-2020-14145 2021-05-17 16:53:38.610576561 +0200 ++++ openssh-8.0p1/sshconnect2.c 2021-05-17 16:54:58.169230103 +0200 +@@ -98,12 +98,25 @@ verify_host_key_callback(struct sshkey * + return 0; + } + ++/* Returns the first item from a comma-separated algorithm list */ ++static char * ++first_alg(const char *algs) ++{ ++ char *ret, *cp; ++ ++ ret = xstrdup(algs); ++ if ((cp = strchr(ret, ',')) != NULL) ++ *cp = '\0'; ++ return ret; ++} ++ + static char * + order_hostkeyalgs(char *host, struct sockaddr *hostaddr, u_short port) + { +- char *oavail, *avail, *first, *last, *alg, *hostname, *ret; ++ char *oavail = NULL, *avail = NULL, *first = NULL, *last = NULL; ++ char *alg = NULL, *hostname = NULL, *ret = NULL, *best = NULL; + size_t maxlen; +- struct hostkeys *hostkeys; ++ struct hostkeys *hostkeys = NULL; + int ktype; + u_int i; + +@@ -115,6 +128,26 @@ order_hostkeyalgs(char *host, struct soc + for (i = 0; i < options.num_system_hostfiles; i++) + load_hostkeys(hostkeys, hostname, options.system_hostfiles[i]); + ++ /* ++ * If a plain public key exists that matches the type of the best ++ * preference HostkeyAlgorithms, then use the whole list as is. ++ * Note that we ignore whether the best preference algorithm is a ++ * certificate type, as sshconnect.c will downgrade certs to ++ * plain keys if necessary. ++ */ ++ best = first_alg(options.hostkeyalgorithms); ++ if (lookup_key_in_hostkeys_by_type(hostkeys, ++ sshkey_type_plain(sshkey_type_from_name(best)), NULL)) { ++ debug3("%s: have matching best-preference key type %s, " ++ "using HostkeyAlgorithms verbatim", __func__, best); ++ ret = xstrdup(options.hostkeyalgorithms); ++ goto out; ++ } ++ ++ /* ++ * Otherwise, prefer the host key algorithms that match known keys ++ * while keeping the ordering of HostkeyAlgorithms as much as possible. ++ */ + oavail = avail = xstrdup(KEX_DEFAULT_PK_ALG); + maxlen = strlen(avail) + 1; + first = xmalloc(maxlen); +@@ -131,11 +164,23 @@ order_hostkeyalgs(char *host, struct soc + while ((alg = strsep(&avail, ",")) && *alg != '\0') { + if ((ktype = sshkey_type_from_name(alg)) == KEY_UNSPEC) + fatal("%s: unknown alg %s", __func__, alg); ++ /* ++ * If we have a @cert-authority marker in known_hosts then ++ * prefer all certificate algorithms. ++ */ ++ if (sshkey_type_is_cert(ktype) && ++ lookup_marker_in_hostkeys(hostkeys, MRK_CA)) { ++ ALG_APPEND(first, alg); ++ continue; ++ } ++ /* If the key appears in known_hosts then prefer it */ + if (lookup_key_in_hostkeys_by_type(hostkeys, +- sshkey_type_plain(ktype), NULL)) ++ sshkey_type_plain(ktype), NULL)) { + ALG_APPEND(first, alg); +- else +- ALG_APPEND(last, alg); ++ continue; ++ } ++ /* Otherwise, put it last */ ++ ALG_APPEND(last, alg); + } + #undef ALG_APPEND + xasprintf(&ret, "%s%s%s", first, +@@ -143,6 +188,8 @@ order_hostkeyalgs(char *host, struct soc + if (*first != '\0') + debug3("%s: prefer hostkeyalgs: %s", __func__, first); + ++ out: ++ free(best); + free(first); + free(last); + free(hostname); diff --git a/openssh-8.0p1-entropy.patch b/openssh-8.0p1-entropy.patch new file mode 100644 index 0000000..5dfee95 --- /dev/null +++ b/openssh-8.0p1-entropy.patch @@ -0,0 +1,302 @@ +diff --git a/entropy.c b/entropy.c +index 2d483b3..b361a04 100644 +--- a/entropy.c ++++ b/entropy.c +@@ -234,6 +234,9 @@ seed_rng(void) + } + #endif /* OPENSSL_PRNG_ONLY */ + ++#ifdef __linux__ ++ linux_seed(); ++#endif /* __linux__ */ + if (RAND_status() != 1) + fatal("PRNG is not seeded"); + +diff --git a/openbsd-compat/Makefile.in b/openbsd-compat/Makefile.in +index b912dbe..9206337 100644 +--- a/openbsd-compat/Makefile.in ++++ b/openbsd-compat/Makefile.in +@@ -20,6 +20,7 @@ OPENBSD=base64.o basename.o bcrypt_pbkdf.o bindresvport.o blowfish.o daemon.o di + port-solaris.o \ + port-net.o \ + port-uw.o \ ++ port-linux-prng.o \ + port-linux-sshd.o + + .c.o: +diff -up openssh-7.4p1/openbsd-compat/port-linux.h.entropy openssh-7.4p1/openbsd-compat/port-linux.h +--- openssh-7.4p1/openbsd-compat/port-linux.h.entropy 2016-12-23 18:34:27.747753563 +0100 ++++ openssh-7.4p1/openbsd-compat/port-linux.h 2016-12-23 18:34:27.769753570 +0100 +@@ -34,4 +34,6 @@ void oom_adjust_restore(void); + void oom_adjust_setup(void); + #endif + ++void linux_seed(void); ++ + #endif /* ! _PORT_LINUX_H */ +diff --git a/openbsd-compat/port-linux-prng.c b/openbsd-compat/port-linux-prng.c +new file mode 100644 +index 0000000..92a617c +--- /dev/null ++++ b/openbsd-compat/port-linux-prng.c +@@ -0,0 +1,78 @@ ++/* ++ * Copyright (c) 2011 - 2020 Red Hat, Inc. ++ * ++ * Authors: ++ * Jan F. Chadima ++ * Jakub Jelen ++ * ++ * 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. ++ */ ++ ++/* ++ * Linux-specific portability code - prng support ++ */ ++ ++#include "includes.h" ++ ++#include ++#include ++#include ++#include ++ ++#include "log.h" ++ ++void ++linux_seed(void) ++{ ++ char *env = NULL; ++ size_t randlen = 14, left; ++ unsigned int flags = 0; ++ unsigned char buf[256], *p; ++ ++ env = getenv("SSH_USE_STRONG_RNG"); ++ if (env && strcmp(env, "0") != 0) { ++ size_t ienv = atoi(env); ++ ++ /* Max on buffer length */ ++ if (ienv > sizeof(buf)) ++ ienv = sizeof(buf); ++ /* Minimum is always 14 B */ ++ if (ienv > randlen) ++ randlen = ienv; ++ flags = GRND_RANDOM; ++ } ++ ++ errno = 0; ++ left = randlen; ++ p = buf; ++ do { ++ ssize_t len = getrandom(p, left, flags); ++ if (len == -1) { ++ if (errno != EINTR) { ++ if (flags) { ++ /* With the variable present, this is fatal error */ ++ fatal("Failed to seed from getrandom: %s", strerror(errno)); ++ } else { ++ /* Otherwise we log the issue drop out from here */ ++ debug("Failed to seed from getrandom: %s", strerror(errno)); ++ return; ++ } ++ } ++ } else if (len > 0) { ++ left -= len; ++ p += len; ++ } ++ } while (left > 0); ++ ++ RAND_seed(buf, randlen); ++} +diff --git a/ssh-add.1 b/ssh-add.1 +index 4812448..16305bf 100644 +--- a/ssh-add.1 ++++ b/ssh-add.1 +@@ -161,6 +161,22 @@ to make this work.) + Identifies the path of a + .Ux Ns -domain + socket used to communicate with the agent. ++.It Ev SSH_USE_STRONG_RNG ++The reseeding of the OpenSSL random generator is usually done from ++.Cm getrandom(1) ++without any specific flags. ++If the ++.Cm SSH_USE_STRONG_RNG ++environment variable is set to value other than ++.Cm 0 ++the OpenSSL random generator is reseeded from ++.Cm getrandom(1) ++with GRND_RANDOM flag specified. ++The number of bytes read is defined by the SSH_USE_STRONG_RNG value. ++Minimum is 14 bytes. ++This setting is not recommended on the computers without the hardware ++random generator because insufficient entropy causes the connection to ++be blocked until enough entropy is available. + .El + .Sh FILES + .Bl -tag -width Ds +diff --git a/ssh-agent.1 b/ssh-agent.1 +index 281ecbd..1a9a635 100644 +--- a/ssh-agent.1 ++++ b/ssh-agent.1 +@@ -201,6 +201,26 @@ 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 ENVIRONMENT ++.Bl -tag -width Ds -compact ++.Pp ++.It Pa SSH_USE_STRONG_RNG ++The reseeding of the OpenSSL random generator is usually done from ++.Cm getrandom(1) ++without any specific flags. ++If the ++.Cm SSH_USE_STRONG_RNG ++environment variable is set to value other than ++.Cm 0 ++the OpenSSL random generator is reseeded from ++.Cm getrandom(1) ++with GRND_RANDOM flag specified. ++The number of bytes read is defined by the SSH_USE_STRONG_RNG value. ++Minimum is 14 bytes. ++This setting is not recommended on the computers without the hardware ++random generator because insufficient entropy causes the connection to ++be blocked until enough entropy is available. ++.El + .Sh SEE ALSO + .Xr ssh 1 , + .Xr ssh-add 1 , +diff --git a/ssh-keygen.1 b/ssh-keygen.1 +index 12e00d4..1b51a4a 100644 +--- a/ssh-keygen.1 ++++ b/ssh-keygen.1 +@@ -832,6 +832,26 @@ Contains Diffie-Hellman groups used for DH-GEX. + The file format is described in + .Xr moduli 5 . + .El ++.Sh ENVIRONMENT ++.Bl -tag -width Ds -compact ++.Pp ++.It Pa SSH_USE_STRONG_RNG ++The reseeding of the OpenSSL random generator is usually done from ++.Cm getrandom(1) ++without any specific flags. ++If the ++.Cm SSH_USE_STRONG_RNG ++environment variable is set to value other than ++.Cm 0 ++the OpenSSL random generator is reseeded from ++.Cm getrandom(1) ++with GRND_RANDOM flag specified. ++The number of bytes read is defined by the SSH_USE_STRONG_RNG value. ++Minimum is 14 bytes. ++This setting is not recommended on the computers without the hardware ++random generator because insufficient entropy causes the connection to ++be blocked until enough entropy is available. ++.El + .Sh SEE ALSO + .Xr ssh 1 , + .Xr ssh-add 1 , +diff --git a/ssh-keysign.8 b/ssh-keysign.8 +index 69d0829..02d79f8 100644 +--- a/ssh-keysign.8 ++++ b/ssh-keysign.8 +@@ -80,6 +80,26 @@ must be set-uid root if host-based authentication is used. + If these files exist they are assumed to contain public certificate + information corresponding with the private keys above. + .El ++.Sh ENVIRONMENT ++.Bl -tag -width Ds -compact ++.Pp ++.It Pa SSH_USE_STRONG_RNG ++The reseeding of the OpenSSL random generator is usually done from ++.Cm getrandom(1) ++without any specific flags. ++If the ++.Cm SSH_USE_STRONG_RNG ++environment variable is set to value other than ++.Cm 0 ++the OpenSSL random generator is reseeded from ++.Cm getrandom(1) ++with GRND_RANDOM flag specified. ++The number of bytes read is defined by the SSH_USE_STRONG_RNG value. ++Minimum is 14 bytes. ++This setting is not recommended on the computers without the hardware ++random generator because insufficient entropy causes the connection to ++be blocked until enough entropy is available. ++.El + .Sh SEE ALSO + .Xr ssh 1 , + .Xr ssh-keygen 1 , +diff --git a/ssh.1 b/ssh.1 +index 929904b..f65e42f 100644 +--- a/ssh.1 ++++ b/ssh.1 +@@ -1309,6 +1309,25 @@ For more information, see the + .Cm PermitUserEnvironment + option in + .Xr sshd_config 5 . ++.Bl -tag -width "SSH_ORIGINAL_COMMAND" ++.Pp ++.It Ev SSH_USE_STRONG_RNG ++The reseeding of the OpenSSL random generator is usually done from ++.Cm getrandom(1) ++without any specific flags. ++If the ++.Cm SSH_USE_STRONG_RNG ++environment variable is set to value other than ++.Cm 0 ++the OpenSSL random generator is reseeded from ++.Cm getrandom(1) ++with GRND_RANDOM flag specified. ++The number of bytes read is defined by the SSH_USE_STRONG_RNG value. ++Minimum is 14 bytes. ++This setting is not recommended on the computers without the hardware ++random generator because insufficient entropy causes the connection to ++be blocked until enough entropy is available. ++.El + .Sh FILES + .Bl -tag -width Ds -compact + .It Pa ~/.rhosts +diff --git a/sshd.8 b/sshd.8 +index c2c237f..058d37a 100644 +--- a/sshd.8 ++++ b/sshd.8 +@@ -951,6 +951,26 @@ 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 ENVIRONMENT ++.Bl -tag -width Ds -compact ++.Pp ++.It Ev SSH_USE_STRONG_RNG ++The reseeding of the OpenSSL random generator is usually done from ++.Cm getrandom(1) ++without any specific flags. ++If the ++.Cm SSH_USE_STRONG_RNG ++environment variable is set to value other than ++.Cm 0 ++the OpenSSL random generator is reseeded from ++.Cm getrandom(1) ++with GRND_RANDOM flag specified. ++The number of bytes read is defined by the SSH_USE_STRONG_RNG value. ++Minimum is 14 bytes. ++This setting is not recommended on the computers without the hardware ++random generator because insufficient entropy causes the connection to ++be blocked until enough entropy is available. ++.El + .Sh IPV6 + IPv6 address can be used everywhere where IPv4 address. In all entries must be the IPv6 address enclosed in square brackets. Note: The square brackets are metacharacters for the shell and must be escaped in shell. + .Sh SEE ALSO + diff --git a/openssh-7.8p1-gsissh.patch b/openssh-8.0p1-gsissh.patch similarity index 58% rename from openssh-7.8p1-gsissh.patch rename to openssh-8.0p1-gsissh.patch index 7e0d6dc..d26b3f4 100644 --- a/openssh-7.8p1-gsissh.patch +++ b/openssh-8.0p1-gsissh.patch @@ -1,10 +1,10 @@ -diff -Nur openssh-7.8p1.orig/auth2.c openssh-7.8p1/auth2.c ---- openssh-7.8p1.orig/auth2.c 2018-10-24 14:09:16.619775688 +0200 -+++ openssh-7.8p1/auth2.c 2018-10-24 14:09:54.219377927 +0200 -@@ -270,7 +270,27 @@ - user = packet_get_cstring(NULL); - service = packet_get_cstring(NULL); - method = packet_get_cstring(NULL); +diff -Nur openssh-8.0p1.orig/auth2.c openssh-8.0p1/auth2.c +--- openssh-8.0p1.orig/auth2.c 2020-11-30 06:45:06.305912769 +0100 ++++ openssh-8.0p1/auth2.c 2020-11-30 06:46:23.890105258 +0100 +@@ -281,7 +281,28 @@ + (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); + +#ifdef GSSAPI @@ -19,7 +19,8 @@ diff -Nur openssh-7.8p1.orig/auth2.c openssh-7.8p1/auth2.c + debug("set username to %s from gssapi context", user); + } else { + debug("failed to set username from gssapi context"); -+ packet_send_debug("failed to set username from gssapi context"); ++ ssh_packet_send_debug(ssh, ++ "failed to set username from gssapi context"); + } + } + } @@ -30,13 +31,13 @@ diff -Nur openssh-7.8p1.orig/auth2.c openssh-7.8p1/auth2.c debug("attempt %d failures %d", authctxt->attempt, authctxt->failures); #ifdef WITH_SELINUX -@@ -281,11 +301,32 @@ +@@ -292,11 +313,33 @@ if ((style = strchr(user, ':')) != NULL) *style++ = 0; - if (authctxt->attempt++ == 0) { - /* setup auth context */ -- authctxt->pw = PRIVSEP(getpwnamallow(user)); +- authctxt->pw = PRIVSEP(getpwnamallow(ssh, user)); + /* If first time or username changed or empty username, + setup/reset authentication context. */ + if ((authctxt->attempt++ == 0) || @@ -50,7 +51,8 @@ diff -Nur openssh-7.8p1.orig/auth2.c openssh-7.8p1/auth2.c authctxt->user = xstrdup(user); - if (authctxt->pw && strcmp(service, "ssh-connection")==0) { + if (strcmp(service, "ssh-connection") != 0) { -+ packet_disconnect("Unsupported service %s", service); ++ ssh_packet_disconnect(ssh, "Unsupported service %s", ++ service); + } +#ifdef GSSAPI + /* If we're going to set the username based on the @@ -62,12 +64,12 @@ diff -Nur openssh-7.8p1.orig/auth2.c openssh-7.8p1/auth2.c + authctxt->pw = fakepw(); + } else { +#endif -+ authctxt->pw = PRIVSEP(getpwnamallow(user)); ++ authctxt->pw = PRIVSEP(getpwnamallow(ssh, user)); + if (authctxt->pw) { authctxt->valid = 1; debug2("%s: setting up authctxt for %s", __func__, user); -@@ -293,6 +334,9 @@ +@@ -304,6 +347,9 @@ /* Invalid user, fake password information */ authctxt->pw = fakepw(); } @@ -76,8 +78,8 @@ diff -Nur openssh-7.8p1.orig/auth2.c openssh-7.8p1/auth2.c +#endif #ifdef USE_PAM if (options.use_pam) - PRIVSEP(start_pam(authctxt)); -@@ -301,6 +345,7 @@ + PRIVSEP(start_pam(ssh)); +@@ -312,6 +358,7 @@ authctxt->valid ? "authenticating " : "invalid ", user); setproctitle("%s%s", authctxt->valid ? user : "unknown", use_privsep ? " [net]" : ""); @@ -85,74 +87,85 @@ diff -Nur openssh-7.8p1.orig/auth2.c openssh-7.8p1/auth2.c authctxt->service = xstrdup(service); authctxt->style = style ? xstrdup(style) : NULL; #ifdef WITH_SELINUX -@@ -315,9 +360,10 @@ - userauth_banner(); +@@ -327,9 +374,10 @@ if (auth2_setup_methods_lists(authctxt) != 0) - packet_disconnect("no authentication methods enabled"); + ssh_packet_disconnect(ssh, + "no authentication methods enabled"); - } else if (strcmp(user, authctxt->user) != 0 || - strcmp(service, authctxt->service) != 0) { -- packet_disconnect("Change of username or service not allowed: " +- ssh_packet_disconnect(ssh, "Change of username or service " + } + } + if (strcmp(service, authctxt->service) != 0) { -+ packet_disconnect("Change of service not allowed: " - "(%s,%s) -> (%s,%s)", ++ ssh_packet_disconnect(ssh, "Change of service " + "not allowed: (%s,%s) -> (%s,%s)", authctxt->user, authctxt->service, user, service); } -diff -Nur openssh-7.8p1.orig/auth2-gss.c openssh-7.8p1/auth2-gss.c ---- openssh-7.8p1.orig/auth2-gss.c 2018-10-24 14:09:16.410777899 +0200 -+++ openssh-7.8p1/auth2-gss.c 2018-10-24 17:39:13.477216029 +0200 -@@ -50,6 +50,7 @@ +diff -Nur openssh-8.0p1.orig/auth2-gss.c openssh-8.0p1/auth2-gss.c +--- openssh-8.0p1.orig/auth2-gss.c 2020-11-30 06:45:06.156912393 +0100 ++++ openssh-8.0p1/auth2-gss.c 2020-11-30 06:46:23.890105258 +0100 +@@ -49,6 +49,7 @@ extern ServerOptions options; -+static void ssh_gssapi_userauth_error(Gssctxt *ctxt); ++static void ssh_gssapi_userauth_error(Gssctxt *ctxt, struct ssh *ssh); static int input_gssapi_token(int type, u_int32_t plen, struct ssh *ssh); static int input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh); static int input_gssapi_exchange_complete(int type, u_int32_t plen, struct ssh *ssh); -@@ -63,8 +64,8 @@ +@@ -62,8 +63,8 @@ { Authctxt *authctxt = ssh->authctxt; - int authenticated = 0; + int r, authenticated = 0; - struct sshbuf *b = NULL; - gss_buffer_desc mic, gssbuf; + struct sshbuf *b = NULL, *b2 = NULL; + gss_buffer_desc mic, gssbuf, gssbuf2; - u_int len; + u_char *p; + size_t len; - mic.value = packet_get_string(&len); -@@ -81,13 +82,27 @@ - gssbuf.value = sshbuf_mutable_ptr(b); +@@ -74,6 +75,9 @@ + if ((b = sshbuf_new()) == NULL) + fatal("%s: sshbuf_new failed", __func__); + ++ if ((b2 = sshbuf_new()) == NULL) ++ fatal("%s: sshbuf_new failed", __func__); ++ + mic.value = p; + mic.length = len; + +@@ -84,13 +88,28 @@ + fatal("%s: sshbuf_mutable_ptr failed", __func__); gssbuf.length = sshbuf_len(b); + /* client may have used empty username to determine target + name from GSSAPI context */ + ssh_gssapi_buildmic(b2, "", authctxt->service, "gssapi-keyex"); + -+ gssbuf2.value = sshbuf_mutable_ptr(b2); ++ if ((gssbuf2.value = sshbuf_mutable_ptr(b2)) == NULL) ++ fatal("%s: sshbuf_mutable_ptr failed", __func__); + gssbuf2.length = sshbuf_len(b2); + /* gss_kex_context is NULL with privsep, so we can't check it here */ - if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gss_kex_context, + if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gss_kex_context, - &gssbuf, &mic)))) - authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user, -- authctxt->pw)); +- authctxt->pw, 1)); + &gssbuf, &mic))) || -+ !GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gss_kex_context, ++ !GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gss_kex_context, + &gssbuf2, &mic)))) { + if (authctxt->valid && authctxt->user && authctxt->user[0]) { + authenticated = + PRIVSEP(ssh_gssapi_userok(authctxt->user, -+ authctxt->pw, 1 /* gssapi-keyex */)); ++ authctxt->pw, 1)); + } + } - + sshbuf_free(b); + sshbuf_free(b2); free(mic.value); return (authenticated); -@@ -142,7 +157,9 @@ +@@ -145,7 +164,9 @@ return (0); } @@ -163,7 +176,7 @@ diff -Nur openssh-7.8p1.orig/auth2-gss.c openssh-7.8p1/auth2-gss.c debug2("%s: disabled because of invalid user", __func__); free(doid); return (0); -@@ -180,7 +197,7 @@ +@@ -183,7 +204,7 @@ Gssctxt *gssctxt; gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; gss_buffer_desc recv_tok; @@ -172,21 +185,22 @@ diff -Nur openssh-7.8p1.orig/auth2-gss.c openssh-7.8p1/auth2-gss.c u_char *p; size_t len; int r; -@@ -201,6 +218,7 @@ +@@ -204,6 +225,7 @@ free(p); if (GSS_ERROR(maj_status)) { -+ ssh_gssapi_userauth_error(gssctxt); ++ ssh_gssapi_userauth_error(gssctxt, ssh); if (send_tok.length != 0) { if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_GSSAPI_ERRTOK)) != 0 || -@@ -275,6 +293,32 @@ +@@ -278,6 +300,34 @@ return 0; } +static void -+gssapi_set_username(Authctxt *authctxt) ++gssapi_set_username(struct ssh *ssh) +{ ++ Authctxt *authctxt = ssh->authctxt; + char *lname = NULL; + + if ((authctxt->user == NULL) || (authctxt->user[0] == '\0')) { @@ -195,17 +209,18 @@ diff -Nur openssh-7.8p1.orig/auth2-gss.c openssh-7.8p1/auth2-gss.c + if (authctxt->user) free(authctxt->user); + authctxt->user = lname; + debug("set username to %s from gssapi context", lname); -+ authctxt->pw = PRIVSEP(getpwnamallow(authctxt->user)); ++ authctxt->pw = PRIVSEP(getpwnamallow(ssh, authctxt->user)); + if (authctxt->pw) { + authctxt->valid = 1; +#ifdef USE_PAM + if (options.use_pam) -+ PRIVSEP(start_pam(authctxt)); ++ PRIVSEP(start_pam(ssh)); +#endif + } + } else { + debug("failed to set username from gssapi context"); -+ packet_send_debug("failed to set username from gssapi context"); ++ ssh_packet_send_debug(ssh, ++ "failed to set username from gssapi context"); + } + } +} @@ -213,7 +228,7 @@ diff -Nur openssh-7.8p1.orig/auth2-gss.c openssh-7.8p1/auth2-gss.c /* * This is called when the client thinks we've completed authentication. * It should only be enabled in the dispatch handler by the function above, -@@ -285,12 +329,14 @@ +@@ -288,12 +338,14 @@ input_gssapi_exchange_complete(int type, u_int32_t plen, struct ssh *ssh) { Authctxt *authctxt = ssh->authctxt; @@ -224,40 +239,40 @@ diff -Nur openssh-7.8p1.orig/auth2-gss.c openssh-7.8p1/auth2-gss.c if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep)) fatal("No authentication or GSSAPI context"); -+ gssapi_set_username(authctxt); ++ gssapi_set_username(ssh); + /* * We don't need to check the status, because we're only enabled in * the dispatcher once the exchange is complete -@@ -299,8 +345,11 @@ +@@ -302,8 +354,11 @@ if ((r = sshpkt_get_end(ssh)) != 0) fatal("%s: %s", __func__, ssh_err(r)); - authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user, -- authctxt->pw)); +- authctxt->pw, 1)); + /* user should be set if valid but we double-check here */ + if (authctxt->valid && authctxt->user && authctxt->user[0]) { + authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user, -+ authctxt->pw, 1 /* gssapi-keyex */)); ++ authctxt->pw, 1)); + } if ((!use_privsep || mm_is_monitor()) && (displayname = ssh_gssapi_displayname()) != NULL) -@@ -352,11 +401,17 @@ +@@ -355,11 +410,17 @@ fatal("%s: sshbuf_mutable_ptr failed", __func__); gssbuf.length = sshbuf_len(b); - if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gssctxt, &gssbuf, &mic)))) -- authenticated = -- PRIVSEP(ssh_gssapi_userok(authctxt->user, authctxt->pw)); +- authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user, +- authctxt->pw, 0)); - else -+ gssapi_set_username(authctxt); ++ gssapi_set_username(ssh); + + if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gssctxt, &gssbuf, &mic)))) { + if (authctxt->valid && authctxt->user && authctxt->user[0]) { + authenticated = + PRIVSEP(ssh_gssapi_userok(authctxt->user, -+ authctxt->pw, 0 /* !gssapi-keyex */)); ++ authctxt->pw, 0)); + } + } else { logit("GSSAPI MIC check failed"); @@ -265,23 +280,26 @@ diff -Nur openssh-7.8p1.orig/auth2-gss.c openssh-7.8p1/auth2-gss.c sshbuf_free(b); if (micuser != authctxt->user) -@@ -376,6 +431,23 @@ +@@ -379,6 +440,26 @@ return 0; } -+static void ssh_gssapi_userauth_error(Gssctxt *ctxt) { ++static void ssh_gssapi_userauth_error(Gssctxt *ctxt, struct ssh *ssh) { + char *errstr; -+ OM_uint32 maj,min; ++ OM_uint32 maj, min; ++ int r; + -+ errstr=PRIVSEP(ssh_gssapi_last_error(ctxt,&maj,&min)); ++ errstr = PRIVSEP(ssh_gssapi_last_error(ctxt, &maj, &min)); + if (errstr) { -+ packet_start(SSH2_MSG_USERAUTH_GSSAPI_ERROR); -+ packet_put_int(maj); -+ packet_put_int(min); -+ packet_put_cstring(errstr); -+ packet_put_cstring(""); -+ packet_send(); -+ packet_write_wait(); ++ if ((r = sshpkt_start(ssh, ++ SSH2_MSG_USERAUTH_GSSAPI_ERROR)) != 0 || ++ (r = sshpkt_put_u32(ssh, maj)) != 0 || ++ (r = sshpkt_put_u32(ssh, min)) != 0 || ++ (r = sshpkt_put_cstring(ssh, errstr)) != 0 || ++ (r = sshpkt_put_cstring(ssh, "")) != 0 || ++ (r = sshpkt_send(ssh)) != 0 || ++ (r = ssh_packet_write_wait(ssh)) != 0) ++ fatal("%s: %s", __func__, ssh_err(r)); + free(errstr); + } +} @@ -289,20 +307,10 @@ diff -Nur openssh-7.8p1.orig/auth2-gss.c openssh-7.8p1/auth2-gss.c Authmethod method_gsskeyex = { "gssapi-keyex", userauth_gsskeyex, -diff -Nur openssh-7.8p1.orig/auth.c openssh-7.8p1/auth.c ---- openssh-7.8p1.orig/auth.c 2018-10-24 14:09:16.621775667 +0200 -+++ openssh-7.8p1/auth.c 2018-10-24 14:09:54.220377916 +0200 -@@ -76,6 +76,9 @@ - #include "compat.h" - #include "channels.h" - -+#include "version.h" -+#include "ssh-globus-usage.h" -+ - /* import */ - extern ServerOptions options; - extern int use_privsep; -@@ -339,7 +342,8 @@ +diff -Nur openssh-8.0p1.orig/auth.c openssh-8.0p1/auth.c +--- openssh-8.0p1.orig/auth.c 2020-11-30 06:45:06.306912772 +0100 ++++ openssh-8.0p1/auth.c 2020-11-30 06:46:23.891105261 +0100 +@@ -343,7 +343,8 @@ method, submethod != NULL ? "/" : "", submethod == NULL ? "" : submethod, authctxt->valid ? "" : "invalid user ", @@ -312,31 +320,7 @@ diff -Nur openssh-7.8p1.orig/auth.c openssh-7.8p1/auth.c ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), extra != NULL ? ": " : "", -@@ -365,6 +369,23 @@ - if (authenticated == 0 && !authctxt->postponed && !partial) - audit_event(audit_classify_auth(method)); - #endif -+ if (authenticated) { -+ char *userdn = NULL; -+ char *mech_name = NULL; -+#ifdef GSSAPI -+ ssh_gssapi_get_client_info(&userdn, &mech_name); -+#endif -+ debug("REPORTING (%s) (%s) (%s) (%s) (%s) (%s) (%s)", -+ SSH_RELEASE, SSLeay_version(SSLEAY_VERSION), -+ method, mech_name?mech_name:"NULL", ssh_remote_ipaddr(ssh), -+ (authctxt->user && authctxt->user[0])? -+ authctxt->user : "unknown", -+ userdn?userdn:"NULL"); -+ ssh_globus_send_usage_metrics(SSH_RELEASE, -+ SSLeay_version(SSLEAY_VERSION), -+ method, mech_name, ssh_remote_ipaddr(ssh), -+ authctxt->user, userdn); -+ } - } - - -@@ -577,6 +598,10 @@ +@@ -581,13 +582,18 @@ #endif pw = getpwnam(user); @@ -347,7 +331,6 @@ diff -Nur openssh-7.8p1.orig/auth.c openssh-7.8p1/auth.c #if defined(_AIX) && defined(HAVE_SETAUTHDB) aix_restoreauthdb(); -@@ -596,7 +621,8 @@ #endif if (pw == NULL) { logit("Invalid user %.100s from %.100s port %d", @@ -355,11 +338,11 @@ diff -Nur openssh-7.8p1.orig/auth.c openssh-7.8p1/auth.c + (user && user[0]) ? user : "unknown", + ssh_remote_ipaddr(ssh), ssh_remote_port(ssh)); #ifdef CUSTOM_FAILED_LOGIN - record_failed_login(user, + record_failed_login(ssh, user, auth_get_canonical_hostname(ssh, options.use_dns), "ssh"); -diff -Nur openssh-7.8p1.orig/auth.h openssh-7.8p1/auth.h ---- openssh-7.8p1.orig/auth.h 2018-10-24 14:09:16.622775656 +0200 -+++ openssh-7.8p1/auth.h 2018-10-24 14:09:54.220377916 +0200 +diff -Nur openssh-8.0p1.orig/auth.h openssh-8.0p1/auth.h +--- openssh-8.0p1.orig/auth.h 2020-11-30 06:45:06.306912772 +0100 ++++ openssh-8.0p1/auth.h 2020-11-30 06:46:23.891105261 +0100 @@ -85,6 +85,8 @@ krb5_principal krb5_user; char *krb5_ticket_file; @@ -369,10 +352,18 @@ diff -Nur openssh-7.8p1.orig/auth.h openssh-7.8p1/auth.h int krb5_set_env; #endif struct sshbuf *loginmsg; -diff -Nur openssh-7.8p1.orig/auth-pam.c openssh-7.8p1/auth-pam.c ---- openssh-7.8p1.orig/auth-pam.c 2018-10-24 14:09:16.677775074 +0200 -+++ openssh-7.8p1/auth-pam.c 2018-10-24 14:09:54.221377906 +0200 -@@ -288,6 +288,55 @@ +diff -Nur openssh-8.0p1.orig/auth-pam.c openssh-8.0p1/auth-pam.c +--- openssh-8.0p1.orig/auth-pam.c 2020-11-30 06:45:06.113912285 +0100 ++++ openssh-8.0p1/auth-pam.c 2020-11-30 06:46:23.892105263 +0100 +@@ -251,6 +251,7 @@ + static char *sshpam_rhost = NULL; + static char *sshpam_laddr = NULL; + static char *sshpam_conninfo = NULL; ++static struct ssh *sshpam_ssh = NULL; + + /* Some PAM implementations don't implement this */ + #ifndef HAVE_PAM_GETENVLIST +@@ -291,6 +292,56 @@ # define pam_chauthtok(a,b) (sshpam_chauthtok_ruid((a), (b))) #endif @@ -396,6 +387,7 @@ diff -Nur openssh-7.8p1.orig/auth-pam.c openssh-7.8p1/auth-pam.c +void +sshpam_check_userchanged(void) +{ ++ int sshpam_err; + struct passwd *pw; + const char *user; + @@ -415,7 +407,7 @@ diff -Nur openssh-7.8p1.orig/auth-pam.c openssh-7.8p1/auth-pam.c + "'%.100s' provided by PAM_USER", user); + pwfree(sshpam_authctxt->pw); + sshpam_authctxt->pw = pwcopy(pw); -+ sshpam_authctxt->valid = allowed_user(pw); ++ sshpam_authctxt->valid = allowed_user(sshpam_ssh, pw); + free(sshpam_authctxt->user); + sshpam_authctxt->user = xstrdup(user); + debug("PAM: user '%.100s' now %svalid", user, @@ -428,7 +420,7 @@ diff -Nur openssh-7.8p1.orig/auth-pam.c openssh-7.8p1/auth-pam.c void sshpam_password_change_required(int reqd) { -@@ -319,7 +368,7 @@ +@@ -322,7 +373,7 @@ static void import_environments(struct sshbuf *b) { @@ -437,7 +429,7 @@ diff -Nur openssh-7.8p1.orig/auth-pam.c openssh-7.8p1/auth-pam.c u_int n, i, num_env; int r; -@@ -335,6 +384,19 @@ +@@ -338,6 +389,19 @@ if ((r = sshbuf_get_u32(b, &n)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); sshpam_password_change_required(n != 0); @@ -457,7 +449,7 @@ diff -Nur openssh-7.8p1.orig/auth-pam.c openssh-7.8p1/auth-pam.c /* Import environment from subprocess */ if ((r = sshbuf_get_u32(b, &num_env)) != 0) -@@ -507,6 +569,13 @@ +@@ -510,6 +574,13 @@ if (sshpam_err != PAM_SUCCESS) goto auth_fail; @@ -469,9 +461,9 @@ diff -Nur openssh-7.8p1.orig/auth-pam.c openssh-7.8p1/auth-pam.c + sshpam_authctxt->pw->pw_name); + } if (!do_pam_account()) { - sshpam_err = PAM_ACCT_EXPIRED; - goto auth_fail; -@@ -527,6 +596,13 @@ + /* Preserve PAM_PERM_DENIED and PAM_USER_UNKNOWN. + * Backward compatibility for other errors. */ +@@ -530,6 +601,13 @@ if ((r = sshbuf_put_u32(buffer, sshpam_account_status)) != 0 || (r = sshbuf_put_u32(buffer, sshpam_authctxt->force_pwchange)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); @@ -485,7 +477,16 @@ diff -Nur openssh-7.8p1.orig/auth-pam.c openssh-7.8p1/auth-pam.c /* Export any environment strings set in child */ for (i = 0; environ[i] != NULL; i++) { -@@ -1024,6 +1100,18 @@ +@@ -714,6 +792,8 @@ + xasprintf(&sshpam_conninfo, "SSH_CONNECTION=%.50s %d %.50s %d", + ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), + sshpam_laddr, ssh_local_port(ssh)); ++ /* Save so allowed_user can be called later */ ++ sshpam_ssh = ssh; + } + if (sshpam_rhost != NULL) { + debug("PAM: setting PAM_RHOST to \"%s\"", sshpam_rhost); +@@ -1050,6 +1130,18 @@ debug3("PAM: %s pam_acct_mgmt = %d (%s)", __func__, sshpam_err, pam_strerror(sshpam_handle, sshpam_err)); @@ -504,7 +505,7 @@ diff -Nur openssh-7.8p1.orig/auth-pam.c openssh-7.8p1/auth-pam.c if (sshpam_err != PAM_SUCCESS && sshpam_err != PAM_NEW_AUTHTOK_REQD) { sshpam_account_status = 0; return (sshpam_account_status); -@@ -1314,6 +1402,9 @@ +@@ -1340,6 +1432,9 @@ pam_strerror(sshpam_handle, sshpam_err)); sshpam_err = pam_authenticate(sshpam_handle, flags); @@ -514,9 +515,9 @@ diff -Nur openssh-7.8p1.orig/auth-pam.c openssh-7.8p1/auth-pam.c sshpam_password = NULL; free(fake); if (sshpam_err == PAM_MAXTRIES) -diff -Nur openssh-7.8p1.orig/auth-pam.h openssh-7.8p1/auth-pam.h ---- openssh-7.8p1.orig/auth-pam.h 2018-10-24 14:09:16.320778851 +0200 -+++ openssh-7.8p1/auth-pam.h 2018-10-24 14:09:54.221377906 +0200 +diff -Nur openssh-8.0p1.orig/auth-pam.h openssh-8.0p1/auth-pam.h +--- openssh-8.0p1.orig/auth-pam.h 2020-11-30 06:45:06.114912287 +0100 ++++ openssh-8.0p1/auth-pam.h 2020-11-30 06:46:23.892105263 +0100 @@ -43,5 +43,6 @@ int sshpam_get_maxtries_reached(void); void sshpam_set_maxtries_reached(int); @@ -524,9 +525,9 @@ diff -Nur openssh-7.8p1.orig/auth-pam.h openssh-7.8p1/auth-pam.h +struct passwd *sshpam_getpw(const char *); #endif /* USE_PAM */ -diff -Nur openssh-7.8p1.orig/canohost.c openssh-7.8p1/canohost.c ---- openssh-7.8p1.orig/canohost.c 2018-08-23 07:41:42.000000000 +0200 -+++ openssh-7.8p1/canohost.c 2018-10-24 14:09:54.221377906 +0200 +diff -Nur openssh-8.0p1.orig/canohost.c openssh-8.0p1/canohost.c +--- openssh-8.0p1.orig/canohost.c 2020-11-30 06:45:06.156912393 +0100 ++++ openssh-8.0p1/canohost.c 2020-11-30 06:46:23.892105263 +0100 @@ -17,6 +17,7 @@ #include #include @@ -535,7 +536,7 @@ diff -Nur openssh-7.8p1.orig/canohost.c openssh-7.8p1/canohost.c #include #include -@@ -202,3 +203,33 @@ +@@ -295,3 +296,33 @@ { return get_sock_port(sock, 1); } @@ -569,20 +570,20 @@ diff -Nur openssh-7.8p1.orig/canohost.c openssh-7.8p1/canohost.c + } + } +} -diff -Nur openssh-7.8p1.orig/canohost.h openssh-7.8p1/canohost.h ---- openssh-7.8p1.orig/canohost.h 2018-08-23 07:41:42.000000000 +0200 -+++ openssh-7.8p1/canohost.h 2018-10-24 14:09:54.221377906 +0200 -@@ -23,4 +23,6 @@ +diff -Nur openssh-8.0p1.orig/canohost.h openssh-8.0p1/canohost.h +--- openssh-8.0p1.orig/canohost.h 2020-11-30 06:45:06.156912393 +0100 ++++ openssh-8.0p1/canohost.h 2020-11-30 06:46:23.893105265 +0100 +@@ -26,4 +26,6 @@ #endif /* _CANOHOST_H */ +void resolve_localhost(char **host); + void ipv64_normalise_mapped(struct sockaddr_storage *, socklen_t *); -diff -Nur openssh-7.8p1.orig/configure.ac openssh-7.8p1/configure.ac ---- openssh-7.8p1.orig/configure.ac 2018-10-24 14:09:16.709774736 +0200 -+++ openssh-7.8p1/configure.ac 2018-10-24 14:09:54.223377885 +0200 -@@ -4504,6 +4504,14 @@ +diff -Nur openssh-8.0p1.orig/configure.ac openssh-8.0p1/configure.ac +--- openssh-8.0p1.orig/configure.ac 2020-11-30 06:45:06.273912689 +0100 ++++ openssh-8.0p1/configure.ac 2020-11-30 06:46:23.894105268 +0100 +@@ -4572,6 +4572,14 @@ AC_CHECK_HEADER([gssapi_krb5.h], , [ CPPFLAGS="$oldCPP" ]) @@ -595,9 +596,9 @@ diff -Nur openssh-7.8p1.orig/configure.ac openssh-7.8p1/configure.ac + GSSAPI="KRB5"; + fi fi - if test ! -z "$need_dash_r" ; then - LDFLAGS="$LDFLAGS -R${KRB5ROOT}/lib" -@@ -4543,6 +4551,50 @@ + if test -n "${rpath_opt}" ; then + LDFLAGS="$LDFLAGS ${rpath_opt}${KRB5ROOT}/lib" +@@ -4611,6 +4619,40 @@ AC_SUBST([GSSLIBS]) AC_SUBST([K5LIBS]) @@ -623,35 +624,25 @@ diff -Nur openssh-7.8p1.orig/configure.ac openssh-7.8p1/configure.ac + GSSAPI="GSI" + fi + -+ LIBS="$LIBS `pkg-config --libs globus-gss-assist globus-gssapi-gsi globus-common`" -+ CPPFLAGS="$CPPFLAGS `pkg-config --cflags globus-gss-assist globus-gssapi-gsi globus-common`" ++ AC_PATH_TOOL([PKGCONFIG], [pkg-config], [no]) ++ if test "x$PKGCONFIG" != "xno"; then ++ LIBS="$LIBS `$PKGCONFIG --libs globus-gss-assist globus-gssapi-gsi globus-common`" ++ CPPFLAGS="$CPPFLAGS `$PKGCONFIG --cflags globus-gss-assist globus-gssapi-gsi globus-common`" ++ fi + + AC_DEFINE(GSSAPI) + AC_DEFINE(HAVE_GSSAPI_H) + + AC_CHECK_FUNCS(globus_gss_assist_map_and_authorize) -+ -+ dnl -+ dnl Check for globus_usage_stats_send -+ dnl -+ AC_SEARCH_LIBS(globus_usage_stats_send, -+ globus_usage, -+ AC_DEFINE([HAVE_GLOBUS_USAGE], 1, [Have Globus Usage])) -+ dnl -+ dnl Check for globus_usage_stats_send_array -+ dnl -+ AC_SEARCH_LIBS(globus_usage_stats_send_array, -+ globus_usage, -+ AC_DEFINE([HAVE_GLOBUS_USAGE_SEND_ARRAY], 1, [Have Globus Usage send_array])) +fi + # Check whether user wants systemd support SYSTEMD_MSG="no" AC_ARG_WITH(systemd, -diff -Nur openssh-7.8p1.orig/gss-genr.c openssh-7.8p1/gss-genr.c ---- openssh-7.8p1.orig/gss-genr.c 2018-10-24 14:09:16.682775021 +0200 -+++ openssh-7.8p1/gss-genr.c 2018-10-24 14:09:54.223377885 +0200 -@@ -41,6 +41,7 @@ +diff -Nur openssh-8.0p1.orig/gss-genr.c openssh-8.0p1/gss-genr.c +--- openssh-8.0p1.orig/gss-genr.c 2020-11-30 06:45:06.158912398 +0100 ++++ openssh-8.0p1/gss-genr.c 2020-11-30 06:46:23.895105270 +0100 +@@ -40,6 +40,7 @@ #include "ssherr.h" #include "sshbuf.h" #include "log.h" @@ -659,7 +650,7 @@ diff -Nur openssh-7.8p1.orig/gss-genr.c openssh-7.8p1/gss-genr.c #include "ssh2.h" #include "cipher.h" #include "sshkey.h" -@@ -396,9 +397,18 @@ +@@ -412,9 +413,18 @@ ssh_gssapi_import_name(Gssctxt *ctx, const char *host) { gss_buffer_desc gssbuf; @@ -679,7 +670,7 @@ diff -Nur openssh-7.8p1.orig/gss-genr.c openssh-7.8p1/gss-genr.c gssbuf.value = val; gssbuf.length = strlen(gssbuf.value); -@@ -406,6 +416,7 @@ +@@ -422,6 +432,7 @@ &gssbuf, GSS_C_NT_HOSTBASED_SERVICE, &ctx->name))) ssh_gssapi_error(ctx); @@ -687,9 +678,9 @@ diff -Nur openssh-7.8p1.orig/gss-genr.c openssh-7.8p1/gss-genr.c free(gssbuf.value); return (ctx->major); } -diff -Nur openssh-7.8p1.orig/gss-serv.c openssh-7.8p1/gss-serv.c ---- openssh-7.8p1.orig/gss-serv.c 2018-10-24 14:09:16.511776830 +0200 -+++ openssh-7.8p1/gss-serv.c 2018-10-24 14:09:54.224377874 +0200 +diff -Nur openssh-8.0p1.orig/gss-serv.c openssh-8.0p1/gss-serv.c +--- openssh-8.0p1.orig/gss-serv.c 2020-11-30 06:45:06.179912451 +0100 ++++ openssh-8.0p1/gss-serv.c 2020-11-30 06:46:23.895105270 +0100 @@ -50,10 +50,12 @@ #include "monitor_wrap.h" @@ -698,7 +689,7 @@ diff -Nur openssh-7.8p1.orig/gss-serv.c openssh-7.8p1/gss-serv.c static ssh_gssapi_client gssapi_client = - { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER, GSS_C_NO_CREDENTIAL, -- GSS_C_NO_NAME, NULL, {NULL, NULL, NULL, NULL}, 0, 0}; +- GSS_C_NO_NAME, NULL, {NULL, NULL, NULL, NULL, NULL}, 0, 0}; + { {0, NULL}, GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER, GSS_C_NO_CREDENTIAL, + GSS_C_NO_NAME, GSS_C_NO_NAME, NULL, {NULL, NULL, NULL, NULL, NULL}, + GSS_C_NO_CONTEXT, 0, 0}; @@ -777,8 +768,8 @@ diff -Nur openssh-7.8p1.orig/gss-serv.c openssh-7.8p1/gss-serv.c return GSS_S_COMPLETE; } -- if ((ctx->major = gss_inquire_cred_by_mech(&ctx->minor, -- ctx->client_creds, ctx->oid, &new_name, +- if ((ctx->major = gss_inquire_cred_by_mech(&ctx->minor, +- ctx->client_creds, ctx->oid, &new_name, + /* Call gss_inquire_cred rather than gss_inquire_cred_by_mech + because GSI doesn't support the latter. -jbasney */ + @@ -789,7 +780,7 @@ diff -Nur openssh-7.8p1.orig/gss-serv.c openssh-7.8p1/gss-serv.c return (ctx->major); } -- ctx->major = gss_compare_name(&ctx->minor, client->name, +- ctx->major = gss_compare_name(&ctx->minor, client->name, + ctx->major = gss_compare_name(&ctx->minor, client->cred_name, new_name, &equal); @@ -804,7 +795,7 @@ diff -Nur openssh-7.8p1.orig/gss-serv.c openssh-7.8p1/gss-serv.c - client->name = new_name; + client->cred_name = new_name; client->creds = ctx->client_creds; - ctx->client_creds = GSS_C_NO_CREDENTIAL; + ctx->client_creds = GSS_C_NO_CREDENTIAL; client->updated = 1; @@ -356,12 +391,17 @@ i++; @@ -888,16 +879,9 @@ diff -Nur openssh-7.8p1.orig/gss-serv.c openssh-7.8p1/gss-serv.c return (*gssapi_client.mech->storecreds)(&gssapi_client); } else debug("ssh_gssapi_storecreds: Not a GSSAPI mechanism"); -@@ -442,16 +505,19 @@ - } +@@ -449,11 +512,13 @@ - /* Privileged */ -+/* gssapi_keyex arg added for Globus usage */ - int --ssh_gssapi_userok(char *user, struct passwd *pw) -+ssh_gssapi_userok(char *user, struct passwd *pw, int gssapi_keyex) - { - OM_uint32 lmin; + (void) kex; /* used in privilege separation */ - if (gssapi_client.exportedname.length == 0 || - gssapi_client.exportedname.value == NULL) { @@ -912,7 +896,7 @@ diff -Nur openssh-7.8p1.orig/gss-serv.c openssh-7.8p1/gss-serv.c if (gssapi_client.mech && gssapi_client.mech->userok) if ((*gssapi_client.mech->userok)(&gssapi_client, user)) { gssapi_client.used = 1; -@@ -462,6 +528,7 @@ +@@ -464,6 +529,7 @@ gss_release_buffer(&lmin, &gssapi_client.displayname); gss_release_buffer(&lmin, &gssapi_client.exportedname); gss_release_cred(&lmin, &gssapi_client.creds); @@ -920,7 +904,7 @@ diff -Nur openssh-7.8p1.orig/gss-serv.c openssh-7.8p1/gss-serv.c explicit_bzero(&gssapi_client, sizeof(ssh_gssapi_client)); return 0; -@@ -471,6 +538,24 @@ +@@ -473,6 +539,24 @@ return (0); } @@ -942,23 +926,23 @@ diff -Nur openssh-7.8p1.orig/gss-serv.c openssh-7.8p1/gss-serv.c + return (0); +} + - /* These bits are only used for rekeying. The unpriviledged child is running + /* These bits are only used for rekeying. The unpriviledged child is running * as the user, the monitor is root. * -@@ -497,9 +582,11 @@ +@@ -499,9 +583,11 @@ pam_handle_t *pamh = NULL; struct pam_conv pamconv = {ssh_gssapi_simple_conv, NULL}; char *envstr; + char **p; char **pw; #endif -- if (gssapi_client.store.envval == NULL && -+ if (gssapi_client.store.filename == NULL && -+ gssapi_client.store.envval == NULL && - gssapi_client.store.envvar == NULL) +- if (gssapi_client.store.envval == NULL) ++ if (gssapi_client.store.filename == NULL && ++ gssapi_client.store.envval == NULL) return; - -@@ -525,6 +612,18 @@ + + ok = PRIVSEP(ssh_gssapi_update_creds(&gssapi_client.store)); +@@ -526,6 +612,18 @@ if (ret) return; @@ -974,26 +958,12 @@ diff -Nur openssh-7.8p1.orig/gss-serv.c openssh-7.8p1/gss-serv.c + } + free_pam_environment(p); + - xasprintf(&envstr, "%s=%s", gssapi_client.store.envvar, + xasprintf(&envstr, "%s=%s", gssapi_client.store.envvar, gssapi_client.store.envval); -@@ -565,4 +664,13 @@ - return (char *)gssapi_client.displayname.value; - } - -+/* added for Globus usage */ -+void -+ssh_gssapi_get_client_info(char **userdn, char **mech) { -+ *userdn = gssapi_client.displayname.value; -+ -+ if (gssapi_client.mech) -+ *mech = gssapi_client.mech->name; -+} -+ - #endif -diff -Nur openssh-7.8p1.orig/gss-serv-gsi.c openssh-7.8p1/gss-serv-gsi.c ---- openssh-7.8p1.orig/gss-serv-gsi.c 1970-01-01 01:00:00.000000000 +0100 -+++ openssh-7.8p1/gss-serv-gsi.c 2018-10-24 14:09:54.224377874 +0200 +diff -Nur openssh-8.0p1.orig/gss-serv-gsi.c openssh-8.0p1/gss-serv-gsi.c +--- openssh-8.0p1.orig/gss-serv-gsi.c 1970-01-01 01:00:00.000000000 +0100 ++++ openssh-8.0p1/gss-serv-gsi.c 2020-11-30 06:46:23.896105272 +0100 @@ -0,0 +1,328 @@ +/* + * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. @@ -1323,9 +1293,9 @@ diff -Nur openssh-7.8p1.orig/gss-serv-gsi.c openssh-7.8p1/gss-serv-gsi.c + +#endif /* GSI */ +#endif /* GSSAPI */ -diff -Nur openssh-7.8p1.orig/gss-serv-krb5.c openssh-7.8p1/gss-serv-krb5.c ---- openssh-7.8p1.orig/gss-serv-krb5.c 2018-10-24 14:09:16.497776978 +0200 -+++ openssh-7.8p1/gss-serv-krb5.c 2018-10-24 14:09:54.224377874 +0200 +diff -Nur openssh-8.0p1.orig/gss-serv-krb5.c openssh-8.0p1/gss-serv-krb5.c +--- openssh-8.0p1.orig/gss-serv-krb5.c 2020-11-30 06:45:06.209912527 +0100 ++++ openssh-8.0p1/gss-serv-krb5.c 2020-11-30 06:46:23.896105272 +0100 @@ -379,6 +379,34 @@ return found_principal; } @@ -1361,16 +1331,16 @@ diff -Nur openssh-7.8p1.orig/gss-serv-krb5.c openssh-7.8p1/gss-serv-krb5.c /* This writes out any forwarded credentials from the structure populated * during userauth. Called after we have setuid to the user */ -@@ -471,7 +499,7 @@ +@@ -473,7 +501,7 @@ return set_env; } -int +static int - ssh_gssapi_krb5_updatecreds(ssh_gssapi_ccache *store, + ssh_gssapi_krb5_updatecreds(ssh_gssapi_ccache *store, ssh_gssapi_client *client) { -@@ -542,7 +570,7 @@ +@@ -544,7 +572,7 @@ {9, "\x2A\x86\x48\x86\xF7\x12\x01\x02\x02"}, NULL, &ssh_gssapi_krb5_userok, @@ -1379,108 +1349,87 @@ diff -Nur openssh-7.8p1.orig/gss-serv-krb5.c openssh-7.8p1/gss-serv-krb5.c &ssh_gssapi_krb5_storecreds, &ssh_gssapi_krb5_updatecreds }; -diff -Nur openssh-7.8p1.orig/kexgsss.c openssh-7.8p1/kexgsss.c ---- openssh-7.8p1.orig/kexgsss.c 2018-10-24 14:09:16.685774989 +0200 -+++ openssh-7.8p1/kexgsss.c 2018-10-24 14:09:54.225377863 +0200 +diff -Nur openssh-8.0p1.orig/kexgsss.c openssh-8.0p1/kexgsss.c +--- openssh-8.0p1.orig/kexgsss.c 2020-11-30 06:45:06.160912403 +0100 ++++ openssh-8.0p1/kexgsss.c 2020-11-30 06:46:23.896105272 +0100 @@ -48,6 +48,7 @@ #include "digest.h" #include "ssherr.h" -+static void kex_gss_send_error(Gssctxt *ctxt); ++static void kex_gss_send_error(Gssctxt *ctxt, struct ssh *ssh); extern ServerOptions options; int -@@ -97,8 +98,10 @@ +@@ -96,8 +97,10 @@ debug2("%s: Acquiring credentials", __func__); - if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, oid)))) + if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, oid)))) { -+ kex_gss_send_error(ctxt); ++ kex_gss_send_error(ctxt, ssh); fatal("Unable to acquire credentials for the server"); + } - switch (ssh->kex->kex_type) { - case KEX_GSS_GRP1_SHA1: -@@ -190,12 +193,13 @@ + do { + debug("Wait SSH2_MSG_KEXGSS_INIT"); +@@ -170,13 +173,14 @@ } while (maj_status & GSS_S_CONTINUE_NEEDED); if (GSS_ERROR(maj_status)) { -+ kex_gss_send_error(ctxt); ++ kex_gss_send_error(ctxt, ssh); if (send_tok.length > 0) { - packet_start(SSH2_MSG_KEXGSS_CONTINUE); - packet_put_string(send_tok.value, send_tok.length); - packet_send(); + if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 || + (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0 || + (r = sshpkt_send(ssh)) != 0) + fatal("sshpkt failed: %s", ssh_err(r)); } - fatal("accept_ctx died"); -+ packet_disconnect("GSSAPI Key Exchange handshake failed"); ++ ssh_packet_disconnect(ssh, "GSSAPI Key Exchange handshake failed"); } if (!(ret_flags & GSS_C_MUTUAL_FLAG)) -@@ -638,4 +642,23 @@ - } +@@ -471,4 +475,26 @@ + sshbuf_free(shared_secret); return r; } + +static void -+kex_gss_send_error(Gssctxt *ctxt) { ++kex_gss_send_error(Gssctxt *ctxt, struct ssh *ssh) { + char *errstr; + OM_uint32 maj, min; ++ int r; + + errstr = PRIVSEP(ssh_gssapi_last_error(ctxt, &maj, &min)); + if (errstr) { -+ packet_start(SSH2_MSG_KEXGSS_ERROR); -+ packet_put_int(maj); -+ packet_put_int(min); -+ packet_put_cstring(errstr); -+ packet_put_cstring(""); -+ packet_send(); -+ packet_write_wait(); ++ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_ERROR)) != 0 || ++ (r = sshpkt_put_u32(ssh, maj)) != 0 || ++ (r = sshpkt_put_u32(ssh, min)) != 0 || ++ (r = sshpkt_put_cstring(ssh, errstr)) != 0 || ++ (r = sshpkt_put_cstring(ssh, "")) != 0 || ++ (r = sshpkt_send(ssh)) != 0) ++ fatal("sshpkt failed: %s", ssh_err(r)); ++ if ((r = ssh_packet_write_wait(ssh)) != 0) ++ fatal("ssh_packet_write_wait: %s", ssh_err(r)); + /* XXX - We should probably log the error locally here */ + free(errstr); + } +} - #endif /* GSSAPI */ -diff -Nur openssh-7.8p1.orig/LICENSE.globus_usage openssh-7.8p1/LICENSE.globus_usage ---- openssh-7.8p1.orig/LICENSE.globus_usage 1970-01-01 01:00:00.000000000 +0100 -+++ openssh-7.8p1/LICENSE.globus_usage 2018-10-24 14:09:54.225377863 +0200 -@@ -0,0 +1,18 @@ -+/* -+ * Portions of the Usage Metrics suport code are derived from the -+ * Globus project's GridFTP subject to the following license. -+ * -+ * Copyright 2010 University of Chicago -+ * -+ * Licensed under the Apache License, Version 2.0 (the "License"); -+ * you may not use this file except in compliance with the License. -+ * You may obtain a copy of the License at -+ * -+ * http://www.apache.org/licenses/LICENSE-2.0 -+ * -+ * Unless required by applicable law or agreed to in writing, software -+ * distributed under the License is distributed on an "AS IS" BASIS, -+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -+ * See the License for the specific language governing permissions and -+ * limitations under the License. -+ */ -diff -Nur openssh-7.8p1.orig/Makefile.in openssh-7.8p1/Makefile.in ---- openssh-7.8p1.orig/Makefile.in 2018-10-24 14:09:16.688774958 +0200 -+++ openssh-7.8p1/Makefile.in 2018-10-24 14:09:54.225377863 +0200 -@@ -124,8 +124,10 @@ + #endif /* defined(GSSAPI) && defined(WITH_OPENSSL) */ +diff -Nur openssh-8.0p1.orig/Makefile.in openssh-8.0p1/Makefile.in +--- openssh-8.0p1.orig/Makefile.in 2020-11-30 06:45:06.309912780 +0100 ++++ openssh-8.0p1/Makefile.in 2020-11-30 06:46:23.897105274 +0100 +@@ -125,6 +125,7 @@ auth2-none.o auth2-passwd.o auth2-pubkey.o \ monitor.o monitor_wrap.o auth-krb5.o \ auth2-gss.o gss-serv.o gss-serv-krb5.o kexgsss.o \ + gss-serv-gsi.o \ loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \ - sftp-server.o sftp-common.o \ -+ ssh-globus-usage.o \ + sftp-server.o sftp-common.o sftp-realpath.o \ sandbox-null.o sandbox-rlimit.o sandbox-systrace.o sandbox-darwin.o \ - sandbox-seccomp-filter.o sandbox-capsicum.o sandbox-pledge.o \ - sandbox-solaris.o uidswap.o -diff -Nur openssh-7.8p1.orig/misc.c openssh-7.8p1/misc.c ---- openssh-7.8p1.orig/misc.c 2018-10-24 14:09:16.494777010 +0200 -+++ openssh-7.8p1/misc.c 2018-10-24 14:09:54.226377853 +0200 -@@ -237,11 +237,14 @@ +diff -Nur openssh-8.0p1.orig/misc.c openssh-8.0p1/misc.c +--- openssh-8.0p1.orig/misc.c 2020-11-30 06:45:06.115912290 +0100 ++++ openssh-8.0p1/misc.c 2020-11-30 06:46:23.898105277 +0100 +@@ -313,11 +313,14 @@ #define WHITESPACE " \t\r\n" #define QUOTE "\"" @@ -1496,7 +1445,7 @@ diff -Nur openssh-7.8p1.orig/misc.c openssh-7.8p1/misc.c int wspace = 0; if (*s == NULL) -@@ -249,6 +252,21 @@ +@@ -325,6 +328,21 @@ old = *s; @@ -1518,7 +1467,7 @@ diff -Nur openssh-7.8p1.orig/misc.c openssh-7.8p1/misc.c *s = strpbrk(*s, split_equals ? WHITESPACE QUOTE "=" : WHITESPACE QUOTE); if (*s == NULL) -@@ -324,6 +342,20 @@ +@@ -400,6 +418,20 @@ return copy; } @@ -1539,10 +1488,10 @@ diff -Nur openssh-7.8p1.orig/misc.c openssh-7.8p1/misc.c /* * Convert ASCII string to TCP/IP port number. * Port must be >=0 and <=65535. -diff -Nur openssh-7.8p1.orig/misc.h openssh-7.8p1/misc.h ---- openssh-7.8p1.orig/misc.h 2018-10-24 14:09:16.494777010 +0200 -+++ openssh-7.8p1/misc.h 2018-10-24 14:09:54.226377853 +0200 -@@ -82,6 +82,7 @@ +diff -Nur openssh-8.0p1.orig/misc.h openssh-8.0p1/misc.h +--- openssh-8.0p1.orig/misc.h 2019-04-18 00:52:57.000000000 +0200 ++++ openssh-8.0p1/misc.h 2020-11-30 06:46:23.898105277 +0100 +@@ -87,6 +87,7 @@ void sock_set_v6only(int); struct passwd *pwcopy(struct passwd *); @@ -1550,20 +1499,20 @@ diff -Nur openssh-7.8p1.orig/misc.h openssh-7.8p1/misc.h const char *ssh_gai_strerror(int); typedef struct arglist arglist; -diff -Nur openssh-7.8p1.orig/monitor.c openssh-7.8p1/monitor.c ---- openssh-7.8p1.orig/monitor.c 2018-10-24 14:09:16.689774947 +0200 -+++ openssh-7.8p1/monitor.c 2018-10-24 14:09:54.226377853 +0200 -@@ -150,6 +150,9 @@ - int mm_answer_gss_userok(int, struct sshbuf *); - int mm_answer_gss_checkmic(int, struct sshbuf *); - int mm_answer_gss_sign(int, struct sshbuf *); -+int mm_answer_gss_error(int, struct sshbuf *); -+int mm_answer_gss_indicate_mechs(int, struct sshbuf *); -+int mm_answer_gss_localname(int, struct sshbuf *); - int mm_answer_gss_updatecreds(int, struct sshbuf *); +diff -Nur openssh-8.0p1.orig/monitor.c openssh-8.0p1/monitor.c +--- openssh-8.0p1.orig/monitor.c 2020-11-30 06:45:06.336912848 +0100 ++++ openssh-8.0p1/monitor.c 2020-11-30 06:46:23.899105279 +0100 +@@ -154,6 +154,9 @@ + int mm_answer_gss_userok(struct ssh *, int, struct sshbuf *); + int mm_answer_gss_checkmic(struct ssh *, int, struct sshbuf *); + int mm_answer_gss_sign(struct ssh*, int, struct sshbuf *); ++int mm_answer_gss_error(struct ssh*, int, struct sshbuf *); ++int mm_answer_gss_indicate_mechs(struct ssh*, int, struct sshbuf *); ++int mm_answer_gss_localname(struct ssh*, int, struct sshbuf *); + int mm_answer_gss_updatecreds(struct ssh*, int, struct sshbuf *); #endif -@@ -200,7 +203,7 @@ +@@ -206,7 +209,7 @@ {MONITOR_REQ_MODULI, MON_ONCE, mm_answer_moduli}, #endif {MONITOR_REQ_SIGN, MON_ONCE, mm_answer_sign}, @@ -1572,7 +1521,7 @@ diff -Nur openssh-7.8p1.orig/monitor.c openssh-7.8p1/monitor.c {MONITOR_REQ_AUTHSERV, MON_ONCE, mm_answer_authserv}, #ifdef WITH_SELINUX {MONITOR_REQ_AUTHROLE, MON_ONCE, mm_answer_authrole}, -@@ -208,7 +211,7 @@ +@@ -214,7 +217,7 @@ {MONITOR_REQ_AUTH2_READ_BANNER, MON_ONCE, mm_answer_auth2_read_banner}, {MONITOR_REQ_AUTHPASSWORD, MON_AUTH, mm_answer_authpassword}, #ifdef USE_PAM @@ -1581,7 +1530,7 @@ diff -Nur openssh-7.8p1.orig/monitor.c openssh-7.8p1/monitor.c {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}, -@@ -232,8 +235,11 @@ +@@ -238,8 +241,11 @@ {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}, @@ -1594,7 +1543,7 @@ diff -Nur openssh-7.8p1.orig/monitor.c openssh-7.8p1/monitor.c #endif {0, 0, NULL} }; -@@ -243,6 +249,8 @@ +@@ -249,6 +255,8 @@ {MONITOR_REQ_GSSSETUP, 0, mm_answer_gss_setup_ctx}, {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx}, {MONITOR_REQ_GSSSIGN, 0, mm_answer_gss_sign}, @@ -1603,7 +1552,7 @@ diff -Nur openssh-7.8p1.orig/monitor.c openssh-7.8p1/monitor.c {MONITOR_REQ_GSSUPCREDS, 0, mm_answer_gss_updatecreds}, #endif #ifdef WITH_OPENSSL -@@ -324,6 +332,8 @@ +@@ -329,6 +337,8 @@ #ifdef GSSAPI /* and for the GSSAPI key exchange */ monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1); @@ -1612,16 +1561,16 @@ diff -Nur openssh-7.8p1.orig/monitor.c openssh-7.8p1/monitor.c #endif /* The first few requests do not require asynchronous access */ -@@ -442,6 +452,8 @@ +@@ -448,6 +458,8 @@ #ifdef GSSAPI /* and for the GSSAPI key exchange */ monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1); + monitor_permit(mon_dispatch, MONITOR_REQ_GSSERR, 1); + monitor_permit(mon_dispatch, MONITOR_REQ_GSSMECHS, 1); - #endif + #endif if (auth_opts->permit_pty_flag) { -@@ -763,15 +775,18 @@ +@@ -766,15 +778,18 @@ debug3("%s", __func__); @@ -1631,7 +1580,7 @@ diff -Nur openssh-7.8p1.orig/monitor.c openssh-7.8p1/monitor.c if ((r = sshbuf_get_cstring(m, &username, NULL)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); - pwent = getpwnamallow(username); + pwent = getpwnamallow(ssh, username); + if (authctxt->user) free(authctxt->user); authctxt->user = xstrdup(username); @@ -1643,43 +1592,11 @@ diff -Nur openssh-7.8p1.orig/monitor.c openssh-7.8p1/monitor.c setproctitle("%s [priv]", pwent ? username : "unknown"); free(username); -@@ -2010,13 +2025,17 @@ - mm_answer_gss_userok(int sock, struct sshbuf *m) - { - int r, authenticated; -+ int gssapi_keyex; - const char *displayname; - - if (!options.gss_authentication && !options.gss_keyex) - fatal("%s: GSSAPI authentication not enabled", __func__); - -+ if ((r = sshbuf_get_u32(m, &gssapi_keyex)) != 0) -+ fatal("%s: buffer error: %s", __func__, ssh_err(r)); -+ - authenticated = authctxt->valid && -- ssh_gssapi_userok(authctxt->user, authctxt->pw); -+ ssh_gssapi_userok(authctxt->user, authctxt->pw, gssapi_keyex); - - sshbuf_reset(m); - if ((r = sshbuf_put_u32(m, authenticated)) != 0) -@@ -2025,7 +2044,10 @@ - debug3("%s: sending result %d", __func__, authenticated); - mm_request_send(sock, MONITOR_ANS_GSSUSEROK, m); - -- auth_method = "gssapi-with-mic"; -+ if (gssapi_keyex) -+ auth_method = "gssapi-keyex"; -+ else -+ auth_method = "gssapi-with-mic"; - - if ((displayname = ssh_gssapi_displayname()) != NULL) - auth2_record_info(authctxt, "%s", displayname); -@@ -2034,6 +2056,79 @@ - return (authenticated); +@@ -2067,6 +2082,79 @@ } -+int -+mm_answer_gss_error(int socket, struct sshbuf *m) + int ++mm_answer_gss_error(struct ssh *ssh, int socket, struct sshbuf *m) +{ + OM_uint32 major, minor; + char *msg; @@ -1700,7 +1617,7 @@ diff -Nur openssh-7.8p1.orig/monitor.c openssh-7.8p1/monitor.c +} + +int -+mm_answer_gss_indicate_mechs(int socket, struct sshbuf *m) ++mm_answer_gss_indicate_mechs(struct ssh *ssh, int socket, struct sshbuf *m) +{ + OM_uint32 major, minor; + gss_OID_set mech_set; @@ -1727,7 +1644,7 @@ diff -Nur openssh-7.8p1.orig/monitor.c openssh-7.8p1/monitor.c +} + +int -+mm_answer_gss_localname(int socket, struct sshbuf *m) ++mm_answer_gss_localname(struct ssh *ssh, int socket, struct sshbuf *m) +{ + char *name; + int r; @@ -1751,62 +1668,28 @@ diff -Nur openssh-7.8p1.orig/monitor.c openssh-7.8p1/monitor.c + return(0); +} + - int - mm_answer_gss_sign(int socket, struct sshbuf *m) ++int + mm_answer_gss_sign(struct ssh *ssh, int socket, struct sshbuf *m) { -@@ -2085,12 +2180,14 @@ - ssh_gssapi_ccache store; - int ok, r; + gss_buffer_desc data; +diff -Nur openssh-8.0p1.orig/monitor.h openssh-8.0p1/monitor.h +--- openssh-8.0p1.orig/monitor.h 2020-11-30 06:45:06.310912782 +0100 ++++ openssh-8.0p1/monitor.h 2020-11-30 06:46:23.899105279 +0100 +@@ -75,6 +75,10 @@ -- if ((r = sshbuf_get_cstring(m, &store.envvar, NULL)) != 0 || -+ if ((r = sshbuf_get_cstring(m, &store.filename, NULL)) != 0 || -+ (r = sshbuf_get_cstring(m, &store.envvar, NULL)) != 0 || - (r = sshbuf_get_cstring(m, &store.envval, NULL)) != 0) - fatal("%s: buffer error: %s", __func__, ssh_err(r)); - - ok = ssh_gssapi_update_creds(&store); - -+ free(store.filename); - free(store.envvar); - free(store.envval); - -diff -Nur openssh-7.8p1.orig/monitor.h openssh-7.8p1/monitor.h ---- openssh-7.8p1.orig/monitor.h 2018-10-24 14:09:16.630775571 +0200 -+++ openssh-7.8p1/monitor.h 2018-10-24 14:09:54.227377842 +0200 -@@ -73,8 +73,10 @@ - MONITOR_REQ_AUDIT_UNSUPPORTED = 118, MONITOR_ANS_AUDIT_UNSUPPORTED = 119, - MONITOR_REQ_AUDIT_KEX = 120, MONITOR_ANS_AUDIT_KEX = 121, - MONITOR_REQ_AUDIT_SESSION_KEY_FREE = 122, MONITOR_ANS_AUDIT_SESSION_KEY_FREE = 123, -- MONITOR_REQ_AUDIT_SERVER_KEY_FREE = 124 -- -+ MONITOR_REQ_AUDIT_SERVER_KEY_FREE = 124, + MONITOR_REQ_GSSSIGN = 150, MONITOR_ANS_GSSSIGN = 151, + MONITOR_REQ_GSSUPCREDS = 152, MONITOR_ANS_GSSUPCREDS = 153, ++ + MONITOR_REQ_GSSMECHS = 200, MONITOR_ANS_GSSMECHS = 201, + MONITOR_REQ_GSSLOCALNAME = 202, MONITOR_ANS_GSSLOCALNAME = 203, + MONITOR_REQ_GSSERR = 204, MONITOR_ANS_GSSERR = 205 }; - struct monitor { -diff -Nur openssh-7.8p1.orig/monitor_wrap.c openssh-7.8p1/monitor_wrap.c ---- openssh-7.8p1.orig/monitor_wrap.c 2018-10-24 14:09:16.665775201 +0200 -+++ openssh-7.8p1/monitor_wrap.c 2018-10-24 14:33:40.976350751 +0200 -@@ -1045,13 +1045,15 @@ - } - - int --mm_ssh_gssapi_userok(char *user, struct passwd *pw) -+mm_ssh_gssapi_userok(char *user, struct passwd *pw, int gssapi_keyex) - { - struct sshbuf *m; - int r, authenticated = 0; - - if ((m = sshbuf_new()) == NULL) - fatal("%s: sshbuf_new failed", __func__); -+ if ((r = sshbuf_put_u32(m, gssapi_keyex)) != 0) -+ fatal("%s: buffer error: %s", __func__, ssh_err(r)); - - mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSUSEROK, m); - mm_request_receive_expect(pmonitor->m_recvfd, -@@ -1065,6 +1067,94 @@ + struct ssh; +diff -Nur openssh-8.0p1.orig/monitor_wrap.c openssh-8.0p1/monitor_wrap.c +--- openssh-8.0p1.orig/monitor_wrap.c 2020-11-30 06:45:06.337912850 +0100 ++++ openssh-8.0p1/monitor_wrap.c 2020-11-30 06:46:23.900105282 +0100 +@@ -1061,6 +1061,94 @@ return (authenticated); } @@ -1878,7 +1761,7 @@ diff -Nur openssh-7.8p1.orig/monitor_wrap.c openssh-7.8p1/monitor_wrap.c + int r; + + if ((m = sshbuf_new()) == NULL) -+ fatal("%s: sshbuf_new failed", __func__); ++ fatal("%s: sshbuf_new failed", __func__); + mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSLOCALNAME, m); + + debug3("%s: waiting for MONITOR_ANS_GSSLOCALNAME", __func__); @@ -1901,25 +1784,11 @@ diff -Nur openssh-7.8p1.orig/monitor_wrap.c openssh-7.8p1/monitor_wrap.c OM_uint32 mm_ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_desc *data, gss_buffer_desc *hash) { -@@ -1098,7 +1188,8 @@ - if ((m = sshbuf_new()) == NULL) - fatal("%s: sshbuf_new failed", __func__); - -- if ((r = sshbuf_put_cstring(m, store->envvar ? store->envvar : "")) != 0 || -+ if ((r = sshbuf_put_cstring(m, store->filename ? store->filename : "")) != 0 || -+ (r = sshbuf_put_cstring(m, store->envvar ? store->envvar : "")) != 0 || - (r = sshbuf_put_cstring(m, store->envval ? store->envval : "")) != 0) - fatal("%s: buffer error: %s", __func__, ssh_err(r)); - -diff -Nur openssh-7.8p1.orig/monitor_wrap.h openssh-7.8p1/monitor_wrap.h ---- openssh-7.8p1.orig/monitor_wrap.h 2018-10-24 14:09:16.647775391 +0200 -+++ openssh-7.8p1/monitor_wrap.h 2018-10-24 14:09:54.242377684 +0200 -@@ -65,9 +65,13 @@ - OM_uint32 mm_ssh_gssapi_server_ctx(Gssctxt **, gss_OID); - OM_uint32 mm_ssh_gssapi_accept_ctx(Gssctxt *, - gss_buffer_desc *, gss_buffer_desc *, OM_uint32 *); --int mm_ssh_gssapi_userok(char *user, struct passwd *); -+int mm_ssh_gssapi_userok(char *user, struct passwd *, int gssapi_keyex); +diff -Nur openssh-8.0p1.orig/monitor_wrap.h openssh-8.0p1/monitor_wrap.h +--- openssh-8.0p1.orig/monitor_wrap.h 2020-11-30 06:45:06.319912805 +0100 ++++ openssh-8.0p1/monitor_wrap.h 2020-11-30 06:46:23.900105282 +0100 +@@ -69,6 +69,10 @@ + int mm_ssh_gssapi_userok(char *user, struct passwd *, int kex); OM_uint32 mm_ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t); OM_uint32 mm_ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t); +int mm_ssh_gssapi_localname(char **user); @@ -1929,10 +1798,10 @@ diff -Nur openssh-7.8p1.orig/monitor_wrap.h openssh-7.8p1/monitor_wrap.h int mm_ssh_gssapi_update_creds(ssh_gssapi_ccache *); #endif -diff -Nur openssh-7.8p1.orig/readconf.c openssh-7.8p1/readconf.c ---- openssh-7.8p1.orig/readconf.c 2018-10-24 14:09:16.656775296 +0200 -+++ openssh-7.8p1/readconf.c 2018-10-24 14:09:54.246377641 +0200 -@@ -2013,13 +2013,13 @@ +diff -Nur openssh-8.0p1.orig/readconf.c openssh-8.0p1/readconf.c +--- openssh-8.0p1.orig/readconf.c 2020-11-30 06:45:06.326912822 +0100 ++++ openssh-8.0p1/readconf.c 2020-11-30 06:46:23.901105284 +0100 +@@ -2052,11 +2052,11 @@ if (options->challenge_response_authentication == -1) options->challenge_response_authentication = 1; if (options->gss_authentication == -1) @@ -1945,15 +1814,12 @@ diff -Nur openssh-7.8p1.orig/readconf.c openssh-7.8p1/readconf.c - options->gss_deleg_creds = 0; + options->gss_deleg_creds = 1; if (options->gss_trust_dns == -1) -- options->gss_trust_dns = 0; -+ options->gss_trust_dns = 1; + options->gss_trust_dns = 0; if (options->gss_renewal_rekey == -1) - options->gss_renewal_rekey = 0; - #ifdef GSSAPI -diff -Nur openssh-7.8p1.orig/readconf.h openssh-7.8p1/readconf.h ---- openssh-7.8p1.orig/readconf.h 2018-10-24 14:09:16.522776714 +0200 -+++ openssh-7.8p1/readconf.h 2018-10-24 14:09:54.247377631 +0200 -@@ -77,6 +77,8 @@ +diff -Nur openssh-8.0p1.orig/readconf.h openssh-8.0p1/readconf.h +--- openssh-8.0p1.orig/readconf.h 2020-11-30 06:45:06.163912411 +0100 ++++ openssh-8.0p1/readconf.h 2020-11-30 06:46:23.901105284 +0100 +@@ -78,6 +78,8 @@ 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. */ @@ -1962,9 +1828,9 @@ diff -Nur openssh-7.8p1.orig/readconf.h openssh-7.8p1/readconf.h int escape_char; /* Escape character; -2 = none */ u_int num_system_hostfiles; /* Paths for /etc/ssh/ssh_known_hosts */ -diff -Nur openssh-7.8p1.orig/servconf.c openssh-7.8p1/servconf.c ---- openssh-7.8p1.orig/servconf.c 2018-10-24 14:09:16.672775127 +0200 -+++ openssh-7.8p1/servconf.c 2018-10-24 14:09:54.247377631 +0200 +diff -Nur openssh-8.0p1.orig/servconf.c openssh-8.0p1/servconf.c +--- openssh-8.0p1.orig/servconf.c 2020-11-30 06:45:06.339912855 +0100 ++++ openssh-8.0p1/servconf.c 2020-11-30 06:46:23.902105287 +0100 @@ -84,6 +84,7 @@ /* Portable-specific options */ @@ -1975,7 +1841,7 @@ diff -Nur openssh-7.8p1.orig/servconf.c openssh-7.8p1/servconf.c options->num_ports = 0; @@ -127,9 +128,11 @@ options->kerberos_get_afs_token = -1; - options->kerberos_unique_ticket = -1; + options->kerberos_unique_ccache = -1; options->gss_authentication=-1; + options->gss_deleg_creds = -1; options->gss_keyex = -1; @@ -1985,16 +1851,7 @@ diff -Nur openssh-7.8p1.orig/servconf.c openssh-7.8p1/servconf.c options->gss_store_rekey = -1; options->gss_kex_algorithms = NULL; options->use_kuserok = -1; -@@ -177,6 +180,8 @@ - options->chroot_directory = NULL; - options->authorized_keys_command = NULL; - options->authorized_keys_command_user = NULL; -+ options->disable_usage_stats = 1; -+ options->usage_stats_targets = NULL; - options->revoked_keys_file = NULL; - options->trusted_user_ca_keys = NULL; - options->authorized_principals_file = NULL; -@@ -269,6 +274,8 @@ +@@ -286,6 +289,8 @@ /* Portable-specific options */ if (options->use_pam == -1) options->use_pam = 0; @@ -2003,9 +1860,9 @@ diff -Nur openssh-7.8p1.orig/servconf.c openssh-7.8p1/servconf.c /* Standard Options */ if (options->num_host_key_files == 0) { -@@ -346,13 +353,17 @@ - if (options->kerberos_unique_ticket == -1) - options->kerberos_unique_ticket = 0; +@@ -363,13 +368,17 @@ + if (options->kerberos_unique_ccache == -1) + options->kerberos_unique_ccache = 0; if (options->gss_authentication == -1) - options->gss_authentication = 0; + options->gss_authentication = 1; @@ -2023,7 +1880,7 @@ diff -Nur openssh-7.8p1.orig/servconf.c openssh-7.8p1/servconf.c if (options->gss_store_rekey == -1) options->gss_store_rekey = 0; #ifdef GSSAPI -@@ -487,7 +498,7 @@ +@@ -501,7 +510,7 @@ typedef enum { sBadOption, /* == unknown option */ /* Portable-specific options */ @@ -2032,7 +1889,7 @@ diff -Nur openssh-7.8p1.orig/servconf.c openssh-7.8p1/servconf.c /* Standard Options */ sPort, sHostKeyFile, sLoginGraceTime, sPermitRootLogin, sLogFacility, sLogLevel, -@@ -509,10 +520,14 @@ +@@ -523,6 +532,9 @@ sHostbasedUsesNameFromPacketOnly, sHostbasedAcceptedKeyTypes, sHostKeyAlgorithms, sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile, @@ -2040,14 +1897,9 @@ diff -Nur openssh-7.8p1.orig/servconf.c openssh-7.8p1/servconf.c + sGssCredsPath, + sGsiAllowLimitedProxy, sGssAuthentication, sGssCleanupCreds, sGssEnablek5users, sGssStrictAcceptor, - sGssKeyEx, sGssStoreRekey, sGssKexAlgorithms, sAcceptEnv, sSetEnv, sPermitTunnel, - sMatch, sPermitOpen, sPermitListen, sForceCommand, sChrootDirectory, - sUsePrivilegeSeparation, sAllowAgentForwarding, -+ sDisUsageStats, sUsageStatsTarg, - sHostCertificate, - sRevokedKeys, sTrustedUserCAKeys, sAuthorizedPrincipalsFile, - sAuthorizedPrincipalsCommand, sAuthorizedPrincipalsCommandUser, -@@ -538,8 +553,10 @@ + sGssKeyEx, sGssKexAlgorithms, sGssStoreRekey, + sAcceptEnv, sSetEnv, sPermitTunnel, +@@ -553,8 +565,10 @@ /* Portable-specific options */ #ifdef USE_PAM { "usepam", sUsePAM, SSHCFG_GLOBAL }, @@ -2058,12 +1910,13 @@ diff -Nur openssh-7.8p1.orig/servconf.c openssh-7.8p1/servconf.c #endif { "pamauthenticationviakbdint", sDeprecated, SSHCFG_GLOBAL }, /* Standard Options */ -@@ -587,7 +604,14 @@ +@@ -602,8 +616,15 @@ { "afstokenpassing", sUnsupported, SSHCFG_GLOBAL }, #ifdef GSSAPI { "gssapiauthentication", sGssAuthentication, SSHCFG_ALL }, + { "gssapidelegatecredentials", sGssDelegateCreds, SSHCFG_ALL }, { "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_GLOBAL }, + { "gssapicleanupcreds", sGssCleanupCreds, SSHCFG_GLOBAL }, + { "gssapicredentialspath", sGssCredsPath, SSHCFG_GLOBAL }, +#ifdef GSI + { "gsiallowlimitedproxy", sGsiAllowLimitedProxy, SSHCFG_GLOBAL }, @@ -2073,27 +1926,28 @@ diff -Nur openssh-7.8p1.orig/servconf.c openssh-7.8p1/servconf.c { "gssapistrictacceptorcheck", sGssStrictAcceptor, SSHCFG_GLOBAL }, { "gssapikeyexchange", sGssKeyEx, SSHCFG_GLOBAL }, { "gssapistorecredentialsonrekey", sGssStoreRekey, SSHCFG_GLOBAL }, -@@ -595,7 +619,10 @@ - { "gssapikexalgorithms", sGssKexAlgorithms, SSHCFG_GLOBAL }, +@@ -611,8 +632,11 @@ + { "gssapienablek5users", sGssEnablek5users, SSHCFG_ALL }, #else { "gssapiauthentication", sUnsupported, SSHCFG_ALL }, + { "gssapidelegatecredentials", sUnsupported, SSHCFG_ALL }, { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL }, + { "gssapicleanupcreds", sUnsupported, SSHCFG_GLOBAL }, + { "gssapicredentialspath", sUnsupported, SSHCFG_GLOBAL }, + { "gsiallowlimitedproxy", sUnsupported, SSHCFG_GLOBAL }, { "gssapistrictacceptorcheck", sUnsupported, SSHCFG_GLOBAL }, { "gssapikeyexchange", sUnsupported, SSHCFG_GLOBAL }, { "gssapistorecredentialsonrekey", sUnsupported, SSHCFG_GLOBAL }, -@@ -666,6 +693,8 @@ +@@ -682,6 +706,8 @@ { "permitlisten", sPermitListen, SSHCFG_ALL }, { "forcecommand", sForceCommand, SSHCFG_ALL }, { "chrootdirectory", sChrootDirectory, SSHCFG_ALL }, -+ { "disableusagestats", sDisUsageStats, SSHCFG_GLOBAL}, -+ { "usagestatstargets", sUsageStatsTarg, SSHCFG_GLOBAL}, ++ { "disableusagestats", sUnsupported, SSHCFG_GLOBAL}, ++ { "usagestatstargets", sUnsupported, SSHCFG_GLOBAL}, { "hostcertificate", sHostCertificate, SSHCFG_GLOBAL }, { "revokedkeys", sRevokedKeys, SSHCFG_ALL }, { "trustedusercakeys", sTrustedUserCAKeys, SSHCFG_ALL }, -@@ -1296,6 +1325,10 @@ +@@ -1317,6 +1343,10 @@ intptr = &options->use_pam; goto parse_flag; @@ -2104,7 +1958,7 @@ diff -Nur openssh-7.8p1.orig/servconf.c openssh-7.8p1/servconf.c /* Standard Options */ case sBadOption: return -1; -@@ -1510,6 +1543,10 @@ +@@ -1537,6 +1567,10 @@ intptr = &options->gss_authentication; goto parse_flag; @@ -2115,7 +1969,7 @@ diff -Nur openssh-7.8p1.orig/servconf.c openssh-7.8p1/servconf.c case sGssKeyEx: intptr = &options->gss_keyex; goto parse_flag; -@@ -1518,6 +1555,10 @@ +@@ -1545,6 +1579,10 @@ intptr = &options->gss_cleanup_creds; goto parse_flag; @@ -2126,7 +1980,7 @@ diff -Nur openssh-7.8p1.orig/servconf.c openssh-7.8p1/servconf.c case sGssStrictAcceptor: intptr = &options->gss_strict_acceptor; goto parse_flag; -@@ -1538,6 +1579,12 @@ +@@ -1565,6 +1603,12 @@ options->gss_kex_algorithms = xstrdup(arg); break; @@ -2139,43 +1993,7 @@ diff -Nur openssh-7.8p1.orig/servconf.c openssh-7.8p1/servconf.c case sPasswordAuthentication: intptr = &options->password_authentication; goto parse_flag; -@@ -2051,6 +2098,35 @@ - *charptr = xstrdup(arg); - break; - -+ case sDisUsageStats: -+ arg = strdelim(&cp); -+ if (!arg || *arg == '\0') -+ fatal("%s line %d: missing value.", -+ filename, linenum); -+ if (!strcasecmp(arg, "true") || -+ !strcasecmp(arg, "enabled") || -+ !strcasecmp(arg, "yes") || -+ !strcasecmp(arg, "on") || -+ !strcasecmp(arg, "1")) -+ options->disable_usage_stats = 1; -+ else if (!strcasecmp(arg, "false") || -+ !strcasecmp(arg, "disabled") || -+ !strcasecmp(arg, "no") || -+ !strcasecmp(arg, "off") || -+ !strcasecmp(arg, "0")) -+ options->disable_usage_stats = 0; -+ else -+ fatal("Incorrect value for disable_usage_stats"); -+ break; -+ -+ case sUsageStatsTarg: -+ arg = strdelim(&cp); -+ if (!arg || *arg == '\0') -+ fatal("%s line %d: missing value.", -+ filename, linenum); -+ options->usage_stats_targets = xstrdup(arg); -+ break; -+ - case sTrustedUserCAKeys: - charptr = &options->trusted_user_ca_keys; - goto parse_filename; -@@ -2345,6 +2421,7 @@ +@@ -2368,6 +2412,7 @@ M_CP_INTOPT(password_authentication); M_CP_INTOPT(gss_authentication); @@ -2183,10 +2001,10 @@ diff -Nur openssh-7.8p1.orig/servconf.c openssh-7.8p1/servconf.c M_CP_INTOPT(pubkey_authentication); M_CP_INTOPT(kerberos_authentication); M_CP_INTOPT(hostbased_authentication); -diff -Nur openssh-7.8p1.orig/servconf.h openssh-7.8p1/servconf.h ---- openssh-7.8p1.orig/servconf.h 2018-10-24 14:09:16.535776576 +0200 -+++ openssh-7.8p1/servconf.h 2018-10-24 14:09:54.248377620 +0200 -@@ -129,9 +129,12 @@ +diff -Nur openssh-8.0p1.orig/servconf.h openssh-8.0p1/servconf.h +--- openssh-8.0p1.orig/servconf.h 2020-11-30 06:45:06.227912573 +0100 ++++ openssh-8.0p1/servconf.h 2020-11-30 06:46:23.903105289 +0100 +@@ -131,9 +131,12 @@ * be stored in per-session ccache */ int use_kuserok; int enable_k5users; @@ -2199,7 +2017,7 @@ diff -Nur openssh-7.8p1.orig/servconf.h openssh-7.8p1/servconf.h int gss_strict_acceptor; /* If true, restrict the GSSAPI acceptor name */ int gss_store_rekey; char *gss_kex_algorithms; /* GSSAPI kex methods to be offered by client. */ -@@ -191,6 +194,7 @@ +@@ -192,6 +195,7 @@ char *adm_forced_command; int use_pam; /* Enable auth via PAM */ @@ -2207,21 +2025,10 @@ diff -Nur openssh-7.8p1.orig/servconf.h openssh-7.8p1/servconf.h int permit_tun; -@@ -200,6 +204,10 @@ - u_int num_permitted_listens; - - char *chroot_directory; -+ -+ int disable_usage_stats; -+ char *usage_stats_targets; -+ - char *revoked_keys_file; - char *trusted_user_ca_keys; - char *authorized_keys_command; -diff -Nur openssh-7.8p1.orig/ssh.1 openssh-7.8p1/ssh.1 ---- openssh-7.8p1.orig/ssh.1 2018-10-24 14:09:16.525776682 +0200 -+++ openssh-7.8p1/ssh.1 2018-10-24 14:09:54.249377610 +0200 -@@ -1423,6 +1423,18 @@ +diff -Nur openssh-8.0p1.orig/ssh.1 openssh-8.0p1/ssh.1 +--- openssh-8.0p1.orig/ssh.1 2020-11-30 06:45:06.284912716 +0100 ++++ openssh-8.0p1/ssh.1 2020-11-30 06:46:23.904105291 +0100 +@@ -1427,6 +1427,18 @@ on to new connections). .It Ev USER Set to the name of the user logging in. @@ -2240,10 +2047,10 @@ diff -Nur openssh-7.8p1.orig/ssh.1 openssh-7.8p1/ssh.1 .El .Pp Additionally, -diff -Nur openssh-7.8p1.orig/ssh.c openssh-7.8p1/ssh.c ---- openssh-7.8p1.orig/ssh.c 2018-10-24 14:09:16.690774937 +0200 -+++ openssh-7.8p1/ssh.c 2018-10-24 14:09:54.249377610 +0200 -@@ -541,6 +541,32 @@ +diff -Nur openssh-8.0p1.orig/ssh.c openssh-8.0p1/ssh.c +--- openssh-8.0p1.orig/ssh.c 2020-11-30 06:45:06.328912827 +0100 ++++ openssh-8.0p1/ssh.c 2020-11-30 06:46:23.904105291 +0100 +@@ -542,6 +542,38 @@ fatal("Can't open user config file %.100s: " "%.100s", config, strerror(errno)); } else { @@ -2259,25 +2066,31 @@ diff -Nur openssh-7.8p1.orig/ssh.c openssh-7.8p1/ssh.c + r = snprintf(buf, sizeof buf, "%s/%s.gssapi", pw->pw_dir, + _PATH_SSH_USER_CONFFILE); + if (r > 0 && (size_t)r < sizeof(buf)) -+ (void)read_config_file(buf, pw, host, host_arg, &options, 1); ++ (void)read_config_file(buf, pw, host, host_arg, ++ &options, SSHCONF_CHECKPERM | SSHCONF_USERCONF | ++ (final_pass ? SSHCONF_FINAL : 0), want_final_pass); +#ifdef GSI + r = snprintf(buf, sizeof buf, "%s/%s.gsi", pw->pw_dir, + _PATH_SSH_USER_CONFFILE); + if (r > 0 && (size_t)r < sizeof(buf)) -+ (void)read_config_file(buf, pw, host, host_arg, &options, 1); ++ (void)read_config_file(buf, pw, host, host_arg, ++ &options, SSHCONF_CHECKPERM | SSHCONF_USERCONF | ++ (final_pass ? SSHCONF_FINAL : 0), want_final_pass); +#endif +#if defined(KRB5) + r = snprintf(buf, sizeof buf, "%s/%s.krb", pw->pw_dir, + _PATH_SSH_USER_CONFFILE); + if (r > 0 && (size_t)r < sizeof(buf)) -+ (void)read_config_file(buf, pw, host, host_arg, &options, 1); ++ (void)read_config_file(buf, pw, host, host_arg, ++ &options, SSHCONF_CHECKPERM | SSHCONF_USERCONF | ++ (final_pass ? SSHCONF_FINAL : 0), want_final_pass); +#endif +#endif r = snprintf(buf, sizeof buf, "%s/%s", pw->pw_dir, _PATH_SSH_USER_CONFFILE); if (r > 0 && (size_t)r < sizeof(buf)) -@@ -1283,8 +1309,12 @@ - logit("FIPS mode initialized"); +@@ -1288,8 +1320,12 @@ + tty_flag = 0; } - if (options.user == NULL) @@ -2290,27 +2103,25 @@ diff -Nur openssh-7.8p1.orig/ssh.c openssh-7.8p1/ssh.c /* Set up strings used to percent_expand() arguments */ if (gethostname(thishost, sizeof(thishost)) == -1) -diff -Nur openssh-7.8p1.orig/ssh_config openssh-7.8p1/ssh_config ---- openssh-7.8p1.orig/ssh_config 2018-10-24 14:09:16.427777719 +0200 -+++ openssh-7.8p1/ssh_config 2018-10-24 14:09:54.249377610 +0200 -@@ -22,10 +22,10 @@ +diff -Nur openssh-8.0p1.orig/ssh_config openssh-8.0p1/ssh_config +--- openssh-8.0p1.orig/ssh_config 2020-11-30 06:45:06.166912418 +0100 ++++ openssh-8.0p1/ssh_config 2020-11-30 06:46:23.905105294 +0100 +@@ -22,9 +22,9 @@ # ForwardX11 no # PasswordAuthentication yes # HostbasedAuthentication no -# GSSAPIAuthentication no -# GSSAPIDelegateCredentials no -# GSSAPIKeyExchange no --# GSSAPITrustDNS no +# GSSAPIAuthentication yes +# GSSAPIDelegateCredentials yes +# GSSAPIKeyExchange yes -+# GSSAPITrustDNS yes + # GSSAPITrustDNS no # BatchMode no # CheckHostIP yes - # AddressFamily any -diff -Nur openssh-7.8p1.orig/ssh_config.5 openssh-7.8p1/ssh_config.5 ---- openssh-7.8p1.orig/ssh_config.5 2018-10-24 14:09:16.599775899 +0200 -+++ openssh-7.8p1/ssh_config.5 2018-10-24 14:09:54.250377599 +0200 +diff -Nur openssh-8.0p1.orig/ssh_config.5 openssh-8.0p1/ssh_config.5 +--- openssh-8.0p1.orig/ssh_config.5 2020-11-30 06:45:06.264912666 +0100 ++++ openssh-8.0p1/ssh_config.5 2020-11-30 06:46:23.906105296 +0100 @@ -52,6 +52,12 @@ user's configuration file .Pq Pa ~/.ssh/config @@ -2324,16 +2135,16 @@ diff -Nur openssh-7.8p1.orig/ssh_config.5 openssh-7.8p1/ssh_config.5 system-wide configuration file .Pq Pa /etc/ssh/ssh_config .El -@@ -717,7 +723,7 @@ +@@ -751,7 +757,7 @@ .It Cm GSSAPIAuthentication Specifies whether user authentication based on GSSAPI is allowed. The default is -.Cm no . +.Cm yes . .It Cm GSSAPIClientIdentity - If set, specifies the GSSAPI client identity that ssh should use when - connecting to the server. The default is unset, which means that the default -@@ -725,12 +731,12 @@ + If set, specifies the GSSAPI client identity that ssh should use when + connecting to the server. The default is unset, which means that the default +@@ -759,12 +765,12 @@ .It Cm GSSAPIDelegateCredentials Forward (delegate) credentials to the server. The default is @@ -2346,18 +2157,9 @@ diff -Nur openssh-7.8p1.orig/ssh_config.5 openssh-7.8p1/ssh_config.5 -.Dq no . +.Dq yes . .It Cm GSSAPIRenewalForcesRekey - If set to + If set to .Dq yes -@@ -762,7 +768,7 @@ - the hostname entered on the - command line will be passed untouched to the GSSAPI library. - The default is --.Dq no . -+.Dq yes . - .It Cm GSSAPIKexAlgorithms - The list of key exchange algorithms that are offered for GSSAPI - key exchange. Possible values are -@@ -1227,7 +1233,7 @@ +@@ -1263,7 +1269,7 @@ .Cm password ) . The default is: .Bd -literal -offset indent @@ -2366,22 +2168,22 @@ diff -Nur openssh-7.8p1.orig/ssh_config.5 openssh-7.8p1/ssh_config.5 keyboard-interactive,password .Ed .It Cm ProxyCommand -diff -Nur openssh-7.8p1.orig/sshconnect2.c openssh-7.8p1/sshconnect2.c ---- openssh-7.8p1.orig/sshconnect2.c 2018-10-24 14:09:16.658775275 +0200 -+++ openssh-7.8p1/sshconnect2.c 2018-10-24 14:09:54.257377525 +0200 -@@ -863,6 +863,11 @@ - int r, ok = 0; - const char *gss_host = NULL; +diff -Nur openssh-8.0p1.orig/sshconnect2.c openssh-8.0p1/sshconnect2.c +--- openssh-8.0p1.orig/sshconnect2.c 2020-11-30 06:45:06.329912830 +0100 ++++ openssh-8.0p1/sshconnect2.c 2020-11-30 06:53:53.185127645 +0100 +@@ -784,6 +784,11 @@ + gss_OID mech = NULL; + char *gss_host = NULL; + if (!options.gss_authentication) { + verbose("GSSAPI authentication disabled."); + return 0; + } + - if (options.gss_server_identity) - gss_host = options.gss_server_identity; - else if (options.gss_trust_dns) { -@@ -956,7 +961,8 @@ + if (options.gss_server_identity) { + gss_host = xstrdup(options.gss_server_identity); + } else if (options.gss_trust_dns) { +@@ -892,7 +897,8 @@ if (status == GSS_S_COMPLETE) { /* send either complete or MIC, depending on mechanism */ @@ -2391,7 +2193,7 @@ diff -Nur openssh-7.8p1.orig/sshconnect2.c openssh-7.8p1/sshconnect2.c if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE)) != 0 || (r = sshpkt_send(ssh)) != 0) -@@ -1126,6 +1132,20 @@ +@@ -1062,6 +1068,20 @@ return r; } @@ -2410,9 +2212,9 @@ diff -Nur openssh-7.8p1.orig/sshconnect2.c openssh-7.8p1/sshconnect2.c +#endif + int - userauth_gsskeyex(Authctxt *authctxt) + userauth_gsskeyex(struct ssh *ssh) { -@@ -1146,6 +1166,11 @@ +@@ -1084,6 +1104,11 @@ if ((b = sshbuf_new()) == NULL) fatal("%s: sshbuf_new failed", __func__); @@ -2424,24 +2226,21 @@ diff -Nur openssh-7.8p1.orig/sshconnect2.c openssh-7.8p1/sshconnect2.c ssh_gssapi_buildmic(b, authctxt->server_user, authctxt->service, "gssapi-keyex"); -@@ -1158,7 +1183,13 @@ +@@ -1097,7 +1122,9 @@ } - packet_start(SSH2_MSG_USERAUTH_REQUEST); -+#ifdef GSI -+ if (options.implicit && is_gsi_oid(gss_kex_context->oid)) -+ packet_put_cstring(""); -+ else -+#endif - packet_put_cstring(authctxt->server_user); -+ - packet_put_cstring(authctxt->service); - packet_put_cstring(authctxt->method->name); - packet_put_string(mic.value, mic.length); -diff -Nur openssh-7.8p1.orig/sshd.8 openssh-7.8p1/sshd.8 ---- openssh-7.8p1.orig/sshd.8 2018-10-24 14:09:16.457777401 +0200 -+++ openssh-7.8p1/sshd.8 2018-10-24 14:09:54.258377514 +0200 -@@ -788,6 +788,44 @@ + if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_REQUEST)) != 0 || +- (r = sshpkt_put_cstring(ssh, authctxt->server_user)) != 0 || ++ (r = sshpkt_put_cstring(ssh, ++ (options.implicit && is_gsi_oid(gss_kex_context->oid)) ? ++ "" : authctxt->server_user)) != 0 || + (r = sshpkt_put_cstring(ssh, authctxt->service)) != 0 || + (r = sshpkt_put_cstring(ssh, authctxt->method->name)) != 0 || + (r = sshpkt_put_string(ssh, mic.value, mic.length)) != 0 || +diff -Nur openssh-8.0p1.orig/sshd.8 openssh-8.0p1/sshd.8 +--- openssh-8.0p1.orig/sshd.8 2020-11-30 06:45:06.284912716 +0100 ++++ openssh-8.0p1/sshd.8 2020-11-30 06:46:23.909105304 +0100 +@@ -788,6 +788,29 @@ # A CA key, accepted for any host in *.mydomain.com or *.mydomain.org @cert-authority *.mydomain.org,*.mydomain.com ssh-rsa AAAAB5W... .Ed @@ -2449,21 +2248,6 @@ diff -Nur openssh-7.8p1.orig/sshd.8 openssh-7.8p1/sshd.8 +.Nm +will normally set the following environment variables: +.Bl -tag -width "SSH_ORIGINAL_COMMAND" -+.It Ev GLOBUS_USAGE_OPTOUT -+Setting this environment variable to "1" will disable the reporting -+of usage metrics. Usage metrics can also be disabled using the -+.Cm DisableUsageStats -+setting in -+.Xr sshd_config 5 . -+.It Ev GLOBUS_USAGE_TARGETS -+If -+.Cm UsageStatsTargets -+is not specified in -+.Xr sshd_config 5 , -+a comma-separated list of targets (without any tags specified) if -+specified in the environment variable -+.Ev GLOBUS_USAGE_TARGETS -+will be used. +.It Ev GRIDMAP +Applies to GSI authentication/authorization. Specifies the location of the +gridmapfile. If not specified, the gridmap file is assumed to be available at @@ -2486,32 +2270,10 @@ diff -Nur openssh-7.8p1.orig/sshd.8 openssh-7.8p1/sshd.8 .Sh FILES .Bl -tag -width Ds -compact .It Pa ~/.hushlogin -diff -Nur openssh-7.8p1.orig/sshd.c openssh-7.8p1/sshd.c ---- openssh-7.8p1.orig/sshd.c 2018-10-24 14:09:16.691774926 +0200 -+++ openssh-7.8p1/sshd.c 2018-10-24 14:09:54.259377504 +0200 -@@ -130,6 +130,7 @@ - #include "auth-options.h" - #include "version.h" - #include "ssherr.h" -+#include "ssh-globus-usage.h" - - /* Re-exec fds */ - #define REEXEC_DEVCRYPTO_RESERVED_FD (STDERR_FILENO + 1) -@@ -1797,6 +1798,13 @@ - /* Fill in default values for those options not explicitly set. */ - fill_default_server_options(&options); - -+#ifdef HAVE_GLOBUS_USAGE -+ if (ssh_usage_stats_init(options.disable_usage_stats, -+ options.usage_stats_targets) != GLOBUS_SUCCESS) { -+ error("Error initializing Globus Usage Metrics, but continuing ..."); -+ } -+#endif /* HAVE_GLOBUS_USAGE */ -+ - /* challenge-response is implemented via keyboard interactive */ - if (options.challenge_response_authentication) - options.kbd_interactive_authentication = 1; -@@ -2305,7 +2313,7 @@ +diff -Nur openssh-8.0p1.orig/sshd.c openssh-8.0p1/sshd.c +--- openssh-8.0p1.orig/sshd.c 2020-11-30 06:45:06.342912863 +0100 ++++ openssh-8.0p1/sshd.c 2020-11-30 06:46:23.909105304 +0100 +@@ -2253,7 +2253,7 @@ #endif #ifdef GSSAPI @@ -2520,10 +2282,10 @@ diff -Nur openssh-7.8p1.orig/sshd.c openssh-7.8p1/sshd.c temporarily_use_uid(authctxt->pw); authctxt->krb5_set_env = ssh_gssapi_storecreds(); restore_uid(); -diff -Nur openssh-7.8p1.orig/sshd_config openssh-7.8p1/sshd_config ---- openssh-7.8p1.orig/sshd_config 2018-10-24 14:09:16.509776851 +0200 -+++ openssh-7.8p1/sshd_config 2018-10-24 14:09:54.259377504 +0200 -@@ -84,10 +84,11 @@ +diff -Nur openssh-8.0p1.orig/sshd_config openssh-8.0p1/sshd_config +--- openssh-8.0p1.orig/sshd_config 2020-11-30 06:45:06.219912552 +0100 ++++ openssh-8.0p1/sshd_config 2020-11-30 06:46:23.910105306 +0100 +@@ -81,10 +81,11 @@ #KerberosUseKuserok yes # GSSAPI options @@ -2537,7 +2299,7 @@ diff -Nur openssh-7.8p1.orig/sshd_config openssh-7.8p1/sshd_config #GSSAPIEnablek5users no # Set this to 'yes' to enable PAM authentication, account processing, -@@ -103,6 +104,10 @@ +@@ -100,6 +101,10 @@ # problems. UsePAM yes @@ -2548,37 +2310,10 @@ diff -Nur openssh-7.8p1.orig/sshd_config openssh-7.8p1/sshd_config #AllowAgentForwarding yes #AllowTcpForwarding yes #GatewayPorts no -@@ -141,6 +146,10 @@ - # override default of no subsystems - Subsystem sftp /usr/libexec/sftp-server - -+# Usage Metrics -+#UsageStatsTargets usage-stats.example.edu:4810 -+#DisableUsageStats yes -+ - # Example of overriding settings on a per-user basis - #Match User anoncvs - # X11Forwarding no -diff -Nur openssh-7.8p1.orig/sshd_config.5 openssh-7.8p1/sshd_config.5 ---- openssh-7.8p1.orig/sshd_config.5 2018-10-24 14:09:16.562776291 +0200 -+++ openssh-7.8p1/sshd_config.5 2018-10-24 14:09:54.259377504 +0200 -@@ -576,6 +576,15 @@ - TCP and StreamLocal. - This option overrides all other forwarding-related options and may - simplify restricted configurations. -+.It Cm DisableUsageStats -+This keyword can be followed by one of the keywords "true", "enabled", "yes", -+"on" or "1" to disable reporting of usage metrics. Or it can be set to "false", -+"disabled", "no", "off", "0" to enable reporting of usage metrics. Setting the -+.Cm GLOBUS_USAGE_OPTOUT -+environment variable to "1" will also disable the reporting of usage metrics. -+Disabling reporting of usage metrics will cause the -+.Cm UsageStatsTargets -+setting to be ignored. - .It Cm ExposeAuthInfo - Writes a temporary file containing a list of authentication methods and - public credentials (e.g. keys) used to authenticate the user. -@@ -633,15 +642,34 @@ +diff -Nur openssh-8.0p1.orig/sshd_config.5 openssh-8.0p1/sshd_config.5 +--- openssh-8.0p1.orig/sshd_config.5 2020-11-30 06:45:06.297912749 +0100 ++++ openssh-8.0p1/sshd_config.5 2020-11-30 06:46:23.911105308 +0100 +@@ -642,15 +642,34 @@ to allow the client to select the address to which the forwarding is bound. The default is .Cm no . @@ -2614,128 +2349,16 @@ diff -Nur openssh-7.8p1.orig/sshd_config.5 openssh-7.8p1/sshd_config.5 .It Cm GSSAPIEnablek5users Specifies whether to look at .k5users file for GSSAPI authentication access control. Further details are described in -@@ -652,7 +680,7 @@ +@@ -661,7 +680,7 @@ Specifies whether key exchange based on GSSAPI is allowed. GSSAPI key exchange doesn't rely on ssh keys to verify host identity. The default is --.Dq no . -+.Dq yes . +-.Cm no . ++.Cm yes . .It Cm GSSAPIStrictAcceptorCheck Determines whether to be strict about the identity of the GSSAPI acceptor a client authenticates against. -@@ -691,7 +719,6 @@ - .Pp - The default is - .Dq gss-gex-sha1-,gss-group14-sha1- . --This option only applies to protocol version 2 connections using GSSAPI. - .It Cm HostbasedAcceptedKeyTypes - Specifies the key types that will be accepted for hostbased authentication - as a list of comma-separated patterns. -@@ -1627,6 +1654,103 @@ - .Cm TrustedUserCAKeys . - For more details on certificates, see the CERTIFICATES section in - .Xr ssh-keygen 1 . -+.It Cm UsageStatsTargets -+This option can be used to specify the target collector hosts to which usage -+metrics should be reported. This setting will be ignored if -+.Cm DisableUsageStats -+is enabled. Multiple targets can be specified separated by comma(s), but no -+space(s). Each target specification is of the format -+.Pa host:port[!tags]. -+Tags control what data elements are reported. The following list specifies -+the tags for the corresponding data elements. -+.Pp -+.Bl -item -offset indent -compact -+.It -+.Cm V -+.Sm off -+- OpenSSH version, reported by default. -+.Sm on -+.It -+.Cm v -+.Sm off -+- SSL version, reported by default. -+.Sm on -+.It -+.Cm M -+.Sm off -+- User authentication method used such as "gssapi-keyex", "gssapi-with-mic", etc. Reported by default. -+.Sm on -+.It -+.Cm m -+.Sm off -+- User authentication mechanism used such as "GSI", "Kerberos", etc. Reported by default. -+.Sm on -+.It -+.Cm I -+.Sm off -+- Client IP address. Not reported by default. -+.Sm on -+.It -+.Cm u -+.Sm off -+- User name. Not reported by default. -+.Sm on -+.It -+.Cm U -+.Sm off -+- User DN. Not reported by default. -+.Sm on -+.Pp -+In addition to the above selected information, the following data are -+reported to ALL the specified/default target collectors. There's no way to -+exclude these from being reported other than by disabling the reporting of -+usage metrics altogether: -+.Pp -+.It -+.Cm Component code -+.Sm off -+- 12 for GSI OpenSSH -+.Sm on -+.It -+.Cm Component Data Format version -+.Sm off -+- 0 currently -+.Sm on -+.It -+.Cm IP Address -+.Sm off -+- IP address of reporting server -+.Sm on -+.It -+.Cm Timestamp -+.It -+.Cm Hostname -+.Sm off -+- Host name of reporting server -+.Sm on -+.Pp -+If no tags are specified in a host spec, or the special string -+.Cm default -+is specified, the tags -+.Cm VvMm -+are assumed. A site could choose to allow a -+different set of data to be reported by specifying a different tag set. The -+last 3 tags -+.Cm I , -+.Cm u -+and -+.Cm U -+above are more meant for a local collector that a -+site might like to deploy since they could be construed as private information. -+The special string -+.Cm all -+denotes all tags. -+.El -+.Pp -+Usage Metrics reporting is disabled unless -+.Cm UsageStatsTargets -+is specified. -+.Pp - .It Cm UseDNS - Specifies whether - .Xr sshd 8 -@@ -1668,6 +1792,12 @@ +@@ -1656,6 +1675,12 @@ as a non-root user. The default is .Cm no . @@ -2748,470 +2371,10 @@ diff -Nur openssh-7.8p1.orig/sshd_config.5 openssh-7.8p1/sshd_config.5 .It Cm VersionAddendum Optionally specifies additional text to append to the SSH protocol banner sent by the server upon connection. -diff -Nur openssh-7.8p1.orig/ssh-globus-usage.c openssh-7.8p1/ssh-globus-usage.c ---- openssh-7.8p1.orig/ssh-globus-usage.c 1970-01-01 01:00:00.000000000 +0100 -+++ openssh-7.8p1/ssh-globus-usage.c 2018-10-24 14:09:54.260377493 +0200 -@@ -0,0 +1,396 @@ -+/* -+ * Copyright 2009 The Board of Trustees of the University -+ * of Illinois. See the LICENSE file for detailed license information. -+ * -+ * Portions, specifically log_usage_stats(), ssh_usage_stats_init(), -+ * ssh_usage_stats_close(), ssh_usage_ent_s, ssh_usage_tag_e and -+ * TAG #defines were based on those from Usage Metrics portions of: -+ * gridftp/server/source/globus_i_gfs_log.c -+ * -+ * Copyright 1999-2006 University of Chicago -+ * -+ * Licensed under the Apache License, Version 2.0 (the "License"); -+ * you may not use this file except in compliance with the License. -+ * You may obtain a copy of the License at -+ * -+ * http://www.apache.org/licenses/LICENSE-2.0 -+ * -+ * Unless required by applicable law or agreed to in writing, software -+ * distributed under the License is distributed on an "AS IS" BASIS, -+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -+ * See the License for the specific language governing permissions and -+ * limitations under the License. -+ */ -+ -+#include "includes.h" -+ -+#ifdef HAVE_GLOBUS_USAGE -+ -+#include -+#include -+ -+#include "log.h" -+#include "ssh-globus-usage.h" -+ -+static globus_list_t *usage_handle_list = NULL; -+ -+#define SSH_GLOBUS_USAGE_ID 12 -+#define SSH_GLOBUS_USAGE_VER 0 -+ -+#define SSH_GLOBUS_DEFAULT_TAGLIST "VvMm" -+#define SSH_GLOBUS_ALL_TAGLIST "VvMmIuU" -+#define SSH_GLOBUS_TAGCOUNT 25 -+ -+typedef enum ssh_usage_tag_e -+{ -+ SSH_GLOBUS_USAGE_SSH_VER = 'V', -+ SSH_GLOBUS_USAGE_SSL_VER = 'v', -+ SSH_GLOBUS_USAGE_METHOD = 'M', -+ SSH_GLOBUS_USAGE_MECHANISM = 'm', -+ SSH_GLOBUS_USAGE_CLIENTIP = 'I', -+ SSH_GLOBUS_USAGE_USERNAME = 'u', -+ SSH_GLOBUS_USAGE_USERDN = 'U' -+ /* !! ADD to ALL_TAGLIST above and to globus_usage_stats_send() -+ invocation below when adding here */ -+} ssh_usage_tag_t; -+ -+typedef struct ssh_usage_ent_s -+{ -+ globus_usage_stats_handle_t handle; -+ char * target; -+ char * taglist; -+} ssh_usage_ent_t; -+ -+ -+globus_result_t -+ssh_usage_stats_init(int disable_usage_stats, char *usage_stats_targets) -+{ -+ globus_result_t result; -+ char * target_str = NULL; -+ char * ptr = ptr; -+ char * target = NULL; -+ char * entry = NULL; -+ globus_list_t * list = NULL; -+ ssh_usage_ent_t * usage_ent = NULL; -+ -+ if (disable_usage_stats || !usage_stats_targets) -+ return GLOBUS_SUCCESS; -+ -+ result = globus_module_activate(GLOBUS_USAGE_MODULE); -+ if (result != GLOBUS_SUCCESS) -+ { -+ error("ERROR: couldn't activate USAGE STATS module"); -+ return result; -+ } -+ -+ target_str = strdup(usage_stats_targets); -+ if (target_str == NULL) -+ { -+ error("ERROR: strdup failure for target_str"); -+ goto error; -+ } -+ debug("Processing usage_stats_target (%s)\n", target_str); -+ -+ if(target_str && (strchr(target_str, ',') || strchr(target_str, '!'))) -+ { -+ target = target_str; -+ -+ do { -+ usage_ent = (ssh_usage_ent_t *) malloc(sizeof(ssh_usage_ent_t)); -+ if (usage_ent == NULL) -+ { -+ error("ERROR: couldn't allocate for ssh_usage_ent_t"); -+ goto error; -+ } -+ -+ if ((ptr = strchr(target, ',')) != NULL) -+ *ptr = '\0'; -+ -+ entry = strdup(target); -+ if (entry == NULL) -+ { -+ error("ERROR: strdup failure for target"); -+ goto error; -+ } -+ -+ if (ptr) -+ target = ptr + 1; -+ else -+ target = NULL; -+ -+ if((ptr = strchr(entry, '!')) != NULL) -+ { -+ *ptr = '\0'; -+ usage_ent->taglist = strdup(ptr + 1); -+ if (usage_ent->taglist == NULL) -+ { -+ error("ERROR: strdup failure for taglist"); -+ goto error; -+ } -+ if(strlen(usage_ent->taglist) > SSH_GLOBUS_TAGCOUNT) -+ { -+ usage_ent->taglist[SSH_GLOBUS_TAGCOUNT + 1] = '\0'; -+ } -+ } -+ else -+ { -+ usage_ent->taglist = strdup(SSH_GLOBUS_DEFAULT_TAGLIST); -+ if (usage_ent->taglist == NULL) -+ { -+ error("ERROR: couldn't allocate for taglist"); -+ goto error; -+ } -+ } -+ -+ if(strcasecmp(usage_ent->taglist, "default") == 0) -+ { -+ free(usage_ent->taglist); -+ usage_ent->taglist = strdup(SSH_GLOBUS_DEFAULT_TAGLIST); -+ if (usage_ent->taglist == NULL) -+ { -+ error("ERROR: couldn't allocate for taglist"); -+ goto error; -+ } -+ } -+ else if(strcasecmp(usage_ent->taglist, "all") == 0) -+ { -+ free(usage_ent->taglist); -+ usage_ent->taglist = strdup(SSH_GLOBUS_ALL_TAGLIST); -+ if (usage_ent->taglist == NULL) -+ { -+ error("ERROR: couldn't allocate for taglist"); -+ goto error; -+ } -+ } -+ -+ usage_ent->target = entry; -+ -+ globus_list_insert(&usage_handle_list, usage_ent); -+ } -+ while(target != NULL); -+ -+ free(target_str); -+ } -+ else -+ { -+ usage_ent = (ssh_usage_ent_t *) malloc(sizeof(ssh_usage_ent_t)); -+ if (usage_ent == NULL) -+ { -+ error("ERROR: couldn't allocate for usage_ent"); -+ goto error; -+ } -+ -+ usage_ent->target = target_str; -+ usage_ent->taglist = strdup(SSH_GLOBUS_DEFAULT_TAGLIST); -+ if (usage_ent->taglist == NULL) -+ { -+ error("ERROR: couldn't allocate for taglist"); -+ goto error; -+ } -+ -+ globus_list_insert(&usage_handle_list, usage_ent); -+ } -+ -+ result = GLOBUS_SUCCESS; -+ for(list = usage_handle_list; -+ !globus_list_empty(list); -+ list = globus_list_rest(list)) -+ { -+ usage_ent = (ssh_usage_ent_t *) globus_list_first(list); -+ -+ usage_ent->handle = NULL; -+ if (globus_usage_stats_handle_init( -+ &usage_ent->handle, -+ SSH_GLOBUS_USAGE_ID, -+ SSH_GLOBUS_USAGE_VER, -+ usage_ent->target) != GLOBUS_SUCCESS) -+ { -+ error("USAGE-STATS: Error initializing (%s) (%s)", -+ usage_ent->target?:"NULL", -+ usage_ent->taglist?:"NULL"); -+ result = GLOBUS_FAILURE; -+ } else -+ debug("USAGE-STATS: Initialized (%s) (%s)", usage_ent->target?:"NULL", -+ usage_ent->taglist?:"NULL"); -+ -+ } -+ -+ return result; -+ -+error: -+ if (target_str) -+ { -+ free(target_str); -+ target_str = NULL; -+ } -+ if (entry) -+ { -+ free(target_str); -+ target_str = NULL; -+ } -+ return GLOBUS_FAILURE; -+} -+ -+void -+ssh_usage_stats_close(int disable_usage_stats) -+{ -+ globus_list_t *list; -+ -+ if (disable_usage_stats) -+ return; -+ -+ list = usage_handle_list; -+ -+ while(!globus_list_empty(list)) -+ { -+ ssh_usage_ent_t *usage_ent; -+ -+ usage_ent = (ssh_usage_ent_t *) -+ globus_list_remove(&list, list); -+ -+ if(usage_ent) -+ { -+ if(usage_ent->handle) -+ { -+ globus_usage_stats_handle_destroy(usage_ent->handle); -+ } -+ if(usage_ent->target) -+ { -+ free(usage_ent->target); -+ } -+ if(usage_ent->taglist) -+ { -+ free(usage_ent->taglist); -+ } -+ free(usage_ent); -+ } -+ } -+ usage_handle_list = NULL; -+} -+ -+static void -+log_usage_stats(const char *ssh_release, const char *ssl_release, -+ const char *method, const char *mechanism, -+ const char *clientip, -+ const char *username, const char *userdn) -+{ -+ globus_result_t result; -+ globus_list_t * list; -+ ssh_usage_ent_t * usage_ent; -+ char * keys[SSH_GLOBUS_TAGCOUNT]; -+ char * values[SSH_GLOBUS_TAGCOUNT]; -+ char * ptr; -+ char * key; -+ char * value; -+ int i = 0; -+ char * save_taglist = NULL; -+ -+ for(list = usage_handle_list; -+ !globus_list_empty(list); -+ list = globus_list_rest(list)) -+ { -+ usage_ent = (ssh_usage_ent_t *) globus_list_first(list); -+ -+ if(!usage_ent || usage_ent->handle == NULL) -+ continue; -+ -+ if(save_taglist == NULL || -+ strcmp(save_taglist, usage_ent->taglist) != 0) -+ { -+ save_taglist = usage_ent->taglist; -+ -+ ptr = usage_ent->taglist; -+ i = 0; -+ while(ptr && *ptr) -+ { -+ switch(*ptr) -+ { -+ case SSH_GLOBUS_USAGE_SSH_VER: -+ key = "SSH_VER"; -+ value = (char *) ssh_release; -+ break; -+ -+ case SSH_GLOBUS_USAGE_SSL_VER: -+ key = "SSL_VER"; -+ value = (char *) ssl_release; -+ break; -+ -+ case SSH_GLOBUS_USAGE_METHOD: -+ key = "METHOD"; -+ value = (char *) method; -+ break; -+ -+ case SSH_GLOBUS_USAGE_MECHANISM: -+ key = "MECH"; -+ value = (char *) mechanism?:""; -+ break; -+ -+ case SSH_GLOBUS_USAGE_CLIENTIP: -+ key = "CLIENTIP"; -+ value = (char *) clientip?:""; -+ break; -+ -+ case SSH_GLOBUS_USAGE_USERNAME: -+ key = "USER"; -+ value = (char *) username?:""; -+ break; -+ -+ case SSH_GLOBUS_USAGE_USERDN: -+ key = "USERDN"; -+ value = (char *) userdn?:""; -+ break; -+ -+ default: -+ key = NULL; -+ value = NULL; -+ break; -+ } -+ -+ if(key != NULL && value != NULL) -+ { -+ keys[i] = key; -+ values[i] = value; -+ i++; -+ } -+ -+ ptr++; -+ } -+ } -+ -+#ifdef HAVE_GLOBUS_USAGE_SEND_ARRAY -+ result = globus_usage_stats_send_array( -+ usage_ent->handle, i, keys, values); -+#else -+ if (i) -+ result = globus_usage_stats_send( -+ usage_ent->handle, i, -+ i>0?keys[0]:NULL, i>0?values[0]:NULL, -+ i>1?keys[1]:NULL, i>1?values[1]:NULL, -+ i>2?keys[2]:NULL, i>2?values[2]:NULL, -+ i>3?keys[3]:NULL, i>3?values[3]:NULL, -+ i>4?keys[4]:NULL, i>4?values[4]:NULL, -+ i>5?keys[5]:NULL, i>5?values[5]:NULL, -+ i>6?keys[6]:NULL, i>6?values[6]:NULL); -+#endif /* HAVE_GLOBUS_USAGE_SEND_ARRAY */ -+ } -+ -+ return; -+} -+#endif /* HAVE_GLOBUS_USAGE */ -+ -+void -+ssh_globus_send_usage_metrics(const char *ssh_release, -+ const char *ssl_release, -+ const char *method, -+ const char *mechanism, -+ const char *client_ip, -+ const char *username, -+ const char *userdn) -+{ -+#ifdef HAVE_GLOBUS_USAGE -+ -+ log_usage_stats(ssh_release, ssl_release, method, mechanism, -+ client_ip, username, userdn); -+ -+#endif /* HAVE_GLOBUS_USAGE */ -+} -diff -Nur openssh-7.8p1.orig/ssh-globus-usage.h openssh-7.8p1/ssh-globus-usage.h ---- openssh-7.8p1.orig/ssh-globus-usage.h 1970-01-01 01:00:00.000000000 +0100 -+++ openssh-7.8p1/ssh-globus-usage.h 2018-10-24 14:09:54.260377493 +0200 -@@ -0,0 +1,48 @@ -+/* -+ * Copyright 2009 The Board of Trustees of the University -+ * of Illinois. See the LICENSE file for detailed license information. -+ * -+ * Portions, specifically ssh_usage_stats_init(), ssh_usage_stats_close() -+ * were based on those from: gridftp/server/source/globus_i_gfs_log.h -+ * Copyright 1999-2006 University of Chicago -+ * -+ * Licensed under the Apache License, Version 2.0 (the "License"); -+ * you may not use this file except in compliance with the License. -+ * You may obtain a copy of the License at -+ * -+ * http://www.apache.org/licenses/LICENSE-2.0 -+ * -+ * Unless required by applicable law or agreed to in writing, software -+ * distributed under the License is distributed on an "AS IS" BASIS, -+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -+ * See the License for the specific language governing permissions and -+ * limitations under the License. -+ */ -+ -+#ifndef __SSH_GLOBUS_USAGE_H -+#define __SSH_GLOBUS_USAGE_H -+ -+#include "includes.h" -+ -+#ifdef HAVE_GLOBUS_USAGE -+ -+#include "globus_usage.h" -+ -+globus_result_t -+ssh_usage_stats_init(int disable_usage_stats, char *usage_stats_targets); -+ -+void -+ssh_usage_stats_close(int disable_usage_stats); -+ -+#endif /* HAVE_GLOBUS_USAGE */ -+ -+void -+ssh_globus_send_usage_metrics(const char *ssh_release, -+ const char *ssl_release, -+ const char *method, -+ const char *mechanism, -+ const char *client_ip, -+ const char *username, -+ const char *userdn); -+ -+#endif /* __SSH_GLOBUS_USAGE_H */ -diff -Nur openssh-7.8p1.orig/ssh-gss.h openssh-7.8p1/ssh-gss.h ---- openssh-7.8p1.orig/ssh-gss.h 2018-10-24 14:09:16.560776312 +0200 -+++ openssh-7.8p1/ssh-gss.h 2018-10-24 14:09:54.260377493 +0200 -@@ -85,6 +85,7 @@ - KEX_GSS_GRP14_SHA1_ID - - typedef struct { -+ char *filename; - char *envvar; - char *envval; - struct passwd *owner; -@@ -92,12 +93,14 @@ +diff -Nur openssh-8.0p1.orig/ssh-gss.h openssh-8.0p1/ssh-gss.h +--- openssh-8.0p1.orig/ssh-gss.h 2020-11-30 06:45:06.182912459 +0100 ++++ openssh-8.0p1/ssh-gss.h 2020-11-30 06:46:23.912105311 +0100 +@@ -97,12 +97,14 @@ } ssh_gssapi_ccache; typedef struct { @@ -3227,7 +2390,7 @@ diff -Nur openssh-7.8p1.orig/ssh-gss.h openssh-7.8p1/ssh-gss.h int used; int updated; } ssh_gssapi_client; -@@ -118,7 +121,7 @@ +@@ -123,7 +125,7 @@ OM_uint32 minor; /* both */ gss_ctx_id_t context; /* both */ gss_name_t name; /* both */ @@ -3236,7 +2399,7 @@ diff -Nur openssh-7.8p1.orig/ssh-gss.h openssh-7.8p1/ssh-gss.h gss_cred_id_t creds; /* server */ gss_name_t client; /* server */ gss_cred_id_t client_creds; /* both */ -@@ -155,6 +158,9 @@ +@@ -161,6 +163,9 @@ OM_uint32 ssh_gssapi_client_identity(Gssctxt *, const char *); int ssh_gssapi_credentials_updated(Gssctxt *); @@ -3244,31 +2407,14 @@ diff -Nur openssh-7.8p1.orig/ssh-gss.h openssh-7.8p1/ssh-gss.h +void ssh_gssapi_rekey_creds(); + /* In the server */ - typedef int ssh_gssapi_check_fn(Gssctxt **, gss_OID, const char *, + typedef int ssh_gssapi_check_fn(Gssctxt **, gss_OID, const char *, const char *); -@@ -165,7 +171,7 @@ - int ssh_gssapi_server_check_mech(Gssctxt **,gss_OID, const char *, - const char *); - OM_uint32 ssh_gssapi_server_ctx(Gssctxt **, gss_OID); --int ssh_gssapi_userok(char *name, struct passwd *); -+int ssh_gssapi_userok(char *name, struct passwd *, int gssapi_keyex); - OM_uint32 ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t); - void ssh_gssapi_do_child(char ***, u_int *); - void ssh_gssapi_cleanup_creds(void); -@@ -178,6 +184,7 @@ - int ssh_gssapi_update_creds(ssh_gssapi_ccache *store); - - void ssh_gssapi_rekey_creds(void); -+void ssh_gssapi_get_client_info(char **userdn, char **mech); - #endif /* GSSAPI */ - - #endif /* _SSH_GSS_H */ -diff -Nur openssh-7.8p1.orig/version.h openssh-7.8p1/version.h ---- openssh-7.8p1.orig/version.h 2018-08-23 07:41:42.000000000 +0200 -+++ openssh-7.8p1/version.h 2018-10-24 14:09:54.260377493 +0200 +diff -Nur openssh-8.0p1.orig/version.h openssh-8.0p1/version.h +--- openssh-8.0p1.orig/version.h 2019-04-18 00:52:57.000000000 +0200 ++++ openssh-8.0p1/version.h 2020-11-30 06:46:23.913105313 +0100 @@ -2,5 +2,19 @@ - #define SSH_VERSION "OpenSSH_7.8" + #define SSH_VERSION "OpenSSH_8.0" +#ifdef GSI +#define GSI_VERSION " GSI" @@ -3284,6 +2430,6 @@ diff -Nur openssh-7.8p1.orig/version.h openssh-7.8p1/version.h + #define SSH_PORTABLE "p1" -#define SSH_RELEASE SSH_VERSION SSH_PORTABLE -+#define GSI_PORTABLE "b-GSI" ++#define GSI_PORTABLE "c-GSI" +#define SSH_RELEASE SSH_VERSION SSH_PORTABLE GSI_PORTABLE \ + GSI_VERSION KRB5_VERSION diff --git a/openssh-8.0p1-gssapi-keyex.patch b/openssh-8.0p1-gssapi-keyex.patch new file mode 100644 index 0000000..2182c49 --- /dev/null +++ b/openssh-8.0p1-gssapi-keyex.patch @@ -0,0 +1,3971 @@ +diff --git a/Makefile.in b/Makefile.in +index 6f001bb3..c31821ac 100644 +--- a/Makefile.in ++++ b/Makefile.in +@@ -100,6 +100,7 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \ + kex.o kexdh.o kexgex.o kexecdh.o kexc25519.o \ + kexgexc.o kexgexs.o \ + sntrup4591761.o kexsntrup4591761x25519.o kexgen.o \ ++ kexgssc.o \ + platform-pledge.o platform-tracing.o platform-misc.o + + +@@ -114,7 +115,7 @@ SSHDOBJS=sshd.o auth-rhosts.o auth-passwd.o \ + auth-bsdauth.o auth2-hostbased.o auth2-kbdint.o \ + auth2-none.o auth2-passwd.o auth2-pubkey.o \ + monitor.o monitor_wrap.o auth-krb5.o \ +- auth2-gss.o gss-serv.o gss-serv-krb5.o \ ++ auth2-gss.o gss-serv.o gss-serv-krb5.o kexgsss.o \ + loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \ + sftp-server.o sftp-common.o \ + sandbox-null.o sandbox-rlimit.o sandbox-systrace.o sandbox-darwin.o \ +diff --git a/auth.c b/auth.c +index 332b6220..7664aaac 100644 +--- a/auth.c ++++ b/auth.c +@@ -399,7 +399,8 @@ auth_root_allowed(struct ssh *ssh, const char *method) + case PERMIT_NO_PASSWD: + if (strcmp(method, "publickey") == 0 || + strcmp(method, "hostbased") == 0 || +- strcmp(method, "gssapi-with-mic") == 0) ++ strcmp(method, "gssapi-with-mic") == 0 || ++ strcmp(method, "gssapi-keyex") == 0) + return 1; + break; + case PERMIT_FORCED_ONLY: +@@ -723,99 +724,6 @@ fakepw(void) + return (&fake); + } + +-/* +- * Returns the remote DNS hostname as a string. The returned string must not +- * be freed. NB. this will usually trigger a DNS query the first time it is +- * called. +- * This function does additional checks on the hostname to mitigate some +- * attacks on legacy rhosts-style authentication. +- * XXX is RhostsRSAAuthentication vulnerable to these? +- * XXX Can we remove these checks? (or if not, remove RhostsRSAAuthentication?) +- */ +- +-static char * +-remote_hostname(struct ssh *ssh) +-{ +- struct sockaddr_storage from; +- socklen_t fromlen; +- struct addrinfo hints, *ai, *aitop; +- char name[NI_MAXHOST], ntop2[NI_MAXHOST]; +- const char *ntop = ssh_remote_ipaddr(ssh); +- +- /* Get IP address of client. */ +- fromlen = sizeof(from); +- memset(&from, 0, sizeof(from)); +- if (getpeername(ssh_packet_get_connection_in(ssh), +- (struct sockaddr *)&from, &fromlen) < 0) { +- debug("getpeername failed: %.100s", strerror(errno)); +- return strdup(ntop); +- } +- +- ipv64_normalise_mapped(&from, &fromlen); +- if (from.ss_family == AF_INET6) +- fromlen = sizeof(struct sockaddr_in6); +- +- debug3("Trying to reverse map address %.100s.", ntop); +- /* Map the IP address to a host name. */ +- if (getnameinfo((struct sockaddr *)&from, fromlen, name, sizeof(name), +- NULL, 0, NI_NAMEREQD) != 0) { +- /* Host name not found. Use ip address. */ +- return strdup(ntop); +- } +- +- /* +- * if reverse lookup result looks like a numeric hostname, +- * someone is trying to trick us by PTR record like following: +- * 1.1.1.10.in-addr.arpa. IN PTR 2.3.4.5 +- */ +- memset(&hints, 0, sizeof(hints)); +- hints.ai_socktype = SOCK_DGRAM; /*dummy*/ +- hints.ai_flags = AI_NUMERICHOST; +- if (getaddrinfo(name, NULL, &hints, &ai) == 0) { +- logit("Nasty PTR record \"%s\" is set up for %s, ignoring", +- name, ntop); +- freeaddrinfo(ai); +- return strdup(ntop); +- } +- +- /* Names are stored in lowercase. */ +- lowercase(name); +- +- /* +- * Map it back to an IP address and check that the given +- * address actually is an address of this host. This is +- * necessary because anyone with access to a name server can +- * define arbitrary names for an IP address. Mapping from +- * name to IP address can be trusted better (but can still be +- * fooled if the intruder has access to the name server of +- * the domain). +- */ +- memset(&hints, 0, sizeof(hints)); +- hints.ai_family = from.ss_family; +- hints.ai_socktype = SOCK_STREAM; +- if (getaddrinfo(name, NULL, &hints, &aitop) != 0) { +- logit("reverse mapping checking getaddrinfo for %.700s " +- "[%s] failed.", name, ntop); +- return strdup(ntop); +- } +- /* Look for the address from the list of addresses. */ +- for (ai = aitop; ai; ai = ai->ai_next) { +- if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop2, +- sizeof(ntop2), NULL, 0, NI_NUMERICHOST) == 0 && +- (strcmp(ntop, ntop2) == 0)) +- break; +- } +- freeaddrinfo(aitop); +- /* If we reached the end of the list, the address was not there. */ +- if (ai == NULL) { +- /* Address not found for the host name. */ +- logit("Address %.100s maps to %.600s, but this does not " +- "map back to the address.", ntop, name); +- return strdup(ntop); +- } +- return strdup(name); +-} +- + /* + * Return the canonical name of the host in the other side of the current + * connection. The host name is cached, so it is efficient to call this +diff --git a/auth2-gss.c b/auth2-gss.c +index 9351e042..d6446c0c 100644 +--- a/auth2-gss.c ++++ b/auth2-gss.c +@@ -1,7 +1,7 @@ + /* $OpenBSD: auth2-gss.c,v 1.29 2018/07/31 03:10:27 djm Exp $ */ + + /* +- * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. ++ * Copyright (c) 2001-2007 Simon Wilkinson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions +@@ -54,6 +54,48 @@ static int input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh); + static int input_gssapi_exchange_complete(int type, u_int32_t plen, struct ssh *ssh); + static int input_gssapi_errtok(int, u_int32_t, struct ssh *); + ++/* ++ * The 'gssapi_keyex' userauth mechanism. ++ */ ++static int ++userauth_gsskeyex(struct ssh *ssh) ++{ ++ Authctxt *authctxt = ssh->authctxt; ++ int r, authenticated = 0; ++ struct sshbuf *b = NULL; ++ gss_buffer_desc mic, gssbuf; ++ u_char *p; ++ size_t len; ++ ++ if ((r = sshpkt_get_string(ssh, &p, &len)) != 0 || ++ (r = sshpkt_get_end(ssh)) != 0) ++ fatal("%s: %s", __func__, ssh_err(r)); ++ ++ if ((b = sshbuf_new()) == NULL) ++ fatal("%s: sshbuf_new failed", __func__); ++ ++ mic.value = p; ++ mic.length = len; ++ ++ ssh_gssapi_buildmic(b, authctxt->user, authctxt->service, ++ "gssapi-keyex"); ++ ++ if ((gssbuf.value = sshbuf_mutable_ptr(b)) == NULL) ++ fatal("%s: sshbuf_mutable_ptr failed", __func__); ++ gssbuf.length = sshbuf_len(b); ++ ++ /* gss_kex_context is NULL with privsep, so we can't check it here */ ++ if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gss_kex_context, ++ &gssbuf, &mic)))) ++ authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user, ++ authctxt->pw, 1)); ++ ++ sshbuf_free(b); ++ free(mic.value); ++ ++ return (authenticated); ++} ++ + /* + * We only support those mechanisms that we know about (ie ones that we know + * how to check local user kuserok and the like) +@@ -260,7 +302,8 @@ input_gssapi_exchange_complete(int type, u_int32_t plen, struct ssh *ssh) + if ((r = sshpkt_get_end(ssh)) != 0) + fatal("%s: %s", __func__, ssh_err(r)); + +- authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user)); ++ authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user, ++ authctxt->pw, 1)); + + if ((!use_privsep || mm_is_monitor()) && + (displayname = ssh_gssapi_displayname()) != NULL) +@@ -306,7 +349,8 @@ input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh) + gssbuf.length = sshbuf_len(b); + + if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gssctxt, &gssbuf, &mic)))) +- authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user)); ++ authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user, ++ authctxt->pw, 0)); + else + logit("GSSAPI MIC check failed"); + +@@ -326,6 +370,12 @@ input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh) + return 0; + } + ++Authmethod method_gsskeyex = { ++ "gssapi-keyex", ++ userauth_gsskeyex, ++ &options.gss_authentication ++}; ++ + Authmethod method_gssapi = { + "gssapi-with-mic", + userauth_gssapi, +diff --git a/auth2.c b/auth2.c +index 16ae1a36..7417eafa 100644 +--- a/auth2.c ++++ b/auth2.c +@@ -75,6 +75,7 @@ extern Authmethod method_passwd; + extern Authmethod method_kbdint; + extern Authmethod method_hostbased; + #ifdef GSSAPI ++extern Authmethod method_gsskeyex; + extern Authmethod method_gssapi; + #endif + +@@ -82,6 +83,7 @@ Authmethod *authmethods[] = { + &method_none, + &method_pubkey, + #ifdef GSSAPI ++ &method_gsskeyex, + &method_gssapi, + #endif + &method_passwd, +diff --git a/canohost.c b/canohost.c +index f71a0856..404731d2 100644 +--- a/canohost.c ++++ b/canohost.c +@@ -35,6 +35,99 @@ + #include "canohost.h" + #include "misc.h" + ++/* ++ * Returns the remote DNS hostname as a string. The returned string must not ++ * be freed. NB. this will usually trigger a DNS query the first time it is ++ * called. ++ * This function does additional checks on the hostname to mitigate some ++ * attacks on legacy rhosts-style authentication. ++ * XXX is RhostsRSAAuthentication vulnerable to these? ++ * XXX Can we remove these checks? (or if not, remove RhostsRSAAuthentication?) ++ */ ++ ++char * ++remote_hostname(struct ssh *ssh) ++{ ++ struct sockaddr_storage from; ++ socklen_t fromlen; ++ struct addrinfo hints, *ai, *aitop; ++ char name[NI_MAXHOST], ntop2[NI_MAXHOST]; ++ const char *ntop = ssh_remote_ipaddr(ssh); ++ ++ /* Get IP address of client. */ ++ fromlen = sizeof(from); ++ memset(&from, 0, sizeof(from)); ++ if (getpeername(ssh_packet_get_connection_in(ssh), ++ (struct sockaddr *)&from, &fromlen) < 0) { ++ debug("getpeername failed: %.100s", strerror(errno)); ++ return strdup(ntop); ++ } ++ ++ ipv64_normalise_mapped(&from, &fromlen); ++ if (from.ss_family == AF_INET6) ++ fromlen = sizeof(struct sockaddr_in6); ++ ++ debug3("Trying to reverse map address %.100s.", ntop); ++ /* Map the IP address to a host name. */ ++ if (getnameinfo((struct sockaddr *)&from, fromlen, name, sizeof(name), ++ NULL, 0, NI_NAMEREQD) != 0) { ++ /* Host name not found. Use ip address. */ ++ return strdup(ntop); ++ } ++ ++ /* ++ * if reverse lookup result looks like a numeric hostname, ++ * someone is trying to trick us by PTR record like following: ++ * 1.1.1.10.in-addr.arpa. IN PTR 2.3.4.5 ++ */ ++ memset(&hints, 0, sizeof(hints)); ++ hints.ai_socktype = SOCK_DGRAM; /*dummy*/ ++ hints.ai_flags = AI_NUMERICHOST; ++ if (getaddrinfo(name, NULL, &hints, &ai) == 0) { ++ logit("Nasty PTR record \"%s\" is set up for %s, ignoring", ++ name, ntop); ++ freeaddrinfo(ai); ++ return strdup(ntop); ++ } ++ ++ /* Names are stored in lowercase. */ ++ lowercase(name); ++ ++ /* ++ * Map it back to an IP address and check that the given ++ * address actually is an address of this host. This is ++ * necessary because anyone with access to a name server can ++ * define arbitrary names for an IP address. Mapping from ++ * name to IP address can be trusted better (but can still be ++ * fooled if the intruder has access to the name server of ++ * the domain). ++ */ ++ memset(&hints, 0, sizeof(hints)); ++ hints.ai_family = from.ss_family; ++ hints.ai_socktype = SOCK_STREAM; ++ if (getaddrinfo(name, NULL, &hints, &aitop) != 0) { ++ logit("reverse mapping checking getaddrinfo for %.700s " ++ "[%s] failed.", name, ntop); ++ return strdup(ntop); ++ } ++ /* Look for the address from the list of addresses. */ ++ for (ai = aitop; ai; ai = ai->ai_next) { ++ if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop2, ++ sizeof(ntop2), NULL, 0, NI_NUMERICHOST) == 0 && ++ (strcmp(ntop, ntop2) == 0)) ++ break; ++ } ++ freeaddrinfo(aitop); ++ /* If we reached the end of the list, the address was not there. */ ++ if (ai == NULL) { ++ /* Address not found for the host name. */ ++ logit("Address %.100s maps to %.600s, but this does not " ++ "map back to the address.", ntop, name); ++ return strdup(ntop); ++ } ++ return strdup(name); ++} ++ + void + ipv64_normalise_mapped(struct sockaddr_storage *addr, socklen_t *len) + { +diff --git a/canohost.h b/canohost.h +index 26d62855..0cadc9f1 100644 +--- a/canohost.h ++++ b/canohost.h +@@ -15,6 +15,9 @@ + #ifndef _CANOHOST_H + #define _CANOHOST_H + ++struct ssh; ++ ++char *remote_hostname(struct ssh *); + char *get_peer_ipaddr(int); + int get_peer_port(int); + char *get_local_ipaddr(int); +diff --git a/clientloop.c b/clientloop.c +index 521467bd..a0578e9d 100644 +--- a/clientloop.c ++++ b/clientloop.c +@@ -112,6 +112,10 @@ + #include "ssherr.h" + #include "hostfile.h" + ++#ifdef GSSAPI ++#include "ssh-gss.h" ++#endif ++ + /* import options */ + extern Options options; + +@@ -1374,9 +1378,18 @@ client_loop(struct ssh *ssh, int have_pty, int escape_char_arg, + break; + + /* Do channel operations unless rekeying in progress. */ +- if (!ssh_packet_is_rekeying(ssh)) ++ if (!ssh_packet_is_rekeying(ssh)) { + channel_after_select(ssh, readset, writeset); + ++#ifdef GSSAPI ++ if (options.gss_renewal_rekey && ++ ssh_gssapi_credentials_updated(NULL)) { ++ debug("credentials updated - forcing rekey"); ++ need_rekeying = 1; ++ } ++#endif ++ } ++ + /* Buffer input from the connection. */ + client_process_net_input(ssh, readset); + +diff --git a/configure.ac b/configure.ac +index 30be6c18..2869f704 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -665,6 +665,30 @@ main() { if (NSVersionOfRunTimeLibrary("System") >= (60 << 16)) + [Use tunnel device compatibility to OpenBSD]) + AC_DEFINE([SSH_TUN_PREPEND_AF], [1], + [Prepend the address family to IP tunnel traffic]) ++ AC_MSG_CHECKING([if we have the Security Authorization Session API]) ++ AC_TRY_COMPILE([#include ], ++ [SessionCreate(0, 0);], ++ [ac_cv_use_security_session_api="yes" ++ AC_DEFINE([USE_SECURITY_SESSION_API], [1], ++ [platform has the Security Authorization Session API]) ++ LIBS="$LIBS -framework Security" ++ AC_MSG_RESULT([yes])], ++ [ac_cv_use_security_session_api="no" ++ AC_MSG_RESULT([no])]) ++ AC_MSG_CHECKING([if we have an in-memory credentials cache]) ++ AC_TRY_COMPILE( ++ [#include ], ++ [cc_context_t c; ++ (void) cc_initialize (&c, 0, NULL, NULL);], ++ [AC_DEFINE([USE_CCAPI], [1], ++ [platform uses an in-memory credentials cache]) ++ LIBS="$LIBS -framework Security" ++ AC_MSG_RESULT([yes]) ++ if test "x$ac_cv_use_security_session_api" = "xno"; then ++ AC_MSG_ERROR([*** Need a security framework to use the credentials cache API ***]) ++ fi], ++ [AC_MSG_RESULT([no])] ++ ) + m4_pattern_allow([AU_IPv]) + AC_CHECK_DECL([AU_IPv4], [], + AC_DEFINE([AU_IPv4], [0], [System only supports IPv4 audit records]) +diff --git a/gss-genr.c b/gss-genr.c +index d56257b4..3eaa5fa5 100644 +--- a/gss-genr.c ++++ b/gss-genr.c +@@ -1,7 +1,7 @@ + /* $OpenBSD: gss-genr.c,v 1.26 2018/07/10 09:13:30 djm Exp $ */ + + /* +- * Copyright (c) 2001-2007 Simon Wilkinson. All rights reserved. ++ * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions +@@ -41,12 +41,36 @@ + #include "sshbuf.h" + #include "log.h" + #include "ssh2.h" ++#include "cipher.h" ++#include "sshkey.h" ++#include "kex.h" ++#include "digest.h" ++#include "packet.h" + + #include "ssh-gss.h" + + extern u_char *session_id2; + extern u_int session_id2_len; + ++typedef struct { ++ char *encoded; ++ gss_OID oid; ++} ssh_gss_kex_mapping; ++ ++/* ++ * XXX - It would be nice to find a more elegant way of handling the ++ * XXX passing of the key exchange context to the userauth routines ++ */ ++ ++Gssctxt *gss_kex_context = NULL; ++ ++static ssh_gss_kex_mapping *gss_enc2oid = NULL; ++ ++int ++ssh_gssapi_oid_table_ok(void) { ++ return (gss_enc2oid != NULL); ++} ++ + /* sshbuf_get for gss_buffer_desc */ + int + ssh_gssapi_get_buffer_desc(struct sshbuf *b, gss_buffer_desc *g) +@@ -62,6 +86,161 @@ ssh_gssapi_get_buffer_desc(struct sshbuf *b, gss_buffer_desc *g) + return 0; + } + ++/* sshpkt_get of gss_buffer_desc */ ++int ++ssh_gssapi_sshpkt_get_buffer_desc(struct ssh *ssh, gss_buffer_desc *g) ++{ ++ int r; ++ u_char *p; ++ size_t len; ++ ++ if ((r = sshpkt_get_string(ssh, &p, &len)) != 0) ++ return r; ++ g->value = p; ++ g->length = len; ++ return 0; ++} ++ ++/* ++ * Return a list of the gss-group1-sha1 mechanisms supported by this program ++ * ++ * We test mechanisms to ensure that we can use them, to avoid starting ++ * a key exchange with a bad mechanism ++ */ ++ ++char * ++ssh_gssapi_client_mechanisms(const char *host, const char *client, ++ const char *kex) { ++ gss_OID_set gss_supported = NULL; ++ OM_uint32 min_status; ++ ++ if (GSS_ERROR(gss_indicate_mechs(&min_status, &gss_supported))) ++ return NULL; ++ ++ return ssh_gssapi_kex_mechs(gss_supported, ssh_gssapi_check_mechanism, ++ host, client, kex); ++} ++ ++char * ++ssh_gssapi_kex_mechs(gss_OID_set gss_supported, ssh_gssapi_check_fn *check, ++ const char *host, const char *client, const char *kex) { ++ struct sshbuf *buf = NULL; ++ size_t i; ++ int r, oidpos, enclen; ++ char *mechs, *encoded; ++ u_char digest[SSH_DIGEST_MAX_LENGTH]; ++ char deroid[2]; ++ struct ssh_digest_ctx *md = NULL; ++ char *s, *cp, *p; ++ ++ if (gss_enc2oid != NULL) { ++ for (i = 0; gss_enc2oid[i].encoded != NULL; i++) ++ free(gss_enc2oid[i].encoded); ++ free(gss_enc2oid); ++ } ++ ++ gss_enc2oid = xmalloc(sizeof(ssh_gss_kex_mapping) * ++ (gss_supported->count + 1)); ++ ++ if ((buf = sshbuf_new()) == NULL) ++ fatal("%s: sshbuf_new failed", __func__); ++ ++ oidpos = 0; ++ s = cp = xstrdup(kex); ++ for (i = 0; i < gss_supported->count; i++) { ++ if (gss_supported->elements[i].length < 128 && ++ (*check)(NULL, &(gss_supported->elements[i]), host, client)) { ++ ++ deroid[0] = SSH_GSS_OIDTYPE; ++ deroid[1] = gss_supported->elements[i].length; ++ ++ if ((md = ssh_digest_start(SSH_DIGEST_MD5)) == NULL || ++ (r = ssh_digest_update(md, deroid, 2)) != 0 || ++ (r = ssh_digest_update(md, ++ gss_supported->elements[i].elements, ++ gss_supported->elements[i].length)) != 0 || ++ (r = ssh_digest_final(md, digest, sizeof(digest))) != 0) ++ fatal("%s: digest failed: %s", __func__, ++ ssh_err(r)); ++ ssh_digest_free(md); ++ md = NULL; ++ ++ encoded = xmalloc(ssh_digest_bytes(SSH_DIGEST_MD5) ++ * 2); ++ enclen = __b64_ntop(digest, ++ ssh_digest_bytes(SSH_DIGEST_MD5), encoded, ++ ssh_digest_bytes(SSH_DIGEST_MD5) * 2); ++ ++ cp = strncpy(s, kex, strlen(kex)); ++ for ((p = strsep(&cp, ",")); p && *p != '\0'; ++ (p = strsep(&cp, ","))) { ++ if (sshbuf_len(buf) != 0 && ++ (r = sshbuf_put_u8(buf, ',')) != 0) ++ fatal("%s: sshbuf_put_u8 error: %s", ++ __func__, ssh_err(r)); ++ if ((r = sshbuf_put(buf, p, strlen(p))) != 0 || ++ (r = sshbuf_put(buf, encoded, enclen)) != 0) ++ fatal("%s: sshbuf_put error: %s", ++ __func__, ssh_err(r)); ++ } ++ ++ gss_enc2oid[oidpos].oid = &(gss_supported->elements[i]); ++ gss_enc2oid[oidpos].encoded = encoded; ++ oidpos++; ++ } ++ } ++ free(s); ++ gss_enc2oid[oidpos].oid = NULL; ++ gss_enc2oid[oidpos].encoded = NULL; ++ ++ if ((mechs = sshbuf_dup_string(buf)) == NULL) ++ fatal("%s: sshbuf_dup_string failed", __func__); ++ ++ sshbuf_free(buf); ++ ++ if (strlen(mechs) == 0) { ++ free(mechs); ++ mechs = NULL; ++ } ++ ++ return (mechs); ++} ++ ++gss_OID ++ssh_gssapi_id_kex(Gssctxt *ctx, char *name, int kex_type) { ++ int i = 0; ++ ++#define SKIP_KEX_NAME(type) \ ++ case type: \ ++ if (strlen(name) < sizeof(type##_ID)) \ ++ return GSS_C_NO_OID; \ ++ name += sizeof(type##_ID) - 1; \ ++ break; ++ ++ switch (kex_type) { ++ SKIP_KEX_NAME(KEX_GSS_GRP1_SHA1) ++ SKIP_KEX_NAME(KEX_GSS_GRP14_SHA1) ++ SKIP_KEX_NAME(KEX_GSS_GRP14_SHA256) ++ SKIP_KEX_NAME(KEX_GSS_GRP16_SHA512) ++ SKIP_KEX_NAME(KEX_GSS_GEX_SHA1) ++ SKIP_KEX_NAME(KEX_GSS_NISTP256_SHA256) ++ SKIP_KEX_NAME(KEX_GSS_C25519_SHA256) ++ default: ++ return GSS_C_NO_OID; ++ } ++ ++#undef SKIP_KEX_NAME ++ ++ while (gss_enc2oid[i].encoded != NULL && ++ strcmp(name, gss_enc2oid[i].encoded) != 0) ++ i++; ++ ++ if (gss_enc2oid[i].oid != NULL && ctx != NULL) ++ ssh_gssapi_set_oid(ctx, gss_enc2oid[i].oid); ++ ++ return gss_enc2oid[i].oid; ++} ++ + /* Check that the OID in a data stream matches that in the context */ + int + ssh_gssapi_check_oid(Gssctxt *ctx, void *data, size_t len) +@@ -218,7 +397,7 @@ ssh_gssapi_init_ctx(Gssctxt *ctx, int deleg_creds, gss_buffer_desc *recv_tok, + } + + ctx->major = gss_init_sec_context(&ctx->minor, +- GSS_C_NO_CREDENTIAL, &ctx->context, ctx->name, ctx->oid, ++ ctx->client_creds, &ctx->context, ctx->name, ctx->oid, + GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG | deleg_flag, + 0, NULL, recv_tok, NULL, send_tok, flags, NULL); + +@@ -247,9 +426,43 @@ ssh_gssapi_import_name(Gssctxt *ctx, const char *host) + return (ctx->major); + } + ++OM_uint32 ++ssh_gssapi_client_identity(Gssctxt *ctx, const char *name) ++{ ++ gss_buffer_desc gssbuf; ++ gss_name_t gssname; ++ OM_uint32 status; ++ gss_OID_set oidset; ++ ++ gssbuf.value = (void *) name; ++ gssbuf.length = strlen(gssbuf.value); ++ ++ gss_create_empty_oid_set(&status, &oidset); ++ gss_add_oid_set_member(&status, ctx->oid, &oidset); ++ ++ ctx->major = gss_import_name(&ctx->minor, &gssbuf, ++ GSS_C_NT_USER_NAME, &gssname); ++ ++ if (!ctx->major) ++ ctx->major = gss_acquire_cred(&ctx->minor, ++ gssname, 0, oidset, GSS_C_INITIATE, ++ &ctx->client_creds, NULL, NULL); ++ ++ gss_release_name(&status, &gssname); ++ gss_release_oid_set(&status, &oidset); ++ ++ if (ctx->major) ++ ssh_gssapi_error(ctx); ++ ++ return(ctx->major); ++} ++ + OM_uint32 + ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash) + { ++ if (ctx == NULL) ++ return -1; ++ + if ((ctx->major = gss_get_mic(&ctx->minor, ctx->context, + GSS_C_QOP_DEFAULT, buffer, hash))) + ssh_gssapi_error(ctx); +@@ -257,6 +470,19 @@ ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash) + return (ctx->major); + } + ++/* Priviledged when used by server */ ++OM_uint32 ++ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic) ++{ ++ if (ctx == NULL) ++ return -1; ++ ++ ctx->major = gss_verify_mic(&ctx->minor, ctx->context, ++ gssbuf, gssmic, NULL); ++ ++ return (ctx->major); ++} ++ + void + ssh_gssapi_buildmic(struct sshbuf *b, const char *user, const char *service, + const char *context) +@@ -273,11 +499,16 @@ ssh_gssapi_buildmic(struct sshbuf *b, const char *user, const char *service, + } + + int +-ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host) ++ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host, ++ const char *client) + { + gss_buffer_desc token = GSS_C_EMPTY_BUFFER; + OM_uint32 major, minor; + gss_OID_desc spnego_oid = {6, (void *)"\x2B\x06\x01\x05\x05\x02"}; ++ Gssctxt *intctx = NULL; ++ ++ if (ctx == NULL) ++ ctx = &intctx; + + /* RFC 4462 says we MUST NOT do SPNEGO */ + if (oid->length == spnego_oid.length && +@@ -287,6 +518,10 @@ ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host) + ssh_gssapi_build_ctx(ctx); + ssh_gssapi_set_oid(*ctx, oid); + major = ssh_gssapi_import_name(*ctx, host); ++ ++ if (!GSS_ERROR(major) && client) ++ major = ssh_gssapi_client_identity(*ctx, client); ++ + if (!GSS_ERROR(major)) { + major = ssh_gssapi_init_ctx(*ctx, 0, GSS_C_NO_BUFFER, &token, + NULL); +@@ -296,10 +531,66 @@ ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host) + GSS_C_NO_BUFFER); + } + +- if (GSS_ERROR(major)) ++ if (GSS_ERROR(major) || intctx != NULL) + ssh_gssapi_delete_ctx(ctx); + + return (!GSS_ERROR(major)); + } + ++int ++ssh_gssapi_credentials_updated(Gssctxt *ctxt) { ++ static gss_name_t saved_name = GSS_C_NO_NAME; ++ static OM_uint32 saved_lifetime = 0; ++ static gss_OID saved_mech = GSS_C_NO_OID; ++ static gss_name_t name; ++ static OM_uint32 last_call = 0; ++ OM_uint32 lifetime, now, major, minor; ++ int equal; ++ ++ now = time(NULL); ++ ++ if (ctxt) { ++ debug("Rekey has happened - updating saved versions"); ++ ++ if (saved_name != GSS_C_NO_NAME) ++ gss_release_name(&minor, &saved_name); ++ ++ major = gss_inquire_cred(&minor, GSS_C_NO_CREDENTIAL, ++ &saved_name, &saved_lifetime, NULL, NULL); ++ ++ if (!GSS_ERROR(major)) { ++ saved_mech = ctxt->oid; ++ saved_lifetime+= now; ++ } else { ++ /* Handle the error */ ++ } ++ return 0; ++ } ++ ++ if (now - last_call < 10) ++ return 0; ++ ++ last_call = now; ++ ++ if (saved_mech == GSS_C_NO_OID) ++ return 0; ++ ++ major = gss_inquire_cred(&minor, GSS_C_NO_CREDENTIAL, ++ &name, &lifetime, NULL, NULL); ++ if (major == GSS_S_CREDENTIALS_EXPIRED) ++ return 0; ++ else if (GSS_ERROR(major)) ++ return 0; ++ ++ major = gss_compare_name(&minor, saved_name, name, &equal); ++ gss_release_name(&minor, &name); ++ if (GSS_ERROR(major)) ++ return 0; ++ ++ if (equal && (saved_lifetime < lifetime + now - 10)) ++ return 1; ++ ++ return 0; ++} ++ + #endif /* GSSAPI */ +diff --git a/gss-serv-krb5.c b/gss-serv-krb5.c +index a151bc1e..8d2b677f 100644 +--- a/gss-serv-krb5.c ++++ b/gss-serv-krb5.c +@@ -1,7 +1,7 @@ + /* $OpenBSD: gss-serv-krb5.c,v 1.9 2018/07/09 21:37:55 markus Exp $ */ + + /* +- * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. ++ * Copyright (c) 2001-2007 Simon Wilkinson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions +@@ -120,7 +120,7 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client) + krb5_error_code problem; + krb5_principal princ; + OM_uint32 maj_status, min_status; +- int len; ++ const char *new_ccname, *new_cctype; + const char *errmsg; + + if (client->creds == NULL) { +@@ -180,11 +180,26 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client) + return; + } + +- client->store.filename = xstrdup(krb5_cc_get_name(krb_context, ccache)); ++ new_cctype = krb5_cc_get_type(krb_context, ccache); ++ new_ccname = krb5_cc_get_name(krb_context, ccache); ++ + client->store.envvar = "KRB5CCNAME"; +- len = strlen(client->store.filename) + 6; +- client->store.envval = xmalloc(len); +- snprintf(client->store.envval, len, "FILE:%s", client->store.filename); ++#ifdef USE_CCAPI ++ xasprintf(&client->store.envval, "API:%s", new_ccname); ++ client->store.filename = NULL; ++#else ++ if (new_ccname[0] == ':') ++ new_ccname++; ++ xasprintf(&client->store.envval, "%s:%s", new_cctype, new_ccname); ++ if (strcmp(new_cctype, "DIR") == 0) { ++ char *p; ++ p = strrchr(client->store.envval, '/'); ++ if (p) ++ *p = '\0'; ++ } ++ if ((strcmp(new_cctype, "FILE") == 0) || (strcmp(new_cctype, "DIR") == 0)) ++ client->store.filename = xstrdup(new_ccname); ++#endif + + #ifdef USE_PAM + if (options.use_pam) +@@ -193,9 +208,76 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client) + + krb5_cc_close(krb_context, ccache); + ++ client->store.data = krb_context; ++ + return; + } + ++int ++ssh_gssapi_krb5_updatecreds(ssh_gssapi_ccache *store, ++ ssh_gssapi_client *client) ++{ ++ krb5_ccache ccache = NULL; ++ krb5_principal principal = NULL; ++ char *name = NULL; ++ krb5_error_code problem; ++ OM_uint32 maj_status, min_status; ++ ++ if ((problem = krb5_cc_resolve(krb_context, store->envval, &ccache))) { ++ logit("krb5_cc_resolve(): %.100s", ++ krb5_get_err_text(krb_context, problem)); ++ return 0; ++ } ++ ++ /* Find out who the principal in this cache is */ ++ if ((problem = krb5_cc_get_principal(krb_context, ccache, ++ &principal))) { ++ logit("krb5_cc_get_principal(): %.100s", ++ krb5_get_err_text(krb_context, problem)); ++ krb5_cc_close(krb_context, ccache); ++ return 0; ++ } ++ ++ if ((problem = krb5_unparse_name(krb_context, principal, &name))) { ++ logit("krb5_unparse_name(): %.100s", ++ krb5_get_err_text(krb_context, problem)); ++ krb5_free_principal(krb_context, principal); ++ krb5_cc_close(krb_context, ccache); ++ return 0; ++ } ++ ++ ++ if (strcmp(name,client->exportedname.value)!=0) { ++ debug("Name in local credentials cache differs. Not storing"); ++ krb5_free_principal(krb_context, principal); ++ krb5_cc_close(krb_context, ccache); ++ krb5_free_unparsed_name(krb_context, name); ++ return 0; ++ } ++ krb5_free_unparsed_name(krb_context, name); ++ ++ /* Name matches, so lets get on with it! */ ++ ++ if ((problem = krb5_cc_initialize(krb_context, ccache, principal))) { ++ logit("krb5_cc_initialize(): %.100s", ++ krb5_get_err_text(krb_context, problem)); ++ krb5_free_principal(krb_context, principal); ++ krb5_cc_close(krb_context, ccache); ++ return 0; ++ } ++ ++ krb5_free_principal(krb_context, principal); ++ ++ if ((maj_status = gss_krb5_copy_ccache(&min_status, client->creds, ++ ccache))) { ++ logit("gss_krb5_copy_ccache() failed. Sorry!"); ++ krb5_cc_close(krb_context, ccache); ++ return 0; ++ } ++ ++ return 1; ++} ++ + ssh_gssapi_mech gssapi_kerberos_mech = { + "toWM5Slw5Ew8Mqkay+al2g==", + "Kerberos", +@@ -203,7 +285,8 @@ ssh_gssapi_mech gssapi_kerberos_mech = { + NULL, + &ssh_gssapi_krb5_userok, + NULL, +- &ssh_gssapi_krb5_storecreds ++ &ssh_gssapi_krb5_storecreds, ++ &ssh_gssapi_krb5_updatecreds + }; + + #endif /* KRB5 */ +diff --git a/gss-serv.c b/gss-serv.c +index ab3a15f0..6ce56e92 100644 +--- a/gss-serv.c ++++ b/gss-serv.c +@@ -1,7 +1,7 @@ + /* $OpenBSD: gss-serv.c,v 1.31 2018/07/09 21:37:55 markus Exp $ */ + + /* +- * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. ++ * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions +@@ -44,17 +44,19 @@ + #include "session.h" + #include "misc.h" + #include "servconf.h" ++#include "uidswap.h" + + #include "ssh-gss.h" ++#include "monitor_wrap.h" + + extern ServerOptions options; + + static ssh_gssapi_client gssapi_client = +- { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER, +- GSS_C_NO_CREDENTIAL, NULL, {NULL, NULL, NULL, NULL}}; ++ { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER, GSS_C_NO_CREDENTIAL, ++ GSS_C_NO_NAME, NULL, {NULL, NULL, NULL, NULL, NULL}, 0, 0}; + + ssh_gssapi_mech gssapi_null_mech = +- { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL}; ++ { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL, NULL}; + + #ifdef KRB5 + extern ssh_gssapi_mech gssapi_kerberos_mech; +@@ -140,6 +142,29 @@ ssh_gssapi_server_ctx(Gssctxt **ctx, gss_OID oid) + return (ssh_gssapi_acquire_cred(*ctx)); + } + ++/* Unprivileged */ ++char * ++ssh_gssapi_server_mechanisms(void) { ++ if (supported_oids == NULL) ++ ssh_gssapi_prepare_supported_oids(); ++ return (ssh_gssapi_kex_mechs(supported_oids, ++ &ssh_gssapi_server_check_mech, NULL, NULL, ++ options.gss_kex_algorithms)); ++} ++ ++/* Unprivileged */ ++int ++ssh_gssapi_server_check_mech(Gssctxt **dum, gss_OID oid, const char *data, ++ const char *dummy) { ++ Gssctxt *ctx = NULL; ++ int res; ++ ++ res = !GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctx, oid))); ++ ssh_gssapi_delete_ctx(&ctx); ++ ++ return (res); ++} ++ + /* Unprivileged */ + void + ssh_gssapi_supported_oids(gss_OID_set *oidset) +@@ -150,7 +175,9 @@ ssh_gssapi_supported_oids(gss_OID_set *oidset) + gss_OID_set supported; + + gss_create_empty_oid_set(&min_status, oidset); +- gss_indicate_mechs(&min_status, &supported); ++ ++ if (GSS_ERROR(gss_indicate_mechs(&min_status, &supported))) ++ return; + + while (supported_mechs[i]->name != NULL) { + if (GSS_ERROR(gss_test_oid_set_member(&min_status, +@@ -276,8 +303,48 @@ OM_uint32 + ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client) + { + int i = 0; ++ int equal = 0; ++ gss_name_t new_name = GSS_C_NO_NAME; ++ gss_buffer_desc ename = GSS_C_EMPTY_BUFFER; ++ ++ if (options.gss_store_rekey && client->used && ctx->client_creds) { ++ if (client->mech->oid.length != ctx->oid->length || ++ (memcmp(client->mech->oid.elements, ++ ctx->oid->elements, ctx->oid->length) !=0)) { ++ debug("Rekeyed credentials have different mechanism"); ++ return GSS_S_COMPLETE; ++ } ++ ++ if ((ctx->major = gss_inquire_cred_by_mech(&ctx->minor, ++ ctx->client_creds, ctx->oid, &new_name, ++ NULL, NULL, NULL))) { ++ ssh_gssapi_error(ctx); ++ return (ctx->major); ++ } + +- gss_buffer_desc ename; ++ ctx->major = gss_compare_name(&ctx->minor, client->name, ++ new_name, &equal); ++ ++ if (GSS_ERROR(ctx->major)) { ++ ssh_gssapi_error(ctx); ++ return (ctx->major); ++ } ++ ++ if (!equal) { ++ debug("Rekeyed credentials have different name"); ++ return GSS_S_COMPLETE; ++ } ++ ++ debug("Marking rekeyed credentials for export"); ++ ++ gss_release_name(&ctx->minor, &client->name); ++ gss_release_cred(&ctx->minor, &client->creds); ++ client->name = new_name; ++ client->creds = ctx->client_creds; ++ ctx->client_creds = GSS_C_NO_CREDENTIAL; ++ client->updated = 1; ++ return GSS_S_COMPLETE; ++ } + + client->mech = NULL; + +@@ -292,6 +359,13 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client) + if (client->mech == NULL) + return GSS_S_FAILURE; + ++ if (ctx->client_creds && ++ (ctx->major = gss_inquire_cred_by_mech(&ctx->minor, ++ ctx->client_creds, ctx->oid, &client->name, NULL, NULL, NULL))) { ++ ssh_gssapi_error(ctx); ++ return (ctx->major); ++ } ++ + if ((ctx->major = gss_display_name(&ctx->minor, ctx->client, + &client->displayname, NULL))) { + ssh_gssapi_error(ctx); +@@ -309,6 +383,8 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client) + return (ctx->major); + } + ++ gss_release_buffer(&ctx->minor, &ename); ++ + /* We can't copy this structure, so we just move the pointer to it */ + client->creds = ctx->client_creds; + ctx->client_creds = GSS_C_NO_CREDENTIAL; +@@ -319,11 +395,20 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client) + void + ssh_gssapi_cleanup_creds(void) + { +- if (gssapi_client.store.filename != NULL) { +- /* Unlink probably isn't sufficient */ +- debug("removing gssapi cred file\"%s\"", +- gssapi_client.store.filename); +- unlink(gssapi_client.store.filename); ++ krb5_ccache ccache = NULL; ++ krb5_error_code problem; ++ ++ if (gssapi_client.store.data != NULL) { ++ if ((problem = krb5_cc_resolve(gssapi_client.store.data, gssapi_client.store.envval, &ccache))) { ++ debug("%s: krb5_cc_resolve(): %.100s", __func__, ++ krb5_get_err_text(gssapi_client.store.data, problem)); ++ } else if ((problem = krb5_cc_destroy(gssapi_client.store.data, ccache))) { ++ debug("%s: krb5_cc_destroy(): %.100s", __func__, ++ krb5_get_err_text(gssapi_client.store.data, problem)); ++ } else { ++ krb5_free_context(gssapi_client.store.data); ++ gssapi_client.store.data = NULL; ++ } + } + } + +@@ -356,19 +441,23 @@ ssh_gssapi_do_child(char ***envp, u_int *envsizep) + + /* Privileged */ + int +-ssh_gssapi_userok(char *user) ++ssh_gssapi_userok(char *user, struct passwd *pw, int kex) + { + OM_uint32 lmin; + ++ (void) kex; /* used in privilege separation */ ++ + if (gssapi_client.exportedname.length == 0 || + gssapi_client.exportedname.value == NULL) { + debug("No suitable client data"); + return 0; + } + if (gssapi_client.mech && gssapi_client.mech->userok) +- if ((*gssapi_client.mech->userok)(&gssapi_client, user)) ++ if ((*gssapi_client.mech->userok)(&gssapi_client, user)) { ++ gssapi_client.used = 1; ++ gssapi_client.store.owner = pw; + return 1; +- else { ++ } else { + /* Destroy delegated credentials if userok fails */ + gss_release_buffer(&lmin, &gssapi_client.displayname); + gss_release_buffer(&lmin, &gssapi_client.exportedname); +@@ -382,14 +471,90 @@ ssh_gssapi_userok(char *user) + return (0); + } + +-/* Privileged */ +-OM_uint32 +-ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic) ++/* These bits are only used for rekeying. The unpriviledged child is running ++ * as the user, the monitor is root. ++ * ++ * In the child, we want to : ++ * *) Ask the monitor to store our credentials into the store we specify ++ * *) If it succeeds, maybe do a PAM update ++ */ ++ ++/* Stuff for PAM */ ++ ++#ifdef USE_PAM ++static int ssh_gssapi_simple_conv(int n, const struct pam_message **msg, ++ struct pam_response **resp, void *data) + { +- ctx->major = gss_verify_mic(&ctx->minor, ctx->context, +- gssbuf, gssmic, NULL); ++ return (PAM_CONV_ERR); ++} ++#endif + +- return (ctx->major); ++void ++ssh_gssapi_rekey_creds(void) { ++ int ok; ++#ifdef USE_PAM ++ int ret; ++ pam_handle_t *pamh = NULL; ++ struct pam_conv pamconv = {ssh_gssapi_simple_conv, NULL}; ++ char *envstr; ++#endif ++ ++ if (gssapi_client.store.filename == NULL && ++ gssapi_client.store.envval == NULL && ++ gssapi_client.store.envvar == NULL) ++ return; ++ ++ ok = PRIVSEP(ssh_gssapi_update_creds(&gssapi_client.store)); ++ ++ if (!ok) ++ return; ++ ++ debug("Rekeyed credentials stored successfully"); ++ ++ /* Actually managing to play with the ssh pam stack from here will ++ * be next to impossible. In any case, we may want different options ++ * for rekeying. So, use our own :) ++ */ ++#ifdef USE_PAM ++ if (!use_privsep) { ++ debug("Not even going to try and do PAM with privsep disabled"); ++ return; ++ } ++ ++ ret = pam_start("sshd-rekey", gssapi_client.store.owner->pw_name, ++ &pamconv, &pamh); ++ if (ret) ++ return; ++ ++ xasprintf(&envstr, "%s=%s", gssapi_client.store.envvar, ++ gssapi_client.store.envval); ++ ++ ret = pam_putenv(pamh, envstr); ++ if (!ret) ++ pam_setcred(pamh, PAM_REINITIALIZE_CRED); ++ pam_end(pamh, PAM_SUCCESS); ++#endif ++} ++ ++int ++ssh_gssapi_update_creds(ssh_gssapi_ccache *store) { ++ int ok = 0; ++ ++ /* Check we've got credentials to store */ ++ if (!gssapi_client.updated) ++ return 0; ++ ++ gssapi_client.updated = 0; ++ ++ temporarily_use_uid(gssapi_client.store.owner); ++ if (gssapi_client.mech && gssapi_client.mech->updatecreds) ++ ok = (*gssapi_client.mech->updatecreds)(store, &gssapi_client); ++ else ++ debug("No update function for this mechanism"); ++ ++ restore_uid(); ++ ++ return ok; + } + + /* Privileged */ +diff --git a/hmac.c b/hmac.c +index 1c879640..a29f32c5 100644 +--- a/hmac.c ++++ b/hmac.c +@@ -19,6 +19,7 @@ + + #include + #include ++#include + + #include "sshbuf.h" + #include "digest.h" +diff --git a/kex.c b/kex.c +index 34808b5c..a2a4794e 100644 +--- a/kex.c ++++ b/kex.c +@@ -55,11 +55,16 @@ + #include "misc.h" + #include "dispatch.h" + #include "monitor.h" ++#include "xmalloc.h" + + #include "ssherr.h" + #include "sshbuf.h" + #include "digest.h" + ++#ifdef GSSAPI ++#include "ssh-gss.h" ++#endif ++ + /* prototype */ + static int kex_choose_conf(struct ssh *); + static int kex_input_newkeys(int, u_int32_t, struct ssh *); +@@ -113,15 +118,28 @@ static const struct kexalg kexalgs[] = { + #endif /* HAVE_EVP_SHA256 || !WITH_OPENSSL */ + { NULL, -1, -1, -1}, + }; ++static const struct kexalg gss_kexalgs[] = { ++#ifdef GSSAPI ++ { KEX_GSS_GEX_SHA1_ID, KEX_GSS_GEX_SHA1, 0, SSH_DIGEST_SHA1 }, ++ { KEX_GSS_GRP1_SHA1_ID, KEX_GSS_GRP1_SHA1, 0, SSH_DIGEST_SHA1 }, ++ { KEX_GSS_GRP14_SHA1_ID, KEX_GSS_GRP14_SHA1, 0, SSH_DIGEST_SHA1 }, ++ { KEX_GSS_GRP14_SHA256_ID, KEX_GSS_GRP14_SHA256, 0, SSH_DIGEST_SHA256 }, ++ { KEX_GSS_GRP16_SHA512_ID, KEX_GSS_GRP16_SHA512, 0, SSH_DIGEST_SHA512 }, ++ { KEX_GSS_NISTP256_SHA256_ID, KEX_GSS_NISTP256_SHA256, ++ NID_X9_62_prime256v1, SSH_DIGEST_SHA256 }, ++ { KEX_GSS_C25519_SHA256_ID, KEX_GSS_C25519_SHA256, 0, SSH_DIGEST_SHA256 }, ++#endif ++ { NULL, -1, -1, -1 }, ++}; + +-char * +-kex_alg_list(char sep) ++static char * ++kex_alg_list_internal(char sep, const struct kexalg *algs) + { + char *ret = NULL, *tmp; + size_t nlen, rlen = 0; + const struct kexalg *k; + +- for (k = kexalgs; k->name != NULL; k++) { ++ for (k = algs; k->name != NULL; k++) { + if (ret != NULL) + ret[rlen++] = sep; + nlen = strlen(k->name); +@@ -136,6 +154,18 @@ kex_alg_list(char sep) + return ret; + } + ++char * ++kex_alg_list(char sep) ++{ ++ return kex_alg_list_internal(sep, kexalgs); ++} ++ ++char * ++kex_gss_alg_list(char sep) ++{ ++ return kex_alg_list_internal(sep, gss_kexalgs); ++} ++ + static const struct kexalg * + kex_alg_by_name(const char *name) + { +@@ -145,6 +175,10 @@ kex_alg_by_name(const char *name) + if (strcmp(k->name, name) == 0) + return k; + } ++ for (k = gss_kexalgs; k->name != NULL; k++) { ++ if (strncmp(k->name, name, strlen(k->name)) == 0) ++ return k; ++ } + return NULL; + } + +@@ -301,6 +335,29 @@ kex_assemble_names(char **listp, const char *def, const char *all) + return r; + } + ++/* Validate GSS KEX method name list */ ++int ++kex_gss_names_valid(const char *names) ++{ ++ char *s, *cp, *p; ++ ++ if (names == NULL || *names == '\0') ++ return 0; ++ s = cp = xstrdup(names); ++ for ((p = strsep(&cp, ",")); p && *p != '\0'; ++ (p = strsep(&cp, ","))) { ++ if (strncmp(p, "gss-", 4) != 0 ++ || kex_alg_by_name(p) == NULL) { ++ error("Unsupported KEX algorithm \"%.100s\"", p); ++ free(s); ++ return 0; ++ } ++ } ++ debug3("gss kex names ok: [%s]", names); ++ free(s); ++ return 1; ++} ++ + /* put algorithm proposal into buffer */ + int + kex_prop2buf(struct sshbuf *b, char *proposal[PROPOSAL_MAX]) +@@ -657,6 +714,9 @@ kex_free(struct kex *kex) + sshbuf_free(kex->server_version); + sshbuf_free(kex->client_pub); + free(kex->session_id); ++#ifdef GSSAPI ++ free(kex->gss_host); ++#endif /* GSSAPI */ + free(kex->failed_choice); + free(kex->hostkey_alg); + free(kex->name); +diff --git a/kex.h b/kex.h +index 6d446d1c..f95dc02c 100644 +--- a/kex.h ++++ b/kex.h +@@ -103,6 +103,15 @@ enum kex_exchange { + KEX_ECDH_SHA2, + KEX_C25519_SHA256, + KEX_KEM_SNTRUP4591761X25519_SHA512, ++#ifdef GSSAPI ++ KEX_GSS_GRP1_SHA1, ++ KEX_GSS_GRP14_SHA1, ++ KEX_GSS_GRP14_SHA256, ++ KEX_GSS_GRP16_SHA512, ++ KEX_GSS_GEX_SHA1, ++ KEX_GSS_NISTP256_SHA256, ++ KEX_GSS_C25519_SHA256, ++#endif + KEX_MAX + }; + +@@ -154,6 +163,12 @@ struct kex { + u_int flags; + int hash_alg; + int ec_nid; ++#ifdef GSSAPI ++ int gss_deleg_creds; ++ int gss_trust_dns; ++ char *gss_host; ++ char *gss_client; ++#endif + char *failed_choice; + int (*verify_host_key)(struct sshkey *, struct ssh *); + struct sshkey *(*load_host_public_key)(int, int, struct ssh *); +@@ -175,8 +190,10 @@ struct kex { + + int kex_names_valid(const char *); + char *kex_alg_list(char); ++char *kex_gss_alg_list(char); + char *kex_names_cat(const char *, const char *); + int kex_assemble_names(char **, const char *, const char *); ++int kex_gss_names_valid(const char *); + + int kex_exchange_identification(struct ssh *, int, const char *); + +@@ -203,6 +220,12 @@ int kexgex_client(struct ssh *); + int kexgex_server(struct ssh *); + int kex_gen_client(struct ssh *); + int kex_gen_server(struct ssh *); ++#ifdef GSSAPI ++int kexgssgex_client(struct ssh *); ++int kexgssgex_server(struct ssh *); ++int kexgss_client(struct ssh *); ++int kexgss_server(struct ssh *); ++#endif + + int kex_dh_keypair(struct kex *); + int kex_dh_enc(struct kex *, const struct sshbuf *, struct sshbuf **, +@@ -235,6 +258,12 @@ int kexgex_hash(int, const struct sshbuf *, const struct sshbuf *, + const BIGNUM *, const u_char *, size_t, + u_char *, size_t *); + ++int kex_gen_hash(int hash_alg, const struct sshbuf *client_version, ++ const struct sshbuf *server_version, const struct sshbuf *client_kexinit, ++ const struct sshbuf *server_kexinit, const struct sshbuf *server_host_key_blob, ++ const struct sshbuf *client_pub, const struct sshbuf *server_pub, ++ const struct sshbuf *shared_secret, u_char *hash, size_t *hashlen); ++ + 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))); +diff --git a/kexdh.c b/kexdh.c +index 67133e33..edaa4676 100644 +--- a/kexdh.c ++++ b/kexdh.c +@@ -48,13 +48,23 @@ kex_dh_keygen(struct kex *kex) + { + switch (kex->kex_type) { + case KEX_DH_GRP1_SHA1: ++#ifdef GSSAPI ++ case KEX_GSS_GRP1_SHA1: ++#endif + kex->dh = dh_new_group1(); + break; + case KEX_DH_GRP14_SHA1: + case KEX_DH_GRP14_SHA256: ++#ifdef GSSAPI ++ case KEX_GSS_GRP14_SHA1: ++ case KEX_GSS_GRP14_SHA256: ++#endif + kex->dh = dh_new_group14(); + break; + case KEX_DH_GRP16_SHA512: ++#ifdef GSSAPI ++ case KEX_GSS_GRP16_SHA512: ++#endif + kex->dh = dh_new_group16(); + break; + case KEX_DH_GRP18_SHA512: +diff --git a/kexgen.c b/kexgen.c +index 2abbb9ef..569dc83f 100644 +--- a/kexgen.c ++++ b/kexgen.c +@@ -43,7 +43,7 @@ + static int input_kex_gen_init(int, u_int32_t, struct ssh *); + static int input_kex_gen_reply(int type, u_int32_t seq, struct ssh *ssh); + +-static int ++int + kex_gen_hash( + int hash_alg, + const struct sshbuf *client_version, +diff --git a/kexgssc.c b/kexgssc.c +new file mode 100644 +index 00000000..0b2f6a56 +--- /dev/null ++++ b/kexgssc.c +@@ -0,0 +1,618 @@ ++/* ++ * Copyright (c) 2001-2009 Simon Wilkinson. 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" ++ ++#if defined(GSSAPI) && defined(WITH_OPENSSL) ++ ++#include "includes.h" ++ ++#include ++#include ++ ++#include ++ ++#include "xmalloc.h" ++#include "sshbuf.h" ++#include "ssh2.h" ++#include "sshkey.h" ++#include "cipher.h" ++#include "kex.h" ++#include "log.h" ++#include "packet.h" ++#include "dh.h" ++#include "digest.h" ++#include "ssherr.h" ++ ++#include "ssh-gss.h" ++ ++int ++kexgss_client(struct ssh *ssh) ++{ ++ struct kex *kex = ssh->kex; ++ gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER, ++ recv_tok = GSS_C_EMPTY_BUFFER, ++ gssbuf, msg_tok = GSS_C_EMPTY_BUFFER, *token_ptr; ++ Gssctxt *ctxt; ++ OM_uint32 maj_status, min_status, ret_flags; ++ struct sshbuf *server_blob = NULL; ++ struct sshbuf *shared_secret = NULL; ++ struct sshbuf *server_host_key_blob = NULL; ++ struct sshbuf *empty = NULL; ++ u_char *msg; ++ int type = 0; ++ int first = 1; ++ u_char hash[SSH_DIGEST_MAX_LENGTH]; ++ size_t hashlen; ++ u_char c; ++ int r; ++ ++ /* Initialise our GSSAPI world */ ++ ssh_gssapi_build_ctx(&ctxt); ++ if (ssh_gssapi_id_kex(ctxt, kex->name, kex->kex_type) ++ == GSS_C_NO_OID) ++ fatal("Couldn't identify host exchange"); ++ ++ if (ssh_gssapi_import_name(ctxt, kex->gss_host)) ++ fatal("Couldn't import hostname"); ++ ++ if (kex->gss_client && ++ ssh_gssapi_client_identity(ctxt, kex->gss_client)) ++ fatal("Couldn't acquire client credentials"); ++ ++ /* Step 1 */ ++ switch (kex->kex_type) { ++ case KEX_GSS_GRP1_SHA1: ++ case KEX_GSS_GRP14_SHA1: ++ case KEX_GSS_GRP14_SHA256: ++ case KEX_GSS_GRP16_SHA512: ++ r = kex_dh_keypair(kex); ++ break; ++ case KEX_GSS_NISTP256_SHA256: ++ r = kex_ecdh_keypair(kex); ++ break; ++ case KEX_GSS_C25519_SHA256: ++ r = kex_c25519_keypair(kex); ++ break; ++ default: ++ fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); ++ } ++ if (r != 0) { ++ ssh_gssapi_delete_ctx(&ctxt); ++ return r; ++ } ++ ++ token_ptr = GSS_C_NO_BUFFER; ++ ++ do { ++ debug("Calling gss_init_sec_context"); ++ ++ maj_status = ssh_gssapi_init_ctx(ctxt, ++ kex->gss_deleg_creds, token_ptr, &send_tok, ++ &ret_flags); ++ ++ if (GSS_ERROR(maj_status)) { ++ /* XXX Useles code: Missing send? */ ++ if (send_tok.length != 0) { ++ if ((r = sshpkt_start(ssh, ++ SSH2_MSG_KEXGSS_CONTINUE)) != 0 || ++ (r = sshpkt_put_string(ssh, send_tok.value, ++ send_tok.length)) != 0) ++ fatal("sshpkt failed: %s", ssh_err(r)); ++ } ++ fatal("gss_init_context failed"); ++ } ++ ++ /* If we've got an old receive buffer get rid of it */ ++ if (token_ptr != GSS_C_NO_BUFFER) ++ gss_release_buffer(&min_status, &recv_tok); ++ ++ if (maj_status == GSS_S_COMPLETE) { ++ /* If mutual state flag is not true, kex fails */ ++ if (!(ret_flags & GSS_C_MUTUAL_FLAG)) ++ fatal("Mutual authentication failed"); ++ ++ /* If integ avail flag is not true kex fails */ ++ if (!(ret_flags & GSS_C_INTEG_FLAG)) ++ fatal("Integrity check failed"); ++ } ++ ++ /* ++ * If we have data to send, then the last message that we ++ * received cannot have been a 'complete'. ++ */ ++ if (send_tok.length != 0) { ++ if (first) { ++ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_INIT)) != 0 || ++ (r = sshpkt_put_string(ssh, send_tok.value, ++ send_tok.length)) != 0 || ++ (r = sshpkt_put_stringb(ssh, kex->client_pub)) != 0) ++ fatal("failed to construct packet: %s", ssh_err(r)); ++ first = 0; ++ } else { ++ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 || ++ (r = sshpkt_put_string(ssh, send_tok.value, ++ send_tok.length)) != 0) ++ fatal("failed to construct packet: %s", ssh_err(r)); ++ } ++ if ((r = sshpkt_send(ssh)) != 0) ++ fatal("failed to send packet: %s", ssh_err(r)); ++ gss_release_buffer(&min_status, &send_tok); ++ ++ /* If we've sent them data, they should reply */ ++ do { ++ type = ssh_packet_read(ssh); ++ if (type == SSH2_MSG_KEXGSS_HOSTKEY) { ++ char *tmp = NULL; ++ size_t tmp_len = 0; ++ ++ debug("Received KEXGSS_HOSTKEY"); ++ if (server_host_key_blob) ++ fatal("Server host key received more than once"); ++ if ((r = sshpkt_get_string(ssh, &tmp, &tmp_len)) != 0) ++ fatal("Failed to read server host key: %s", ssh_err(r)); ++ if ((server_host_key_blob = sshbuf_from(tmp, tmp_len)) == NULL) ++ fatal("sshbuf_from failed"); ++ } ++ } while (type == SSH2_MSG_KEXGSS_HOSTKEY); ++ ++ switch (type) { ++ case SSH2_MSG_KEXGSS_CONTINUE: ++ debug("Received GSSAPI_CONTINUE"); ++ if (maj_status == GSS_S_COMPLETE) ++ fatal("GSSAPI Continue received from server when complete"); ++ if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh, ++ &recv_tok)) != 0 || ++ (r = sshpkt_get_end(ssh)) != 0) ++ fatal("Failed to read token: %s", ssh_err(r)); ++ break; ++ case SSH2_MSG_KEXGSS_COMPLETE: ++ debug("Received GSSAPI_COMPLETE"); ++ if (msg_tok.value != NULL) ++ fatal("Received GSSAPI_COMPLETE twice?"); ++ if ((r = sshpkt_getb_froms(ssh, &server_blob)) != 0 || ++ (r = ssh_gssapi_sshpkt_get_buffer_desc(ssh, ++ &msg_tok)) != 0) ++ fatal("Failed to read message: %s", ssh_err(r)); ++ ++ /* Is there a token included? */ ++ if ((r = sshpkt_get_u8(ssh, &c)) != 0) ++ fatal("sshpkt failed: %s", ssh_err(r)); ++ if (c) { ++ if ((r = ssh_gssapi_sshpkt_get_buffer_desc( ++ ssh, &recv_tok)) != 0) ++ fatal("Failed to read token: %s", ssh_err(r)); ++ /* If we're already complete - protocol error */ ++ if (maj_status == GSS_S_COMPLETE) ++ sshpkt_disconnect(ssh, "Protocol error: received token when complete"); ++ } else { ++ /* No token included */ ++ if (maj_status != GSS_S_COMPLETE) ++ sshpkt_disconnect(ssh, "Protocol error: did not receive final token"); ++ } ++ if ((r = sshpkt_get_end(ssh)) != 0) { ++ fatal("Expecting end of packet."); ++ } ++ break; ++ case SSH2_MSG_KEXGSS_ERROR: ++ debug("Received Error"); ++ if ((r = sshpkt_get_u32(ssh, &maj_status)) != 0 || ++ (r = sshpkt_get_u32(ssh, &min_status)) != 0 || ++ (r = sshpkt_get_string(ssh, &msg, NULL)) != 0 || ++ (r = sshpkt_get_string(ssh, NULL, NULL)) != 0 || /* lang tag */ ++ (r = sshpkt_get_end(ssh)) != 0) ++ fatal("sshpkt_get failed: %s", ssh_err(r)); ++ fatal("GSSAPI Error: \n%.400s", msg); ++ default: ++ sshpkt_disconnect(ssh, "Protocol error: didn't expect packet type %d", ++ type); ++ } ++ token_ptr = &recv_tok; ++ } else { ++ /* No data, and not complete */ ++ if (maj_status != GSS_S_COMPLETE) ++ fatal("Not complete, and no token output"); ++ } ++ } while (maj_status & GSS_S_CONTINUE_NEEDED); ++ ++ /* ++ * We _must_ have received a COMPLETE message in reply from the ++ * server, which will have set server_blob and msg_tok ++ */ ++ ++ if (type != SSH2_MSG_KEXGSS_COMPLETE) ++ fatal("Didn't receive a SSH2_MSG_KEXGSS_COMPLETE when I expected it"); ++ ++ /* compute shared secret */ ++ switch (kex->kex_type) { ++ case KEX_GSS_GRP1_SHA1: ++ case KEX_GSS_GRP14_SHA1: ++ case KEX_GSS_GRP14_SHA256: ++ case KEX_GSS_GRP16_SHA512: ++ r = kex_dh_dec(kex, server_blob, &shared_secret); ++ break; ++ case KEX_GSS_C25519_SHA256: ++ if (sshbuf_ptr(server_blob)[sshbuf_len(server_blob)] & 0x80) ++ fatal("The received key has MSB of last octet set!"); ++ r = kex_c25519_dec(kex, server_blob, &shared_secret); ++ break; ++ case KEX_GSS_NISTP256_SHA256: ++ if (sshbuf_len(server_blob) != 65) ++ fatal("The received NIST-P256 key did not match" ++ "expected length (expected 65, got %zu)", sshbuf_len(server_blob)); ++ ++ if (sshbuf_ptr(server_blob)[0] != POINT_CONVERSION_UNCOMPRESSED) ++ fatal("The received NIST-P256 key does not have first octet 0x04"); ++ ++ r = kex_ecdh_dec(kex, server_blob, &shared_secret); ++ break; ++ default: ++ r = SSH_ERR_INVALID_ARGUMENT; ++ break; ++ } ++ if (r != 0) ++ goto out; ++ ++ if ((empty = sshbuf_new()) == NULL) { ++ r = SSH_ERR_ALLOC_FAIL; ++ goto out; ++ } ++ ++ hashlen = sizeof(hash); ++ if ((r = kex_gen_hash( ++ kex->hash_alg, ++ kex->client_version, ++ kex->server_version, ++ kex->my, ++ kex->peer, ++ (server_host_key_blob ? server_host_key_blob : empty), ++ kex->client_pub, ++ server_blob, ++ shared_secret, ++ hash, &hashlen)) != 0) ++ fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); ++ ++ gssbuf.value = hash; ++ gssbuf.length = hashlen; ++ ++ /* Verify that the hash matches the MIC we just got. */ ++ if (GSS_ERROR(ssh_gssapi_checkmic(ctxt, &gssbuf, &msg_tok))) ++ sshpkt_disconnect(ssh, "Hash's MIC didn't verify"); ++ ++ gss_release_buffer(&min_status, &msg_tok); ++ ++ if (kex->gss_deleg_creds) ++ ssh_gssapi_credentials_updated(ctxt); ++ ++ if (gss_kex_context == NULL) ++ gss_kex_context = ctxt; ++ else ++ ssh_gssapi_delete_ctx(&ctxt); ++ ++ if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) == 0) ++ r = kex_send_newkeys(ssh); ++ ++out: ++ explicit_bzero(hash, sizeof(hash)); ++ explicit_bzero(kex->c25519_client_key, sizeof(kex->c25519_client_key)); ++ sshbuf_free(empty); ++ sshbuf_free(server_host_key_blob); ++ sshbuf_free(server_blob); ++ sshbuf_free(shared_secret); ++ sshbuf_free(kex->client_pub); ++ kex->client_pub = NULL; ++ return r; ++} ++ ++int ++kexgssgex_client(struct ssh *ssh) ++{ ++ struct kex *kex = ssh->kex; ++ gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER, ++ recv_tok = GSS_C_EMPTY_BUFFER, gssbuf, ++ msg_tok = GSS_C_EMPTY_BUFFER, *token_ptr; ++ Gssctxt *ctxt; ++ OM_uint32 maj_status, min_status, ret_flags; ++ struct sshbuf *shared_secret = NULL; ++ BIGNUM *p = NULL; ++ BIGNUM *g = NULL; ++ struct sshbuf *buf = NULL; ++ struct sshbuf *server_host_key_blob = NULL; ++ struct sshbuf *server_blob = NULL; ++ BIGNUM *dh_server_pub = NULL; ++ u_char *msg; ++ int type = 0; ++ int first = 1; ++ u_char hash[SSH_DIGEST_MAX_LENGTH]; ++ size_t hashlen; ++ const BIGNUM *pub_key, *dh_p, *dh_g; ++ int nbits = 0, min = DH_GRP_MIN, max = DH_GRP_MAX; ++ struct sshbuf *empty = NULL; ++ u_char c; ++ int r; ++ ++ /* Initialise our GSSAPI world */ ++ ssh_gssapi_build_ctx(&ctxt); ++ if (ssh_gssapi_id_kex(ctxt, kex->name, kex->kex_type) ++ == GSS_C_NO_OID) ++ fatal("Couldn't identify host exchange"); ++ ++ if (ssh_gssapi_import_name(ctxt, kex->gss_host)) ++ fatal("Couldn't import hostname"); ++ ++ if (kex->gss_client && ++ ssh_gssapi_client_identity(ctxt, kex->gss_client)) ++ fatal("Couldn't acquire client credentials"); ++ ++ debug("Doing group exchange"); ++ nbits = dh_estimate(kex->dh_need * 8); ++ ++ kex->min = DH_GRP_MIN; ++ kex->max = DH_GRP_MAX; ++ kex->nbits = nbits; ++ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_GROUPREQ)) != 0 || ++ (r = sshpkt_put_u32(ssh, min)) != 0 || ++ (r = sshpkt_put_u32(ssh, nbits)) != 0 || ++ (r = sshpkt_put_u32(ssh, max)) != 0 || ++ (r = sshpkt_send(ssh)) != 0) ++ fatal("Failed to construct a packet: %s", ssh_err(r)); ++ ++ if ((r = ssh_packet_read_expect(ssh, SSH2_MSG_KEXGSS_GROUP)) != 0) ++ fatal("Error: %s", ssh_err(r)); ++ ++ if ((r = sshpkt_get_bignum2(ssh, &p)) != 0 || ++ (r = sshpkt_get_bignum2(ssh, &g)) != 0 || ++ (r = sshpkt_get_end(ssh)) != 0) ++ fatal("shpkt_get_bignum2 failed: %s", ssh_err(r)); ++ ++ if (BN_num_bits(p) < min || BN_num_bits(p) > max) ++ fatal("GSSGRP_GEX group out of range: %d !< %d !< %d", ++ min, BN_num_bits(p), max); ++ ++ if ((kex->dh = dh_new_group(g, p)) == NULL) ++ fatal("dn_new_group() failed"); ++ p = g = NULL; /* belong to kex->dh now */ ++ ++ if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0) ++ goto out; ++ DH_get0_key(kex->dh, &pub_key, NULL); ++ ++ token_ptr = GSS_C_NO_BUFFER; ++ ++ do { ++ /* Step 2 - call GSS_Init_sec_context() */ ++ debug("Calling gss_init_sec_context"); ++ ++ maj_status = ssh_gssapi_init_ctx(ctxt, ++ kex->gss_deleg_creds, token_ptr, &send_tok, ++ &ret_flags); ++ ++ if (GSS_ERROR(maj_status)) { ++ /* XXX Useles code: Missing send? */ ++ if (send_tok.length != 0) { ++ if ((r = sshpkt_start(ssh, ++ SSH2_MSG_KEXGSS_CONTINUE)) != 0 || ++ (r = sshpkt_put_string(ssh, send_tok.value, ++ send_tok.length)) != 0) ++ fatal("sshpkt failed: %s", ssh_err(r)); ++ } ++ fatal("gss_init_context failed"); ++ } ++ ++ /* If we've got an old receive buffer get rid of it */ ++ if (token_ptr != GSS_C_NO_BUFFER) ++ gss_release_buffer(&min_status, &recv_tok); ++ ++ if (maj_status == GSS_S_COMPLETE) { ++ /* If mutual state flag is not true, kex fails */ ++ if (!(ret_flags & GSS_C_MUTUAL_FLAG)) ++ fatal("Mutual authentication failed"); ++ ++ /* If integ avail flag is not true kex fails */ ++ if (!(ret_flags & GSS_C_INTEG_FLAG)) ++ fatal("Integrity check failed"); ++ } ++ ++ /* ++ * If we have data to send, then the last message that we ++ * received cannot have been a 'complete'. ++ */ ++ if (send_tok.length != 0) { ++ if (first) { ++ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_INIT)) != 0 || ++ (r = sshpkt_put_string(ssh, send_tok.value, ++ send_tok.length)) != 0 || ++ (r = sshpkt_put_bignum2(ssh, pub_key)) != 0) ++ fatal("sshpkt failed: %s", ssh_err(r)); ++ first = 0; ++ } else { ++ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 || ++ (r = sshpkt_put_string(ssh,send_tok.value, ++ send_tok.length)) != 0) ++ fatal("sshpkt failed: %s", ssh_err(r)); ++ } ++ if ((r = sshpkt_send(ssh)) != 0) ++ fatal("sshpkt_send failed: %s", ssh_err(r)); ++ gss_release_buffer(&min_status, &send_tok); ++ ++ /* If we've sent them data, they should reply */ ++ do { ++ type = ssh_packet_read(ssh); ++ if (type == SSH2_MSG_KEXGSS_HOSTKEY) { ++ char *tmp = NULL; ++ size_t tmp_len = 0; ++ ++ debug("Received KEXGSS_HOSTKEY"); ++ if (server_host_key_blob) ++ fatal("Server host key received more than once"); ++ if ((r = sshpkt_get_string(ssh, &tmp, &tmp_len)) != 0) ++ fatal("sshpkt failed: %s", ssh_err(r)); ++ if ((server_host_key_blob = sshbuf_from(tmp, tmp_len)) == NULL) ++ fatal("sshbuf_from failed"); ++ } ++ } while (type == SSH2_MSG_KEXGSS_HOSTKEY); ++ ++ switch (type) { ++ case SSH2_MSG_KEXGSS_CONTINUE: ++ debug("Received GSSAPI_CONTINUE"); ++ if (maj_status == GSS_S_COMPLETE) ++ fatal("GSSAPI Continue received from server when complete"); ++ if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh, ++ &recv_tok)) != 0 || ++ (r = sshpkt_get_end(ssh)) != 0) ++ fatal("sshpkt failed: %s", ssh_err(r)); ++ break; ++ case SSH2_MSG_KEXGSS_COMPLETE: ++ debug("Received GSSAPI_COMPLETE"); ++ if (msg_tok.value != NULL) ++ fatal("Received GSSAPI_COMPLETE twice?"); ++ if ((r = sshpkt_getb_froms(ssh, &server_blob)) != 0 || ++ (r = ssh_gssapi_sshpkt_get_buffer_desc(ssh, ++ &msg_tok)) != 0) ++ fatal("sshpkt failed: %s", ssh_err(r)); ++ ++ /* Is there a token included? */ ++ if ((r = sshpkt_get_u8(ssh, &c)) != 0) ++ fatal("sshpkt failed: %s", ssh_err(r)); ++ if (c) { ++ if ((r = ssh_gssapi_sshpkt_get_buffer_desc( ++ ssh, &recv_tok)) != 0 || ++ (r = sshpkt_get_end(ssh)) != 0) ++ fatal("sshpkt failed: %s", ssh_err(r)); ++ /* If we're already complete - protocol error */ ++ if (maj_status == GSS_S_COMPLETE) ++ sshpkt_disconnect(ssh, "Protocol error: received token when complete"); ++ } else { ++ /* No token included */ ++ if (maj_status != GSS_S_COMPLETE) ++ sshpkt_disconnect(ssh, "Protocol error: did not receive final token"); ++ } ++ break; ++ case SSH2_MSG_KEXGSS_ERROR: ++ debug("Received Error"); ++ if ((r = sshpkt_get_u32(ssh, &maj_status)) != 0 || ++ (r = sshpkt_get_u32(ssh, &min_status)) != 0 || ++ (r = sshpkt_get_string(ssh, &msg, NULL)) != 0 || ++ (r = sshpkt_get_string(ssh, NULL, NULL)) != 0 || /* lang tag */ ++ (r = sshpkt_get_end(ssh)) != 0) ++ fatal("sshpkt failed: %s", ssh_err(r)); ++ fatal("GSSAPI Error: \n%.400s", msg); ++ default: ++ sshpkt_disconnect(ssh, "Protocol error: didn't expect packet type %d", ++ type); ++ } ++ token_ptr = &recv_tok; ++ } else { ++ /* No data, and not complete */ ++ if (maj_status != GSS_S_COMPLETE) ++ fatal("Not complete, and no token output"); ++ } ++ } while (maj_status & GSS_S_CONTINUE_NEEDED); ++ ++ /* ++ * We _must_ have received a COMPLETE message in reply from the ++ * server, which will have set dh_server_pub and msg_tok ++ */ ++ ++ if (type != SSH2_MSG_KEXGSS_COMPLETE) ++ fatal("Didn't receive a SSH2_MSG_KEXGSS_COMPLETE when I expected it"); ++ ++ /* 7. C verifies that the key Q_S is valid */ ++ /* 8. C computes shared secret */ ++ if ((buf = sshbuf_new()) == NULL || ++ (r = sshbuf_put_stringb(buf, server_blob)) != 0 || ++ (r = sshbuf_get_bignum2(buf, &dh_server_pub)) != 0) ++ goto out; ++ sshbuf_free(buf); ++ buf = NULL; ++ ++ if ((shared_secret = sshbuf_new()) == NULL) { ++ r = SSH_ERR_ALLOC_FAIL; ++ goto out; ++ } ++ ++ if ((r = kex_dh_compute_key(kex, dh_server_pub, shared_secret)) != 0) ++ goto out; ++ if ((empty = sshbuf_new()) == NULL) { ++ r = SSH_ERR_ALLOC_FAIL; ++ goto out; ++ } ++ ++ DH_get0_pqg(kex->dh, &dh_p, NULL, &dh_g); ++ hashlen = sizeof(hash); ++ if ((r = kexgex_hash( ++ kex->hash_alg, ++ kex->client_version, ++ kex->server_version, ++ kex->my, ++ kex->peer, ++ (server_host_key_blob ? server_host_key_blob : empty), ++ kex->min, kex->nbits, kex->max, ++ dh_p, dh_g, ++ pub_key, ++ dh_server_pub, ++ sshbuf_ptr(shared_secret), sshbuf_len(shared_secret), ++ hash, &hashlen)) != 0) ++ fatal("Failed to calculate hash: %s", ssh_err(r)); ++ ++ gssbuf.value = hash; ++ gssbuf.length = hashlen; ++ ++ /* Verify that the hash matches the MIC we just got. */ ++ if (GSS_ERROR(ssh_gssapi_checkmic(ctxt, &gssbuf, &msg_tok))) ++ sshpkt_disconnect(ssh, "Hash's MIC didn't verify"); ++ ++ gss_release_buffer(&min_status, &msg_tok); ++ ++ /* save session id */ ++ if (kex->session_id == NULL) { ++ kex->session_id_len = hashlen; ++ kex->session_id = xmalloc(kex->session_id_len); ++ memcpy(kex->session_id, hash, kex->session_id_len); ++ } ++ ++ if (kex->gss_deleg_creds) ++ ssh_gssapi_credentials_updated(ctxt); ++ ++ if (gss_kex_context == NULL) ++ gss_kex_context = ctxt; ++ else ++ ssh_gssapi_delete_ctx(&ctxt); ++ ++ /* Finally derive the keys and send them */ ++ if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) == 0) ++ r = kex_send_newkeys(ssh); ++out: ++ sshbuf_free(buf); ++ sshbuf_free(server_blob); ++ sshbuf_free(empty); ++ explicit_bzero(hash, sizeof(hash)); ++ DH_free(kex->dh); ++ kex->dh = NULL; ++ BN_clear_free(dh_server_pub); ++ sshbuf_free(shared_secret); ++ sshbuf_free(server_host_key_blob); ++ return r; ++} ++#endif /* defined(GSSAPI) && defined(WITH_OPENSSL) */ +diff --git a/kexgsss.c b/kexgsss.c +new file mode 100644 +index 00000000..60bc02de +--- /dev/null ++++ b/kexgsss.c +@@ -0,0 +1,482 @@ ++/* ++ * Copyright (c) 2001-2009 Simon Wilkinson. 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" ++ ++#if defined(GSSAPI) && defined(WITH_OPENSSL) ++ ++#include ++ ++#include ++#include ++ ++#include "xmalloc.h" ++#include "sshbuf.h" ++#include "ssh2.h" ++#include "sshkey.h" ++#include "cipher.h" ++#include "kex.h" ++#include "log.h" ++#include "packet.h" ++#include "dh.h" ++#include "ssh-gss.h" ++#include "monitor_wrap.h" ++#include "misc.h" /* servconf.h needs misc.h for struct ForwardOptions */ ++#include "servconf.h" ++#include "ssh-gss.h" ++#include "digest.h" ++#include "ssherr.h" ++ ++extern ServerOptions options; ++ ++int ++kexgss_server(struct ssh *ssh) ++{ ++ struct kex *kex = ssh->kex; ++ OM_uint32 maj_status, min_status; ++ ++ /* ++ * Some GSSAPI implementations use the input value of ret_flags (an ++ * output variable) as a means of triggering mechanism specific ++ * features. Initializing it to zero avoids inadvertently ++ * activating this non-standard behaviour. ++ */ ++ ++ OM_uint32 ret_flags = 0; ++ gss_buffer_desc gssbuf = {0, NULL}, recv_tok, msg_tok; ++ gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; ++ Gssctxt *ctxt = NULL; ++ struct sshbuf *shared_secret = NULL; ++ struct sshbuf *client_pubkey = NULL; ++ struct sshbuf *server_pubkey = NULL; ++ struct sshbuf *empty = sshbuf_new(); ++ int type = 0; ++ gss_OID oid; ++ char *mechs; ++ u_char hash[SSH_DIGEST_MAX_LENGTH]; ++ size_t hashlen; ++ int r; ++ ++ /* Initialise GSSAPI */ ++ ++ /* If we're rekeying, privsep means that some of the private structures ++ * in the GSSAPI code are no longer available. This kludges them back ++ * into life ++ */ ++ if (!ssh_gssapi_oid_table_ok()) { ++ mechs = ssh_gssapi_server_mechanisms(); ++ free(mechs); ++ } ++ ++ debug2("%s: Identifying %s", __func__, kex->name); ++ oid = ssh_gssapi_id_kex(NULL, kex->name, kex->kex_type); ++ if (oid == GSS_C_NO_OID) ++ fatal("Unknown gssapi mechanism"); ++ ++ debug2("%s: Acquiring credentials", __func__); ++ ++ if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, oid)))) ++ fatal("Unable to acquire credentials for the server"); ++ ++ do { ++ debug("Wait SSH2_MSG_KEXGSS_INIT"); ++ type = ssh_packet_read(ssh); ++ switch(type) { ++ case SSH2_MSG_KEXGSS_INIT: ++ if (gssbuf.value != NULL) ++ fatal("Received KEXGSS_INIT after initialising"); ++ if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh, ++ &recv_tok)) != 0 || ++ (r = sshpkt_getb_froms(ssh, &client_pubkey)) != 0 || ++ (r = sshpkt_get_end(ssh)) != 0) ++ fatal("sshpkt failed: %s", ssh_err(r)); ++ ++ switch (kex->kex_type) { ++ case KEX_GSS_GRP1_SHA1: ++ case KEX_GSS_GRP14_SHA1: ++ case KEX_GSS_GRP14_SHA256: ++ case KEX_GSS_GRP16_SHA512: ++ r = kex_dh_enc(kex, client_pubkey, &server_pubkey, ++ &shared_secret); ++ break; ++ case KEX_GSS_NISTP256_SHA256: ++ r = kex_ecdh_enc(kex, client_pubkey, &server_pubkey, ++ &shared_secret); ++ break; ++ case KEX_GSS_C25519_SHA256: ++ r = kex_c25519_enc(kex, client_pubkey, &server_pubkey, ++ &shared_secret); ++ break; ++ default: ++ fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); ++ } ++ if (r != 0) ++ goto out; ++ ++ /* Send SSH_MSG_KEXGSS_HOSTKEY here, if we want */ ++ ++ /* Calculate the hash early so we can free the ++ * client_pubkey, which has reference to the parent ++ * buffer state->incoming_packet ++ */ ++ hashlen = sizeof(hash); ++ if ((r = kex_gen_hash( ++ kex->hash_alg, ++ kex->client_version, ++ kex->server_version, ++ kex->peer, ++ kex->my, ++ empty, ++ client_pubkey, ++ server_pubkey, ++ shared_secret, ++ hash, &hashlen)) != 0) ++ goto out; ++ ++ gssbuf.value = hash; ++ gssbuf.length = hashlen; ++ ++ sshbuf_free(client_pubkey); ++ client_pubkey = NULL; ++ ++ break; ++ case SSH2_MSG_KEXGSS_CONTINUE: ++ if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh, ++ &recv_tok)) != 0 || ++ (r = sshpkt_get_end(ssh)) != 0) ++ fatal("sshpkt failed: %s", ssh_err(r)); ++ break; ++ default: ++ sshpkt_disconnect(ssh, ++ "Protocol error: didn't expect packet type %d", ++ type); ++ } ++ ++ maj_status = PRIVSEP(ssh_gssapi_accept_ctx(ctxt, &recv_tok, ++ &send_tok, &ret_flags)); ++ ++ gss_release_buffer(&min_status, &recv_tok); ++ ++ if (maj_status != GSS_S_COMPLETE && send_tok.length == 0) ++ fatal("Zero length token output when incomplete"); ++ ++ if (gssbuf.value == NULL) ++ fatal("No client public key"); ++ ++ if (maj_status & GSS_S_CONTINUE_NEEDED) { ++ debug("Sending GSSAPI_CONTINUE"); ++ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 || ++ (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0 || ++ (r = sshpkt_send(ssh)) != 0) ++ fatal("sshpkt failed: %s", ssh_err(r)); ++ gss_release_buffer(&min_status, &send_tok); ++ } ++ } while (maj_status & GSS_S_CONTINUE_NEEDED); ++ ++ if (GSS_ERROR(maj_status)) { ++ if (send_tok.length > 0) { ++ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 || ++ (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0 || ++ (r = sshpkt_send(ssh)) != 0) ++ fatal("sshpkt failed: %s", ssh_err(r)); ++ } ++ fatal("accept_ctx died"); ++ } ++ ++ if (!(ret_flags & GSS_C_MUTUAL_FLAG)) ++ fatal("Mutual Authentication flag wasn't set"); ++ ++ if (!(ret_flags & GSS_C_INTEG_FLAG)) ++ fatal("Integrity flag wasn't set"); ++ ++ if (GSS_ERROR(PRIVSEP(ssh_gssapi_sign(ctxt, &gssbuf, &msg_tok)))) ++ fatal("Couldn't get MIC"); ++ ++ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_COMPLETE)) != 0 || ++ (r = sshpkt_put_stringb(ssh, server_pubkey)) != 0 || ++ (r = sshpkt_put_string(ssh, msg_tok.value, msg_tok.length)) != 0) ++ fatal("sshpkt failed: %s", ssh_err(r)); ++ ++ if (send_tok.length != 0) { ++ if ((r = sshpkt_put_u8(ssh, 1)) != 0 || /* true */ ++ (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0) ++ fatal("sshpkt failed: %s", ssh_err(r)); ++ } else { ++ if ((r = sshpkt_put_u8(ssh, 0)) != 0) /* false */ ++ fatal("sshpkt failed: %s", ssh_err(r)); ++ } ++ if ((r = sshpkt_send(ssh)) != 0) ++ fatal("sshpkt_send failed: %s", ssh_err(r)); ++ ++ gss_release_buffer(&min_status, &send_tok); ++ gss_release_buffer(&min_status, &msg_tok); ++ ++ if (gss_kex_context == NULL) ++ gss_kex_context = ctxt; ++ else ++ ssh_gssapi_delete_ctx(&ctxt); ++ ++ if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) == 0) ++ r = kex_send_newkeys(ssh); ++ ++ /* If this was a rekey, then save out any delegated credentials we ++ * just exchanged. */ ++ if (options.gss_store_rekey) ++ ssh_gssapi_rekey_creds(); ++out: ++ sshbuf_free(empty); ++ explicit_bzero(hash, sizeof(hash)); ++ sshbuf_free(shared_secret); ++ sshbuf_free(client_pubkey); ++ sshbuf_free(server_pubkey); ++ return r; ++} ++ ++int ++kexgssgex_server(struct ssh *ssh) ++{ ++ struct kex *kex = ssh->kex; ++ OM_uint32 maj_status, min_status; ++ ++ /* ++ * Some GSSAPI implementations use the input value of ret_flags (an ++ * output variable) as a means of triggering mechanism specific ++ * features. Initializing it to zero avoids inadvertently ++ * activating this non-standard behaviour. ++ */ ++ ++ OM_uint32 ret_flags = 0; ++ gss_buffer_desc gssbuf, recv_tok, msg_tok; ++ gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; ++ Gssctxt *ctxt = NULL; ++ struct sshbuf *shared_secret = NULL; ++ int type = 0; ++ gss_OID oid; ++ char *mechs; ++ u_char hash[SSH_DIGEST_MAX_LENGTH]; ++ size_t hashlen; ++ BIGNUM *dh_client_pub = NULL; ++ const BIGNUM *pub_key, *dh_p, *dh_g; ++ int min = -1, max = -1, nbits = -1; ++ int cmin = -1, cmax = -1; /* client proposal */ ++ struct sshbuf *empty = sshbuf_new(); ++ int r; ++ ++ /* Initialise GSSAPI */ ++ ++ /* If we're rekeying, privsep means that some of the private structures ++ * in the GSSAPI code are no longer available. This kludges them back ++ * into life ++ */ ++ if (!ssh_gssapi_oid_table_ok()) ++ if ((mechs = ssh_gssapi_server_mechanisms())) ++ free(mechs); ++ ++ debug2("%s: Identifying %s", __func__, kex->name); ++ oid = ssh_gssapi_id_kex(NULL, kex->name, kex->kex_type); ++ if (oid == GSS_C_NO_OID) ++ fatal("Unknown gssapi mechanism"); ++ ++ debug2("%s: Acquiring credentials", __func__); ++ ++ if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, oid)))) ++ fatal("Unable to acquire credentials for the server"); ++ ++ /* 5. S generates an ephemeral key pair (do the allocations early) */ ++ debug("Doing group exchange"); ++ ssh_packet_read_expect(ssh, SSH2_MSG_KEXGSS_GROUPREQ); ++ /* store client proposal to provide valid signature */ ++ if ((r = sshpkt_get_u32(ssh, &cmin)) != 0 || ++ (r = sshpkt_get_u32(ssh, &nbits)) != 0 || ++ (r = sshpkt_get_u32(ssh, &cmax)) != 0 || ++ (r = sshpkt_get_end(ssh)) != 0) ++ fatal("sshpkt failed: %s", ssh_err(r)); ++ kex->nbits = nbits; ++ kex->min = cmin; ++ kex->max = cmax; ++ min = MAX(DH_GRP_MIN, cmin); ++ max = MIN(DH_GRP_MAX, cmax); ++ nbits = MAXIMUM(DH_GRP_MIN, nbits); ++ nbits = MINIMUM(DH_GRP_MAX, nbits); ++ if (max < min || nbits < min || max < nbits) ++ fatal("GSS_GEX, bad parameters: %d !< %d !< %d", ++ min, nbits, max); ++ kex->dh = PRIVSEP(choose_dh(min, nbits, max)); ++ if (kex->dh == NULL) { ++ sshpkt_disconnect(ssh, "Protocol error: no matching group found"); ++ fatal("Protocol error: no matching group found"); ++ } ++ ++ DH_get0_pqg(kex->dh, &dh_p, NULL, &dh_g); ++ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_GROUP)) != 0 || ++ (r = sshpkt_put_bignum2(ssh, dh_p)) != 0 || ++ (r = sshpkt_put_bignum2(ssh, dh_g)) != 0 || ++ (r = sshpkt_send(ssh)) != 0) ++ fatal("sshpkt failed: %s", ssh_err(r)); ++ ++ if ((r = ssh_packet_write_wait(ssh)) != 0) ++ fatal("ssh_packet_write_wait: %s", ssh_err(r)); ++ ++ /* Compute our exchange value in parallel with the client */ ++ if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0) ++ goto out; ++ ++ do { ++ debug("Wait SSH2_MSG_GSSAPI_INIT"); ++ type = ssh_packet_read(ssh); ++ switch(type) { ++ case SSH2_MSG_KEXGSS_INIT: ++ if (dh_client_pub != NULL) ++ fatal("Received KEXGSS_INIT after initialising"); ++ if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh, ++ &recv_tok)) != 0 || ++ (r = sshpkt_get_bignum2(ssh, &dh_client_pub)) != 0 || ++ (r = sshpkt_get_end(ssh)) != 0) ++ fatal("sshpkt failed: %s", ssh_err(r)); ++ ++ /* Send SSH_MSG_KEXGSS_HOSTKEY here, if we want */ ++ break; ++ case SSH2_MSG_KEXGSS_CONTINUE: ++ if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh, ++ &recv_tok)) != 0 || ++ (r = sshpkt_get_end(ssh)) != 0) ++ fatal("sshpkt failed: %s", ssh_err(r)); ++ break; ++ default: ++ sshpkt_disconnect(ssh, ++ "Protocol error: didn't expect packet type %d", ++ type); ++ } ++ ++ maj_status = PRIVSEP(ssh_gssapi_accept_ctx(ctxt, &recv_tok, ++ &send_tok, &ret_flags)); ++ ++ gss_release_buffer(&min_status, &recv_tok); ++ ++ if (maj_status != GSS_S_COMPLETE && send_tok.length == 0) ++ fatal("Zero length token output when incomplete"); ++ ++ if (dh_client_pub == NULL) ++ fatal("No client public key"); ++ ++ if (maj_status & GSS_S_CONTINUE_NEEDED) { ++ debug("Sending GSSAPI_CONTINUE"); ++ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 || ++ (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0 || ++ (r = sshpkt_send(ssh)) != 0) ++ fatal("sshpkt failed: %s", ssh_err(r)); ++ gss_release_buffer(&min_status, &send_tok); ++ } ++ } while (maj_status & GSS_S_CONTINUE_NEEDED); ++ ++ if (GSS_ERROR(maj_status)) { ++ if (send_tok.length > 0) { ++ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 || ++ (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0 || ++ (r = sshpkt_send(ssh)) != 0) ++ fatal("sshpkt failed: %s", ssh_err(r)); ++ } ++ fatal("accept_ctx died"); ++ } ++ ++ if (!(ret_flags & GSS_C_MUTUAL_FLAG)) ++ fatal("Mutual Authentication flag wasn't set"); ++ ++ if (!(ret_flags & GSS_C_INTEG_FLAG)) ++ fatal("Integrity flag wasn't set"); ++ ++ /* calculate shared secret */ ++ if ((shared_secret = sshbuf_new()) == NULL) { ++ r = SSH_ERR_ALLOC_FAIL; ++ goto out; ++ } ++ if ((r = kex_dh_compute_key(kex, dh_client_pub, shared_secret)) != 0) ++ goto out; ++ ++ DH_get0_key(kex->dh, &pub_key, NULL); ++ DH_get0_pqg(kex->dh, &dh_p, NULL, &dh_g); ++ hashlen = sizeof(hash); ++ if ((r = kexgex_hash( ++ kex->hash_alg, ++ kex->client_version, ++ kex->server_version, ++ kex->peer, ++ kex->my, ++ empty, ++ cmin, nbits, cmax, ++ dh_p, dh_g, ++ dh_client_pub, ++ pub_key, ++ sshbuf_ptr(shared_secret), sshbuf_len(shared_secret), ++ hash, &hashlen)) != 0) ++ fatal("kexgex_hash failed: %s", ssh_err(r)); ++ ++ gssbuf.value = hash; ++ gssbuf.length = hashlen; ++ ++ if (GSS_ERROR(PRIVSEP(ssh_gssapi_sign(ctxt, &gssbuf, &msg_tok)))) ++ fatal("Couldn't get MIC"); ++ ++ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_COMPLETE)) != 0 || ++ (r = sshpkt_put_bignum2(ssh, pub_key)) != 0 || ++ (r = sshpkt_put_string(ssh, msg_tok.value, msg_tok.length)) != 0) ++ fatal("sshpkt failed: %s", ssh_err(r)); ++ ++ if (send_tok.length != 0) { ++ if ((r = sshpkt_put_u8(ssh, 1)) != 0 || /* true */ ++ (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0) ++ fatal("sshpkt failed: %s", ssh_err(r)); ++ } else { ++ if ((r = sshpkt_put_u8(ssh, 0)) != 0) /* false */ ++ fatal("sshpkt failed: %s", ssh_err(r)); ++ } ++ if ((r = sshpkt_send(ssh)) != 0) ++ fatal("sshpkt failed: %s", ssh_err(r)); ++ ++ gss_release_buffer(&min_status, &send_tok); ++ gss_release_buffer(&min_status, &msg_tok); ++ ++ if (gss_kex_context == NULL) ++ gss_kex_context = ctxt; ++ else ++ ssh_gssapi_delete_ctx(&ctxt); ++ ++ /* Finally derive the keys and send them */ ++ if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) == 0) ++ r = kex_send_newkeys(ssh); ++ ++ /* If this was a rekey, then save out any delegated credentials we ++ * just exchanged. */ ++ if (options.gss_store_rekey) ++ ssh_gssapi_rekey_creds(); ++out: ++ sshbuf_free(empty); ++ explicit_bzero(hash, sizeof(hash)); ++ DH_free(kex->dh); ++ kex->dh = NULL; ++ BN_clear_free(dh_client_pub); ++ sshbuf_free(shared_secret); ++ return r; ++} ++#endif /* defined(GSSAPI) && defined(WITH_OPENSSL) */ +diff --git a/mac.c b/mac.c +index 51dc11d7..3d11eba6 100644 +--- a/mac.c ++++ b/mac.c +@@ -29,6 +29,7 @@ + + #include + #include ++#include + + #include "digest.h" + #include "hmac.h" +diff --git a/monitor.c b/monitor.c +index 60e52944..669cdb4a 100644 +--- a/monitor.c ++++ b/monitor.c +@@ -147,6 +147,8 @@ 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 *); ++int mm_answer_gss_sign(struct ssh*, int, struct sshbuf *); ++int mm_answer_gss_updatecreds(struct ssh*, int, struct sshbuf *); + #endif + + #ifdef SSH_AUDIT_EVENTS +@@ -219,11 +221,18 @@ struct mon_table mon_dispatch_proto20[] = { + {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}, ++ {MONITOR_REQ_GSSSIGN, MON_ONCE, mm_answer_gss_sign}, + #endif + {0, 0, NULL} + }; + + struct mon_table mon_dispatch_postauth20[] = { ++#ifdef GSSAPI ++ {MONITOR_REQ_GSSSETUP, 0, mm_answer_gss_setup_ctx}, ++ {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx}, ++ {MONITOR_REQ_GSSSIGN, 0, mm_answer_gss_sign}, ++ {MONITOR_REQ_GSSUPCREDS, 0, mm_answer_gss_updatecreds}, ++#endif + #ifdef WITH_OPENSSL + {MONITOR_REQ_MODULI, 0, mm_answer_moduli}, + #endif +@@ -292,6 +301,10 @@ monitor_child_preauth(struct ssh *ssh, struct monitor *pmonitor) + /* Permit requests for moduli and signatures */ + monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1); + monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); ++#ifdef GSSAPI ++ /* and for the GSSAPI key exchange */ ++ monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1); ++#endif + + /* The first few requests do not require asynchronous access */ + while (!authenticated) { +@@ -405,6 +418,10 @@ monitor_child_postauth(struct ssh *ssh, struct monitor *pmonitor) + monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1); + monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); + monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1); ++#ifdef GSSAPI ++ /* and for the GSSAPI key exchange */ ++ monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1); ++#endif + + if (auth_opts->permit_pty_flag) { + monitor_permit(mon_dispatch, MONITOR_REQ_PTY, 1); +@@ -1687,6 +1704,17 @@ monitor_apply_keystate(struct ssh *ssh, struct monitor *pmonitor) + # ifdef OPENSSL_HAS_ECC + kex->kex[KEX_ECDH_SHA2] = kex_gen_server; + # endif ++# ifdef GSSAPI ++ if (options.gss_keyex) { ++ kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server; ++ kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server; ++ kex->kex[KEX_GSS_GRP14_SHA256] = kexgss_server; ++ kex->kex[KEX_GSS_GRP16_SHA512] = kexgss_server; ++ kex->kex[KEX_GSS_GEX_SHA1] = kexgssgex_server; ++ kex->kex[KEX_GSS_NISTP256_SHA256] = kexgss_server; ++ kex->kex[KEX_GSS_C25519_SHA256] = kexgss_server; ++ } ++# endif + #endif /* WITH_OPENSSL */ + kex->kex[KEX_C25519_SHA256] = kex_gen_server; + kex->kex[KEX_KEM_SNTRUP4591761X25519_SHA512] = kex_gen_server; +@@ -1780,8 +1808,8 @@ mm_answer_gss_setup_ctx(struct ssh *ssh, int sock, struct sshbuf *m) + u_char *p; + int r; + +- if (!options.gss_authentication) +- fatal("%s: GSSAPI authentication not enabled", __func__); ++ if (!options.gss_authentication && !options.gss_keyex) ++ fatal("%s: GSSAPI not enabled", __func__); + + if ((r = sshbuf_get_string(m, &p, &len)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); +@@ -1813,8 +1841,8 @@ mm_answer_gss_accept_ctx(struct ssh *ssh, int sock, struct sshbuf *m) + OM_uint32 flags = 0; /* GSI needs this */ + int r; + +- if (!options.gss_authentication) +- fatal("%s: GSSAPI authentication not enabled", __func__); ++ if (!options.gss_authentication && !options.gss_keyex) ++ fatal("%s: GSSAPI not enabled", __func__); + + if ((r = ssh_gssapi_get_buffer_desc(m, &in)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); +@@ -1834,6 +1862,7 @@ mm_answer_gss_accept_ctx(struct ssh *ssh, int sock, struct sshbuf *m) + monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP, 0); + monitor_permit(mon_dispatch, MONITOR_REQ_GSSUSEROK, 1); + monitor_permit(mon_dispatch, MONITOR_REQ_GSSCHECKMIC, 1); ++ monitor_permit(mon_dispatch, MONITOR_REQ_GSSSIGN, 1); + } + return (0); + } +@@ -1845,8 +1874,8 @@ mm_answer_gss_checkmic(struct ssh *ssh, int sock, struct sshbuf *m) + OM_uint32 ret; + int r; + +- if (!options.gss_authentication) +- fatal("%s: GSSAPI authentication not enabled", __func__); ++ if (!options.gss_authentication && !options.gss_keyex) ++ fatal("%s: GSSAPI not enabled", __func__); + + if ((r = ssh_gssapi_get_buffer_desc(m, &gssbuf)) != 0 || + (r = ssh_gssapi_get_buffer_desc(m, &mic)) != 0) +@@ -1872,13 +1901,17 @@ mm_answer_gss_checkmic(struct ssh *ssh, int sock, struct sshbuf *m) + int + mm_answer_gss_userok(struct ssh *ssh, int sock, struct sshbuf *m) + { +- int r, authenticated; ++ int r, authenticated, kex; + const char *displayname; + +- if (!options.gss_authentication) +- fatal("%s: GSSAPI authentication not enabled", __func__); ++ if (!options.gss_authentication && !options.gss_keyex) ++ fatal("%s: GSSAPI not enabled", __func__); + +- authenticated = authctxt->valid && ssh_gssapi_userok(authctxt->user); ++ if ((r = sshbuf_get_u32(m, &kex)) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ ++ authenticated = authctxt->valid && ++ ssh_gssapi_userok(authctxt->user, authctxt->pw, kex); + + sshbuf_reset(m); + if ((r = sshbuf_put_u32(m, authenticated)) != 0) +@@ -1887,7 +1920,11 @@ mm_answer_gss_userok(struct ssh *ssh, int sock, struct sshbuf *m) + debug3("%s: sending result %d", __func__, authenticated); + mm_request_send(sock, MONITOR_ANS_GSSUSEROK, m); + +- auth_method = "gssapi-with-mic"; ++ if (kex) { ++ auth_method = "gssapi-keyex"; ++ } else { ++ auth_method = "gssapi-with-mic"; ++ } + + if ((displayname = ssh_gssapi_displayname()) != NULL) + auth2_record_info(authctxt, "%s", displayname); +@@ -1895,5 +1932,85 @@ mm_answer_gss_userok(struct ssh *ssh, int sock, struct sshbuf *m) + /* Monitor loop will terminate if authenticated */ + return (authenticated); + } ++ ++int ++mm_answer_gss_sign(struct ssh *ssh, int socket, struct sshbuf *m) ++{ ++ gss_buffer_desc data; ++ gss_buffer_desc hash = GSS_C_EMPTY_BUFFER; ++ OM_uint32 major, minor; ++ size_t len; ++ u_char *p = NULL; ++ int r; ++ ++ if (!options.gss_authentication && !options.gss_keyex) ++ fatal("%s: GSSAPI not enabled", __func__); ++ ++ if ((r = sshbuf_get_string(m, &p, &len)) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ data.value = p; ++ data.length = len; ++ /* Lengths of SHA-1, SHA-256 and SHA-512 hashes that are used */ ++ if (data.length != 20 && data.length != 32 && data.length != 64) ++ fatal("%s: data length incorrect: %d", __func__, ++ (int) data.length); ++ ++ /* Save the session ID on the first time around */ ++ if (session_id2_len == 0) { ++ session_id2_len = data.length; ++ session_id2 = xmalloc(session_id2_len); ++ memcpy(session_id2, data.value, session_id2_len); ++ } ++ major = ssh_gssapi_sign(gsscontext, &data, &hash); ++ ++ free(data.value); ++ ++ sshbuf_reset(m); ++ ++ if ((r = sshbuf_put_u32(m, major)) != 0 || ++ (r = sshbuf_put_string(m, hash.value, hash.length)) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ ++ mm_request_send(socket, MONITOR_ANS_GSSSIGN, m); ++ ++ gss_release_buffer(&minor, &hash); ++ ++ /* Turn on getpwnam permissions */ ++ monitor_permit(mon_dispatch, MONITOR_REQ_PWNAM, 1); ++ ++ /* And credential updating, for when rekeying */ ++ monitor_permit(mon_dispatch, MONITOR_REQ_GSSUPCREDS, 1); ++ ++ return (0); ++} ++ ++int ++mm_answer_gss_updatecreds(struct ssh *ssh, int socket, struct sshbuf *m) { ++ ssh_gssapi_ccache store; ++ int r, ok; ++ ++ if (!options.gss_authentication && !options.gss_keyex) ++ fatal("%s: GSSAPI not enabled", __func__); ++ ++ if ((r = sshbuf_get_string(m, (u_char **)&store.filename, NULL)) != 0 || ++ (r = sshbuf_get_string(m, (u_char **)&store.envvar, NULL)) != 0 || ++ (r = sshbuf_get_string(m, (u_char **)&store.envval, NULL)) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ ++ ok = ssh_gssapi_update_creds(&store); ++ ++ free(store.filename); ++ free(store.envvar); ++ free(store.envval); ++ ++ sshbuf_reset(m); ++ if ((r = sshbuf_put_u32(m, ok)) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ ++ mm_request_send(socket, MONITOR_ANS_GSSUPCREDS, m); ++ ++ return(0); ++} ++ + #endif /* GSSAPI */ + +diff --git a/monitor.h b/monitor.h +index 683e5e07..2b1a2d59 100644 +--- a/monitor.h ++++ b/monitor.h +@@ -63,6 +63,8 @@ enum monitor_reqtype { + MONITOR_REQ_PAM_FREE_CTX = 110, MONITOR_ANS_PAM_FREE_CTX = 111, + MONITOR_REQ_AUDIT_EVENT = 112, MONITOR_REQ_AUDIT_COMMAND = 113, + ++ MONITOR_REQ_GSSSIGN = 150, MONITOR_ANS_GSSSIGN = 151, ++ MONITOR_REQ_GSSUPCREDS = 152, MONITOR_ANS_GSSUPCREDS = 153, + }; + + struct ssh; +diff --git a/monitor_wrap.c b/monitor_wrap.c +index 186e8f02..8e4c1c1f 100644 +--- a/monitor_wrap.c ++++ b/monitor_wrap.c +@@ -978,13 +978,15 @@ mm_ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic) + } + + int +-mm_ssh_gssapi_userok(char *user) ++mm_ssh_gssapi_userok(char *user, struct passwd *pw, int kex) + { + struct sshbuf *m; + int r, authenticated = 0; + + if ((m = sshbuf_new()) == NULL) + fatal("%s: sshbuf_new failed", __func__); ++ if ((r = sshbuf_put_u32(m, kex)) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); + + mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSUSEROK, m); + mm_request_receive_expect(pmonitor->m_recvfd, +@@ -997,4 +999,57 @@ mm_ssh_gssapi_userok(char *user) + debug3("%s: user %sauthenticated",__func__, authenticated ? "" : "not "); + return (authenticated); + } ++ ++OM_uint32 ++mm_ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_desc *data, gss_buffer_desc *hash) ++{ ++ struct sshbuf *m; ++ OM_uint32 major; ++ int r; ++ ++ if ((m = sshbuf_new()) == NULL) ++ fatal("%s: sshbuf_new failed", __func__); ++ if ((r = sshbuf_put_string(m, data->value, data->length)) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ ++ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSSIGN, m); ++ mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSSIGN, m); ++ ++ if ((r = sshbuf_get_u32(m, &major)) != 0 || ++ (r = ssh_gssapi_get_buffer_desc(m, hash)) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ ++ sshbuf_free(m); ++ ++ return (major); ++} ++ ++int ++mm_ssh_gssapi_update_creds(ssh_gssapi_ccache *store) ++{ ++ struct sshbuf *m; ++ int r, ok; ++ ++ if ((m = sshbuf_new()) == NULL) ++ fatal("%s: sshbuf_new failed", __func__); ++ ++ if ((r = sshbuf_put_cstring(m, ++ store->filename ? store->filename : "")) != 0 || ++ (r = sshbuf_put_cstring(m, ++ store->envvar ? store->envvar : "")) != 0 || ++ (r = sshbuf_put_cstring(m, ++ store->envval ? store->envval : "")) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ ++ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSUPCREDS, m); ++ mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSUPCREDS, m); ++ ++ if ((r = sshbuf_get_u32(m, &ok)) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ ++ sshbuf_free(m); ++ ++ return (ok); ++} ++ + #endif /* GSSAPI */ +diff --git a/monitor_wrap.h b/monitor_wrap.h +index fdebb3aa..69164a8c 100644 +--- a/monitor_wrap.h ++++ b/monitor_wrap.h +@@ -61,8 +61,10 @@ int mm_sshkey_verify(const struct sshkey *, const u_char *, size_t, + OM_uint32 mm_ssh_gssapi_server_ctx(Gssctxt **, gss_OID); + OM_uint32 mm_ssh_gssapi_accept_ctx(Gssctxt *, + gss_buffer_desc *, gss_buffer_desc *, OM_uint32 *); +-int mm_ssh_gssapi_userok(char *user); ++int mm_ssh_gssapi_userok(char *user, struct passwd *, int kex); + OM_uint32 mm_ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t); ++OM_uint32 mm_ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t); ++int mm_ssh_gssapi_update_creds(ssh_gssapi_ccache *); + #endif + + #ifdef USE_PAM +diff --git a/readconf.c b/readconf.c +index ec497e79..4d699e5f 100644 +--- a/readconf.c ++++ b/readconf.c +@@ -67,6 +67,7 @@ + #include "uidswap.h" + #include "myproposal.h" + #include "digest.h" ++#include "ssh-gss.h" + + /* Format of the configuration file: + +@@ -162,6 +163,8 @@ typedef enum { + oClearAllForwardings, oNoHostAuthenticationForLocalhost, + oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout, + oAddressFamily, oGssAuthentication, oGssDelegateCreds, ++ oGssTrustDns, oGssKeyEx, oGssClientIdentity, oGssRenewalRekey, ++ oGssServerIdentity, oGssKexAlgorithms, + oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly, + oSendEnv, oSetEnv, oControlPath, oControlMaster, oControlPersist, + oHashKnownHosts, +@@ -202,10 +205,22 @@ static struct { + /* Sometimes-unsupported options */ + #if defined(GSSAPI) + { "gssapiauthentication", oGssAuthentication }, ++ { "gssapikeyexchange", oGssKeyEx }, + { "gssapidelegatecredentials", oGssDelegateCreds }, ++ { "gssapitrustdns", oGssTrustDns }, ++ { "gssapiclientidentity", oGssClientIdentity }, ++ { "gssapiserveridentity", oGssServerIdentity }, ++ { "gssapirenewalforcesrekey", oGssRenewalRekey }, ++ { "gssapikexalgorithms", oGssKexAlgorithms }, + # else + { "gssapiauthentication", oUnsupported }, ++ { "gssapikeyexchange", oUnsupported }, + { "gssapidelegatecredentials", oUnsupported }, ++ { "gssapitrustdns", oUnsupported }, ++ { "gssapiclientidentity", oUnsupported }, ++ { "gssapiserveridentity", oUnsupported }, ++ { "gssapirenewalforcesrekey", oUnsupported }, ++ { "gssapikexalgorithms", oUnsupported }, + #endif + #ifdef ENABLE_PKCS11 + { "pkcs11provider", oPKCS11Provider }, +@@ -983,10 +998,42 @@ parse_time: + intptr = &options->gss_authentication; + goto parse_flag; + ++ case oGssKeyEx: ++ intptr = &options->gss_keyex; ++ goto parse_flag; ++ + case oGssDelegateCreds: + intptr = &options->gss_deleg_creds; + goto parse_flag; + ++ case oGssTrustDns: ++ intptr = &options->gss_trust_dns; ++ goto parse_flag; ++ ++ case oGssClientIdentity: ++ charptr = &options->gss_client_identity; ++ goto parse_string; ++ ++ case oGssServerIdentity: ++ charptr = &options->gss_server_identity; ++ goto parse_string; ++ ++ case oGssRenewalRekey: ++ intptr = &options->gss_renewal_rekey; ++ goto parse_flag; ++ ++ case oGssKexAlgorithms: ++ arg = strdelim(&s); ++ if (!arg || *arg == '\0') ++ fatal("%.200s line %d: Missing argument.", ++ filename, linenum); ++ if (!kex_gss_names_valid(arg)) ++ fatal("%.200s line %d: Bad GSSAPI KexAlgorithms '%s'.", ++ filename, linenum, arg ? arg : ""); ++ if (*activep && options->gss_kex_algorithms == NULL) ++ options->gss_kex_algorithms = xstrdup(arg); ++ break; ++ + case oBatchMode: + intptr = &options->batch_mode; + goto parse_flag; +@@ -1854,7 +1901,13 @@ initialize_options(Options * options) + options->pubkey_authentication = -1; + options->challenge_response_authentication = -1; + options->gss_authentication = -1; ++ options->gss_keyex = -1; + options->gss_deleg_creds = -1; ++ options->gss_trust_dns = -1; ++ options->gss_renewal_rekey = -1; ++ options->gss_client_identity = NULL; ++ options->gss_server_identity = NULL; ++ options->gss_kex_algorithms = NULL; + options->password_authentication = -1; + options->kbd_interactive_authentication = -1; + options->kbd_interactive_devices = NULL; +@@ -2000,8 +2053,18 @@ fill_default_options(Options * options) + options->challenge_response_authentication = 1; + if (options->gss_authentication == -1) + options->gss_authentication = 0; ++ if (options->gss_keyex == -1) ++ options->gss_keyex = 0; + if (options->gss_deleg_creds == -1) + options->gss_deleg_creds = 0; ++ if (options->gss_trust_dns == -1) ++ options->gss_trust_dns = 0; ++ if (options->gss_renewal_rekey == -1) ++ options->gss_renewal_rekey = 0; ++#ifdef GSSAPI ++ if (options->gss_kex_algorithms == NULL) ++ options->gss_kex_algorithms = strdup(GSS_KEX_DEFAULT_KEX); ++#endif + if (options->password_authentication == -1) + options->password_authentication = 1; + if (options->kbd_interactive_authentication == -1) +@@ -2616,7 +2679,14 @@ dump_client_config(Options *o, const char *host) + dump_cfg_fmtint(oGatewayPorts, o->fwd_opts.gateway_ports); + #ifdef GSSAPI + dump_cfg_fmtint(oGssAuthentication, o->gss_authentication); ++ dump_cfg_fmtint(oGssKeyEx, o->gss_keyex); + dump_cfg_fmtint(oGssDelegateCreds, o->gss_deleg_creds); ++ dump_cfg_fmtint(oGssTrustDns, o->gss_trust_dns); ++ dump_cfg_fmtint(oGssRenewalRekey, o->gss_renewal_rekey); ++ dump_cfg_string(oGssClientIdentity, o->gss_client_identity); ++ dump_cfg_string(oGssServerIdentity, o->gss_server_identity); ++ dump_cfg_string(oGssKexAlgorithms, o->gss_kex_algorithms ? ++ o->gss_kex_algorithms : GSS_KEX_DEFAULT_KEX); + #endif /* GSSAPI */ + dump_cfg_fmtint(oHashKnownHosts, o->hash_known_hosts); + dump_cfg_fmtint(oHostbasedAuthentication, o->hostbased_authentication); +diff --git a/readconf.h b/readconf.h +index 8e36bf32..0bff6d80 100644 +--- a/readconf.h ++++ b/readconf.h +@@ -40,7 +40,13 @@ typedef struct { + int challenge_response_authentication; + /* Try S/Key or TIS, authentication. */ + int gss_authentication; /* Try GSS authentication */ ++ int gss_keyex; /* Try GSS key exchange */ + int gss_deleg_creds; /* Delegate GSS credentials */ ++ int gss_trust_dns; /* Trust DNS for GSS canonicalization */ ++ int gss_renewal_rekey; /* Credential renewal forces rekey */ ++ char *gss_client_identity; /* Principal to initiate GSSAPI with */ ++ char *gss_server_identity; /* GSSAPI target principal */ ++ char *gss_kex_algorithms; /* GSSAPI kex methods to be offered by client. */ + int password_authentication; /* Try password + * authentication. */ + int kbd_interactive_authentication; /* Try keyboard-interactive auth. */ +diff --git a/servconf.c b/servconf.c +index ffac5d2c..ffdad31e 100644 +--- a/servconf.c ++++ b/servconf.c +@@ -64,6 +64,7 @@ + #include "auth.h" + #include "myproposal.h" + #include "digest.h" ++#include "ssh-gss.h" + + static void add_listen_addr(ServerOptions *, const char *, + const char *, int); +@@ -124,8 +125,11 @@ initialize_server_options(ServerOptions *options) + options->kerberos_ticket_cleanup = -1; + options->kerberos_get_afs_token = -1; + options->gss_authentication=-1; ++ options->gss_keyex = -1; + options->gss_cleanup_creds = -1; + options->gss_strict_acceptor = -1; ++ options->gss_store_rekey = -1; ++ options->gss_kex_algorithms = NULL; + options->password_authentication = -1; + options->kbd_interactive_authentication = -1; + options->challenge_response_authentication = -1; +@@ -351,10 +355,18 @@ fill_default_server_options(ServerOptions *options) + options->kerberos_get_afs_token = 0; + if (options->gss_authentication == -1) + options->gss_authentication = 0; ++ if (options->gss_keyex == -1) ++ options->gss_keyex = 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->gss_store_rekey == -1) ++ options->gss_store_rekey = 0; ++#ifdef GSSAPI ++ if (options->gss_kex_algorithms == NULL) ++ options->gss_kex_algorithms = strdup(GSS_KEX_DEFAULT_KEX); ++#endif + if (options->password_authentication == -1) + options->password_authentication = 1; + if (options->kbd_interactive_authentication == -1) +@@ -498,6 +510,7 @@ typedef enum { + sHostKeyAlgorithms, + sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile, + sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor, ++ sGssKeyEx, sGssKexAlgorithms, sGssStoreRekey, + sAcceptEnv, sSetEnv, sPermitTunnel, + sMatch, sPermitOpen, sPermitListen, sForceCommand, sChrootDirectory, + sUsePrivilegeSeparation, sAllowAgentForwarding, +@@ -572,12 +585,22 @@ static struct { + #ifdef GSSAPI + { "gssapiauthentication", sGssAuthentication, SSHCFG_ALL }, + { "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_GLOBAL }, ++ { "gssapicleanupcreds", sGssCleanupCreds, SSHCFG_GLOBAL }, + { "gssapistrictacceptorcheck", sGssStrictAcceptor, SSHCFG_GLOBAL }, ++ { "gssapikeyexchange", sGssKeyEx, SSHCFG_GLOBAL }, ++ { "gssapistorecredentialsonrekey", sGssStoreRekey, SSHCFG_GLOBAL }, ++ { "gssapikexalgorithms", sGssKexAlgorithms, SSHCFG_GLOBAL }, + #else + { "gssapiauthentication", sUnsupported, SSHCFG_ALL }, + { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL }, ++ { "gssapicleanupcreds", sUnsupported, SSHCFG_GLOBAL }, + { "gssapistrictacceptorcheck", sUnsupported, SSHCFG_GLOBAL }, ++ { "gssapikeyexchange", sUnsupported, SSHCFG_GLOBAL }, ++ { "gssapistorecredentialsonrekey", sUnsupported, SSHCFG_GLOBAL }, ++ { "gssapikexalgorithms", sUnsupported, SSHCFG_GLOBAL }, + #endif ++ { "gssusesessionccache", sUnsupported, SSHCFG_GLOBAL }, ++ { "gssapiusesessioncredcache", sUnsupported, SSHCFG_GLOBAL }, + { "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL }, + { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL }, + { "challengeresponseauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL }, +@@ -1485,6 +1508,10 @@ process_server_config_line(ServerOptions *options, char *line, + intptr = &options->gss_authentication; + goto parse_flag; + ++ case sGssKeyEx: ++ intptr = &options->gss_keyex; ++ goto parse_flag; ++ + case sGssCleanupCreds: + intptr = &options->gss_cleanup_creds; + goto parse_flag; +@@ -1493,6 +1520,22 @@ process_server_config_line(ServerOptions *options, char *line, + intptr = &options->gss_strict_acceptor; + goto parse_flag; + ++ case sGssStoreRekey: ++ intptr = &options->gss_store_rekey; ++ goto parse_flag; ++ ++ case sGssKexAlgorithms: ++ arg = strdelim(&cp); ++ if (!arg || *arg == '\0') ++ fatal("%.200s line %d: Missing argument.", ++ filename, linenum); ++ if (!kex_gss_names_valid(arg)) ++ fatal("%.200s line %d: Bad GSSAPI KexAlgorithms '%s'.", ++ filename, linenum, arg ? arg : ""); ++ if (*activep && options->gss_kex_algorithms == NULL) ++ options->gss_kex_algorithms = xstrdup(arg); ++ break; ++ + case sPasswordAuthentication: + intptr = &options->password_authentication; + goto parse_flag; +@@ -2579,6 +2622,10 @@ dump_config(ServerOptions *o) + #ifdef GSSAPI + dump_cfg_fmtint(sGssAuthentication, o->gss_authentication); + dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds); ++ dump_cfg_fmtint(sGssKeyEx, o->gss_keyex); ++ dump_cfg_fmtint(sGssStrictAcceptor, o->gss_strict_acceptor); ++ dump_cfg_fmtint(sGssStoreRekey, o->gss_store_rekey); ++ dump_cfg_string(sGssKexAlgorithms, o->gss_kex_algorithms); + #endif + dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication); + dump_cfg_fmtint(sKbdInteractiveAuthentication, +diff --git a/servconf.h b/servconf.h +index 54e0a8d8..a476d522 100644 +--- a/servconf.h ++++ b/servconf.h +@@ -126,8 +126,11 @@ typedef struct { + 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_keyex; /* If true, permit GSSAPI key exchange */ + int gss_cleanup_creds; /* If true, destroy cred cache on logout */ + int gss_strict_acceptor; /* If true, restrict the GSSAPI acceptor name */ ++ int gss_store_rekey; ++ char *gss_kex_algorithms; /* GSSAPI kex methods to be offered by client. */ + int password_authentication; /* If true, permit password + * authentication. */ + int kbd_interactive_authentication; /* If true, permit */ +diff --git a/session.c b/session.c +index 48cfaafb..78cc8358 100644 +--- a/session.c ++++ b/session.c +@@ -2674,13 +2674,19 @@ do_cleanup(struct ssh *ssh, Authctxt *authctxt) + + #ifdef KRB5 + if (options.kerberos_ticket_cleanup && +- authctxt->krb5_ctx) ++ authctxt->krb5_ctx) { ++ temporarily_use_uid(authctxt->pw); + krb5_cleanup_proc(authctxt); ++ restore_uid(); ++ } + #endif + + #ifdef GSSAPI +- if (options.gss_cleanup_creds) ++ if (options.gss_cleanup_creds) { ++ temporarily_use_uid(authctxt->pw); + ssh_gssapi_cleanup_creds(); ++ restore_uid(); ++ } + #endif + + /* remove agent socket */ +diff --git a/ssh-gss.h b/ssh-gss.h +index 36180d07..70dd3665 100644 +--- a/ssh-gss.h ++++ b/ssh-gss.h +@@ -1,6 +1,6 @@ + /* $OpenBSD: ssh-gss.h,v 1.14 2018/07/10 09:13:30 djm Exp $ */ + /* +- * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. ++ * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions +@@ -61,10 +61,34 @@ + + #define SSH_GSS_OIDTYPE 0x06 + ++#define SSH2_MSG_KEXGSS_INIT 30 ++#define SSH2_MSG_KEXGSS_CONTINUE 31 ++#define SSH2_MSG_KEXGSS_COMPLETE 32 ++#define SSH2_MSG_KEXGSS_HOSTKEY 33 ++#define SSH2_MSG_KEXGSS_ERROR 34 ++#define SSH2_MSG_KEXGSS_GROUPREQ 40 ++#define SSH2_MSG_KEXGSS_GROUP 41 ++#define KEX_GSS_GRP1_SHA1_ID "gss-group1-sha1-" ++#define KEX_GSS_GRP14_SHA1_ID "gss-group14-sha1-" ++#define KEX_GSS_GRP14_SHA256_ID "gss-group14-sha256-" ++#define KEX_GSS_GRP16_SHA512_ID "gss-group16-sha512-" ++#define KEX_GSS_GEX_SHA1_ID "gss-gex-sha1-" ++#define KEX_GSS_NISTP256_SHA256_ID "gss-nistp256-sha256-" ++#define KEX_GSS_C25519_SHA256_ID "gss-curve25519-sha256-" ++ ++#define GSS_KEX_DEFAULT_KEX \ ++ KEX_GSS_GRP14_SHA256_ID "," \ ++ KEX_GSS_GRP16_SHA512_ID "," \ ++ KEX_GSS_NISTP256_SHA256_ID "," \ ++ KEX_GSS_C25519_SHA256_ID "," \ ++ KEX_GSS_GRP14_SHA1_ID "," \ ++ KEX_GSS_GEX_SHA1_ID ++ + typedef struct { + char *filename; + char *envvar; + char *envval; ++ struct passwd *owner; + void *data; + } ssh_gssapi_ccache; + +@@ -72,8 +92,11 @@ typedef struct { + gss_buffer_desc displayname; + gss_buffer_desc exportedname; + gss_cred_id_t creds; ++ gss_name_t name; + struct ssh_gssapi_mech_struct *mech; + ssh_gssapi_ccache store; ++ int used; ++ int updated; + } ssh_gssapi_client; + + typedef struct ssh_gssapi_mech_struct { +@@ -84,6 +107,7 @@ typedef struct ssh_gssapi_mech_struct { + int (*userok) (ssh_gssapi_client *, char *); + int (*localname) (ssh_gssapi_client *, char **); + void (*storecreds) (ssh_gssapi_client *); ++ int (*updatecreds) (ssh_gssapi_ccache *, ssh_gssapi_client *); + } ssh_gssapi_mech; + + typedef struct { +@@ -94,10 +118,11 @@ typedef struct { + gss_OID oid; /* client */ + gss_cred_id_t creds; /* server */ + gss_name_t client; /* server */ +- gss_cred_id_t client_creds; /* server */ ++ gss_cred_id_t client_creds; /* both */ + } Gssctxt; + + extern ssh_gssapi_mech *supported_mechs[]; ++extern Gssctxt *gss_kex_context; + + int ssh_gssapi_check_oid(Gssctxt *, void *, size_t); + void ssh_gssapi_set_oid_data(Gssctxt *, void *, size_t); +@@ -109,6 +134,7 @@ OM_uint32 ssh_gssapi_test_oid_supported(OM_uint32 *, gss_OID, int *); + + struct sshbuf; + int ssh_gssapi_get_buffer_desc(struct sshbuf *, gss_buffer_desc *); ++int ssh_gssapi_sshpkt_get_buffer_desc(struct ssh *, gss_buffer_desc *); + + OM_uint32 ssh_gssapi_import_name(Gssctxt *, const char *); + OM_uint32 ssh_gssapi_init_ctx(Gssctxt *, int, +@@ -123,17 +149,33 @@ void ssh_gssapi_delete_ctx(Gssctxt **); + OM_uint32 ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t); + void ssh_gssapi_buildmic(struct sshbuf *, const char *, + const char *, const char *); +-int ssh_gssapi_check_mechanism(Gssctxt **, gss_OID, const char *); ++int ssh_gssapi_check_mechanism(Gssctxt **, gss_OID, const char *, const char *); ++OM_uint32 ssh_gssapi_client_identity(Gssctxt *, const char *); ++int ssh_gssapi_credentials_updated(Gssctxt *); + + /* In the server */ ++typedef int ssh_gssapi_check_fn(Gssctxt **, gss_OID, const char *, ++ const char *); ++char *ssh_gssapi_client_mechanisms(const char *, const char *, const char *); ++char *ssh_gssapi_kex_mechs(gss_OID_set, ssh_gssapi_check_fn *, const char *, ++ const char *, const char *); ++gss_OID ssh_gssapi_id_kex(Gssctxt *, char *, int); ++int ssh_gssapi_server_check_mech(Gssctxt **,gss_OID, const char *, ++ const char *); + OM_uint32 ssh_gssapi_server_ctx(Gssctxt **, gss_OID); +-int ssh_gssapi_userok(char *name); ++int ssh_gssapi_userok(char *name, struct passwd *, int kex); + OM_uint32 ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t); + void ssh_gssapi_do_child(char ***, u_int *); + void ssh_gssapi_cleanup_creds(void); + void ssh_gssapi_storecreds(void); + const char *ssh_gssapi_displayname(void); + ++char *ssh_gssapi_server_mechanisms(void); ++int ssh_gssapi_oid_table_ok(void); ++ ++int ssh_gssapi_update_creds(ssh_gssapi_ccache *store); ++void ssh_gssapi_rekey_creds(void); ++ + #endif /* GSSAPI */ + + #endif /* _SSH_GSS_H */ +diff --git a/ssh.1 b/ssh.1 +index 9480eba8..a1c7d230 100644 +--- a/ssh.1 ++++ b/ssh.1 +@@ -497,7 +497,13 @@ For full details of the options listed below, and their possible values, see + .It GatewayPorts + .It GlobalKnownHostsFile + .It GSSAPIAuthentication ++.It GSSAPIKeyExchange ++.It GSSAPIClientIdentity + .It GSSAPIDelegateCredentials ++.It GSSAPIKexAlgorithms ++.It GSSAPIRenewalForcesRekey ++.It GSSAPIServerIdentity ++.It GSSAPITrustDns + .It HashKnownHosts + .It Host + .It HostbasedAuthentication +@@ -573,6 +579,8 @@ flag), + (supported message integrity codes), + .Ar kex + (key exchange algorithms), ++.Ar kex-gss ++(GSSAPI key exchange algorithms), + .Ar key + (key types), + .Ar key-cert +diff --git a/ssh.c b/ssh.c +index 91e7c351..42be7d88 100644 +--- a/ssh.c ++++ b/ssh.c +@@ -736,6 +736,8 @@ main(int ac, char **av) + cp = mac_alg_list('\n'); + else if (strcmp(optarg, "kex") == 0) + cp = kex_alg_list('\n'); ++ else if (strcmp(optarg, "kex-gss") == 0) ++ cp = kex_gss_alg_list('\n'); + else if (strcmp(optarg, "key") == 0) + cp = sshkey_alg_list(0, 0, 0, '\n'); + else if (strcmp(optarg, "key-cert") == 0) +@@ -748,7 +750,7 @@ main(int ac, char **av) + cp = xstrdup("2"); + else if (strcmp(optarg, "help") == 0) { + cp = xstrdup( +- "cipher\ncipher-auth\nkex\nkey\n" ++ "cipher\ncipher-auth\nkex\nkex-gss\nkey\n" + "key-cert\nkey-plain\nmac\n" + "protocol-version\nsig"); + } +diff --git a/ssh_config b/ssh_config +index 5e8ef548..1ff999b6 100644 +--- a/ssh_config ++++ b/ssh_config +@@ -24,6 +24,8 @@ + # HostbasedAuthentication no + # GSSAPIAuthentication no + # GSSAPIDelegateCredentials no ++# GSSAPIKeyExchange no ++# GSSAPITrustDNS no + # BatchMode no + # CheckHostIP yes + # AddressFamily any +diff --git a/ssh_config.5 b/ssh_config.5 +index 41262963..c3c8b274 100644 +--- a/ssh_config.5 ++++ b/ssh_config.5 +@@ -754,10 +754,67 @@ The default is + Specifies whether user authentication based on GSSAPI is allowed. + The default is + .Cm no . ++.It Cm GSSAPIClientIdentity ++If set, specifies the GSSAPI client identity that ssh should use when ++connecting to the server. The default is unset, which means that the default ++identity will be used. + .It Cm GSSAPIDelegateCredentials + Forward (delegate) credentials to the server. + The default is + .Cm no . ++.It Cm GSSAPIKeyExchange ++Specifies whether key exchange based on GSSAPI may be used. When using ++GSSAPI key exchange the server need not have a host key. ++The default is ++.Dq no . ++.It Cm GSSAPIRenewalForcesRekey ++If set to ++.Dq yes ++then renewal of the client's GSSAPI credentials will force the rekeying of the ++ssh connection. With a compatible server, this will delegate the renewed ++credentials to a session on the server. ++.Pp ++Checks are made to ensure that credentials are only propagated when the new ++credentials match the old ones on the originating client and where the ++receiving server still has the old set in its cache. ++.Pp ++The default is ++.Dq no . ++.Pp ++For this to work ++.Cm GSSAPIKeyExchange ++needs to be enabled in the server and also used by the client. ++.It Cm GSSAPIServerIdentity ++If set, specifies the GSSAPI server identity that ssh should expect when ++connecting to the server. The default is unset, which means that the ++expected GSSAPI server identity will be determined from the target ++hostname. ++.It Cm GSSAPITrustDns ++Set to ++.Dq yes ++to indicate that the DNS is trusted to securely canonicalize ++the name of the host being connected to. If ++.Dq no , ++the hostname entered on the ++command line will be passed untouched to the GSSAPI library. ++The default is ++.Dq no . ++.It Cm GSSAPIKexAlgorithms ++The list of key exchange algorithms that are offered for GSSAPI ++key exchange. Possible values are ++.Bd -literal -offset 3n ++gss-gex-sha1- ++gss-group1-sha1- ++gss-group14-sha1- ++gss-group14-sha256- ++gss-group16-sha512- ++gss-nistp256-sha256- ++gss-curve25519-sha256- ++.Ed ++.Pp ++The default is ++.Dq gss-group14-sha256-,gss-group16-sha512-,gss-nistp256-sha256-,gss-curve25519-sha256-,gss-group14-sha1-,gss-gex-sha1- . ++This option only applies to connections using GSSAPI. + .It Cm HashKnownHosts + Indicates that + .Xr ssh 1 +diff --git a/sshconnect2.c b/sshconnect2.c +index dffee90b..0d0a6cb8 100644 +--- a/sshconnect2.c ++++ b/sshconnect2.c +@@ -78,8 +78,6 @@ + #endif + + /* import */ +-extern char *client_version_string; +-extern char *server_version_string; + extern Options options; + + /* +@@ -161,6 +159,11 @@ ssh_kex2(struct ssh *ssh, char *host, struct sockaddr *hostaddr, u_short port) + char *s, *all_key; + int r; + ++#if defined(GSSAPI) && defined(WITH_OPENSSL) ++ char *orig = NULL, *gss = NULL; ++ char *gss_host = NULL; ++#endif ++ + xxx_host = host; + xxx_hostaddr = hostaddr; + +@@ -193,6 +196,41 @@ ssh_kex2(struct ssh *ssh, char *host, struct sockaddr *hostaddr, u_short port) + order_hostkeyalgs(host, hostaddr, port)); + } + ++#if defined(GSSAPI) && defined(WITH_OPENSSL) ++ if (options.gss_keyex) { ++ /* Add the GSSAPI mechanisms currently supported on this ++ * client to the key exchange algorithm proposal */ ++ orig = myproposal[PROPOSAL_KEX_ALGS]; ++ ++ if (options.gss_server_identity) { ++ gss_host = xstrdup(options.gss_server_identity); ++ } else if (options.gss_trust_dns) { ++ gss_host = remote_hostname(ssh); ++ /* Fall back to specified host if we are using proxy command ++ * and can not use DNS on that socket */ ++ if (strcmp(gss_host, "UNKNOWN") == 0) { ++ gss_host = xstrdup(host); ++ } ++ } else { ++ gss_host = xstrdup(host); ++ } ++ ++ gss = ssh_gssapi_client_mechanisms(gss_host, ++ options.gss_client_identity, options.gss_kex_algorithms); ++ if (gss) { ++ debug("Offering GSSAPI proposal: %s", gss); ++ xasprintf(&myproposal[PROPOSAL_KEX_ALGS], ++ "%s,%s", gss, orig); ++ ++ /* If we've got GSSAPI algorithms, then we also support the ++ * 'null' hostkey, as a last resort */ ++ orig = myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]; ++ xasprintf(&myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS], ++ "%s,null", orig); ++ } ++ } ++#endif ++ + if (options.rekey_limit || options.rekey_interval) + ssh_packet_set_rekey_limits(ssh, options.rekey_limit, + options.rekey_interval); +@@ -211,16 +243,46 @@ ssh_kex2(struct ssh *ssh, char *host, struct sockaddr *hostaddr, u_short port) + # ifdef OPENSSL_HAS_ECC + ssh->kex->kex[KEX_ECDH_SHA2] = kex_gen_client; + # endif +-#endif ++# ifdef GSSAPI ++ if (options.gss_keyex) { ++ ssh->kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_client; ++ ssh->kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_client; ++ ssh->kex->kex[KEX_GSS_GRP14_SHA256] = kexgss_client; ++ ssh->kex->kex[KEX_GSS_GRP16_SHA512] = kexgss_client; ++ ssh->kex->kex[KEX_GSS_GEX_SHA1] = kexgssgex_client; ++ ssh->kex->kex[KEX_GSS_NISTP256_SHA256] = kexgss_client; ++ ssh->kex->kex[KEX_GSS_C25519_SHA256] = kexgss_client; ++ } ++# endif ++#endif /* WITH_OPENSSL */ + ssh->kex->kex[KEX_C25519_SHA256] = kex_gen_client; + ssh->kex->kex[KEX_KEM_SNTRUP4591761X25519_SHA512] = kex_gen_client; + ssh->kex->verify_host_key=&verify_host_key_callback; + ++#if defined(GSSAPI) && defined(WITH_OPENSSL) ++ if (options.gss_keyex) { ++ ssh->kex->gss_deleg_creds = options.gss_deleg_creds; ++ ssh->kex->gss_trust_dns = options.gss_trust_dns; ++ ssh->kex->gss_client = options.gss_client_identity; ++ ssh->kex->gss_host = gss_host; ++ } ++#endif ++ + ssh_dispatch_run_fatal(ssh, DISPATCH_BLOCK, &ssh->kex->done); + + /* remove ext-info from the KEX proposals for rekeying */ + myproposal[PROPOSAL_KEX_ALGS] = + compat_kex_proposal(options.kex_algorithms); ++#if defined(GSSAPI) && defined(WITH_OPENSSL) ++ /* repair myproposal after it was crumpled by the */ ++ /* ext-info removal above */ ++ if (gss) { ++ orig = myproposal[PROPOSAL_KEX_ALGS]; ++ xasprintf(&myproposal[PROPOSAL_KEX_ALGS], ++ "%s,%s", gss, orig); ++ free(gss); ++ } ++#endif + if ((r = kex_prop2buf(ssh->kex->my, myproposal)) != 0) + fatal("kex_prop2buf: %s", ssh_err(r)); + +@@ -317,6 +379,7 @@ static int input_gssapi_response(int type, u_int32_t, struct ssh *); + static int input_gssapi_token(int type, u_int32_t, struct ssh *); + static int input_gssapi_error(int, u_int32_t, struct ssh *); + static int input_gssapi_errtok(int, u_int32_t, struct ssh *); ++static int userauth_gsskeyex(struct ssh *); + #endif + + void userauth(struct ssh *, char *); +@@ -333,6 +396,11 @@ static char *authmethods_get(void); + + Authmethod authmethods[] = { + #ifdef GSSAPI ++ {"gssapi-keyex", ++ userauth_gsskeyex, ++ NULL, ++ &options.gss_keyex, ++ NULL}, + {"gssapi-with-mic", + userauth_gssapi, + userauth_gssapi_cleanup, +@@ -698,12 +766,29 @@ userauth_gssapi(struct ssh *ssh) + OM_uint32 min; + int r, ok = 0; + gss_OID mech = NULL; ++ char *gss_host = NULL; ++ ++ if (options.gss_server_identity) { ++ gss_host = xstrdup(options.gss_server_identity); ++ } else if (options.gss_trust_dns) { ++ gss_host = remote_hostname(ssh); ++ /* Fall back to specified host if we are using proxy command ++ * and can not use DNS on that socket */ ++ if (strcmp(gss_host, "UNKNOWN") == 0) { ++ gss_host = xstrdup(authctxt->host); ++ } ++ } else { ++ gss_host = xstrdup(authctxt->host); ++ } + + /* Try one GSSAPI method at a time, rather than sending them all at + * once. */ + + if (authctxt->gss_supported_mechs == NULL) +- gss_indicate_mechs(&min, &authctxt->gss_supported_mechs); ++ if (GSS_ERROR(gss_indicate_mechs(&min, &authctxt->gss_supported_mechs))) { ++ free(gss_host); ++ return 0; ++ } + + /* Check to see whether the mechanism is usable before we offer it */ + while (authctxt->mech_tried < authctxt->gss_supported_mechs->count && +@@ -712,13 +791,15 @@ userauth_gssapi(struct ssh *ssh) + elements[authctxt->mech_tried]; + /* My DER encoding requires length<128 */ + if (mech->length < 128 && ssh_gssapi_check_mechanism(&gssctxt, +- mech, authctxt->host)) { ++ mech, gss_host, options.gss_client_identity)) { + ok = 1; /* Mechanism works */ + } else { + authctxt->mech_tried++; + } + } + ++ free(gss_host); ++ + if (!ok || mech == NULL) + return 0; + +@@ -958,6 +1039,55 @@ input_gssapi_error(int type, u_int32_t plen, struct ssh *ssh) + free(lang); + return r; + } ++ ++int ++userauth_gsskeyex(struct ssh *ssh) ++{ ++ struct sshbuf *b = NULL; ++ Authctxt *authctxt = ssh->authctxt; ++ gss_buffer_desc gssbuf; ++ gss_buffer_desc mic = GSS_C_EMPTY_BUFFER; ++ OM_uint32 ms; ++ int r; ++ ++ static int attempt = 0; ++ if (attempt++ >= 1) ++ return (0); ++ ++ if (gss_kex_context == NULL) { ++ debug("No valid Key exchange context"); ++ return (0); ++ } ++ ++ if ((b = sshbuf_new()) == NULL) ++ fatal("%s: sshbuf_new failed", __func__); ++ ++ ssh_gssapi_buildmic(b, authctxt->server_user, authctxt->service, ++ "gssapi-keyex"); ++ ++ if ((gssbuf.value = sshbuf_mutable_ptr(b)) == NULL) ++ fatal("%s: sshbuf_mutable_ptr failed", __func__); ++ gssbuf.length = sshbuf_len(b); ++ ++ if (GSS_ERROR(ssh_gssapi_sign(gss_kex_context, &gssbuf, &mic))) { ++ sshbuf_free(b); ++ return (0); ++ } ++ ++ if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_REQUEST)) != 0 || ++ (r = sshpkt_put_cstring(ssh, authctxt->server_user)) != 0 || ++ (r = sshpkt_put_cstring(ssh, authctxt->service)) != 0 || ++ (r = sshpkt_put_cstring(ssh, authctxt->method->name)) != 0 || ++ (r = sshpkt_put_string(ssh, mic.value, mic.length)) != 0 || ++ (r = sshpkt_send(ssh)) != 0) ++ fatal("%s: %s", __func__, ssh_err(r)); ++ ++ sshbuf_free(b); ++ gss_release_buffer(&ms, &mic); ++ ++ return (1); ++} ++ + #endif /* GSSAPI */ + + static int +diff --git a/sshd.c b/sshd.c +index cbd3bce9..8c223f6a 100644 +--- a/sshd.c ++++ b/sshd.c +@@ -796,8 +796,8 @@ notify_hostkeys(struct ssh *ssh) + } + debug3("%s: sent %u hostkeys", __func__, nkeys); + if (nkeys == 0) +- fatal("%s: no hostkeys", __func__); +- if ((r = sshpkt_send(ssh)) != 0) ++ debug3("%s: no hostkeys", __func__); ++ else if ((r = sshpkt_send(ssh)) != 0) + sshpkt_fatal(ssh, r, "%s: send", __func__); + sshbuf_free(buf); + } +@@ -1769,7 +1769,8 @@ main(int ac, char **av) + free(fp); + } + accumulate_host_timing_secret(cfg, NULL); +- if (!sensitive_data.have_ssh2_key) { ++ /* The GSSAPI key exchange can run without a host key */ ++ if (!sensitive_data.have_ssh2_key && !options.gss_keyex) { + logit("sshd: no hostkeys available -- exiting."); + exit(1); + } +@@ -2260,6 +2261,48 @@ do_ssh2_kex(struct ssh *ssh) + myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = compat_pkalg_proposal( + list_hostkey_types()); + ++#if defined(GSSAPI) && defined(WITH_OPENSSL) ++ { ++ char *orig; ++ char *gss = NULL; ++ char *newstr = NULL; ++ orig = myproposal[PROPOSAL_KEX_ALGS]; ++ ++ /* ++ * If we don't have a host key, then there's no point advertising ++ * the other key exchange algorithms ++ */ ++ ++ if (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]) == 0) ++ orig = NULL; ++ ++ if (options.gss_keyex) ++ gss = ssh_gssapi_server_mechanisms(); ++ else ++ gss = NULL; ++ ++ if (gss && orig) ++ xasprintf(&newstr, "%s,%s", gss, orig); ++ else if (gss) ++ newstr = gss; ++ else if (orig) ++ newstr = orig; ++ ++ /* ++ * If we've got GSSAPI mechanisms, then we've got the 'null' host ++ * key alg, but we can't tell people about it unless its the only ++ * host key algorithm we support ++ */ ++ if (gss && (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS])) == 0) ++ myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = "null"; ++ ++ if (newstr) ++ myproposal[PROPOSAL_KEX_ALGS] = newstr; ++ else ++ fatal("No supported key exchange algorithms"); ++ } ++#endif ++ + /* start key exchange */ + if ((r = kex_setup(ssh, myproposal)) != 0) + fatal("kex_setup: %s", ssh_err(r)); +@@ -2275,7 +2318,18 @@ do_ssh2_kex(struct ssh *ssh) + # ifdef OPENSSL_HAS_ECC + kex->kex[KEX_ECDH_SHA2] = kex_gen_server; + # endif +-#endif ++# ifdef GSSAPI ++ if (options.gss_keyex) { ++ kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server; ++ kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server; ++ kex->kex[KEX_GSS_GRP14_SHA256] = kexgss_server; ++ kex->kex[KEX_GSS_GRP16_SHA512] = kexgss_server; ++ kex->kex[KEX_GSS_GEX_SHA1] = kexgssgex_server; ++ kex->kex[KEX_GSS_NISTP256_SHA256] = kexgss_server; ++ kex->kex[KEX_GSS_C25519_SHA256] = kexgss_server; ++ } ++# endif ++#endif /* WITH_OPENSSL */ + kex->kex[KEX_C25519_SHA256] = kex_gen_server; + kex->kex[KEX_KEM_SNTRUP4591761X25519_SHA512] = kex_gen_server; + kex->load_host_public_key=&get_hostkey_public_by_type; +diff --git a/sshd_config b/sshd_config +index 19b7c91a..2c48105f 100644 +--- a/sshd_config ++++ b/sshd_config +@@ -69,6 +69,8 @@ AuthorizedKeysFile .ssh/authorized_keys + # GSSAPI options + GSSAPIAuthentication yes + GSSAPICleanupCredentials no ++#GSSAPIStrictAcceptorCheck yes ++#GSSAPIKeyExchange no + + # Set this to 'yes' to enable PAM authentication, account processing, + # and session processing. If this is enabled, PAM authentication will +diff --git a/sshd_config.5 b/sshd_config.5 +index b224f292..2baa6622 100644 +--- a/sshd_config.5 ++++ b/sshd_config.5 +@@ -653,6 +653,11 @@ Specifies whether to automatically destroy the user's credentials cache + on logout. + The default is + .Cm yes . ++.It Cm GSSAPIKeyExchange ++Specifies whether key exchange based on GSSAPI is allowed. GSSAPI key exchange ++doesn't rely on ssh keys to verify host identity. ++The default is ++.Cm no . + .It Cm GSSAPIStrictAcceptorCheck + Determines whether to be strict about the identity of the GSSAPI acceptor + a client authenticates against. +@@ -667,6 +672,31 @@ machine's default store. + This facility is provided to assist with operation on multi homed machines. + The default is + .Cm yes . ++.It Cm GSSAPIStoreCredentialsOnRekey ++Controls whether the user's GSSAPI credentials should be updated following a ++successful connection rekeying. This option can be used to accepted renewed ++or updated credentials from a compatible client. The default is ++.Dq no . ++.Pp ++For this to work ++.Cm GSSAPIKeyExchange ++needs to be enabled in the server and also used by the client. ++.It Cm GSSAPIKexAlgorithms ++The list of key exchange algorithms that are accepted by GSSAPI ++key exchange. Possible values are ++.Bd -literal -offset 3n ++gss-gex-sha1- ++gss-group1-sha1- ++gss-group14-sha1- ++gss-group14-sha256- ++gss-group16-sha512- ++gss-nistp256-sha256- ++gss-curve25519-sha256- ++.Ed ++.Pp ++The default is ++.Dq gss-group14-sha256-,gss-group16-sha512-,gss-nistp256-sha256-,gss-curve25519-sha256-,gss-group14-sha1-,gss-gex-sha1- . ++This option only applies to connections using GSSAPI. + .It Cm HostbasedAcceptedKeyTypes + Specifies the key types that will be accepted for hostbased authentication + as a list of comma-separated patterns. +diff --git a/sshkey.c b/sshkey.c +index ad195776..789cd61e 100644 +--- a/sshkey.c ++++ b/sshkey.c +@@ -135,6 +135,7 @@ static const struct keytype keytypes[] = { + # endif /* OPENSSL_HAS_NISTP521 */ + # endif /* OPENSSL_HAS_ECC */ + #endif /* WITH_OPENSSL */ ++ { "null", "null", NULL, KEY_NULL, 0, 0, 0 }, + { NULL, NULL, NULL, -1, -1, 0, 0 } + }; + +@@ -223,7 +224,7 @@ sshkey_alg_list(int certs_only, int plain_only, int include_sigonly, char sep) + const struct keytype *kt; + + for (kt = keytypes; kt->type != -1; kt++) { +- if (kt->name == NULL) ++ if (kt->name == NULL || kt->type == KEY_NULL) + continue; + if (!include_sigonly && kt->sigonly) + continue; +diff --git a/sshkey.h b/sshkey.h +index a91e6043..c11106c9 100644 +--- a/sshkey.h ++++ b/sshkey.h +@@ -65,6 +65,7 @@ enum sshkey_types { + KEY_ED25519_CERT, + KEY_XMSS, + KEY_XMSS_CERT, ++ KEY_NULL, + KEY_UNSPEC + }; + diff --git a/openssh-8.0p1-hpn-14.19.patch b/openssh-8.0p1-hpn-14.19.patch new file mode 100644 index 0000000..4d69ef1 --- /dev/null +++ b/openssh-8.0p1-hpn-14.19.patch @@ -0,0 +1,2335 @@ +diff -Nur openssh-8.0p1.orig/auth2.c openssh-8.0p1/auth2.c +--- openssh-8.0p1.orig/auth2.c 2024-07-09 15:12:37.162394365 +0200 ++++ openssh-8.0p1/auth2.c 2024-07-09 19:30:12.124093259 +0200 +@@ -53,6 +53,7 @@ + #include "pathnames.h" + #include "sshbuf.h" + #include "ssherr.h" ++#include "canohost.h" + + #ifdef GSSAPI + #include "ssh-gss.h" +@@ -79,6 +80,8 @@ + extern Authmethod method_gssapi; + #endif + ++static int log_flag = 0; ++ + Authmethod *authmethods[] = { + &method_none, + &method_pubkey, +@@ -310,6 +313,11 @@ + + debug("userauth-request for user %s service %s method %s", + user[0] ? user : "", service, method); ++ if (!log_flag) { ++ logit("SSH: Server;Ltype: Authname;Remote: %s-%d;Name: %s", ++ ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), user); ++ log_flag = 1; ++ } + debug("attempt %d failures %d", authctxt->attempt, authctxt->failures); + + #ifdef WITH_SELINUX +diff -Nur openssh-8.0p1.orig/channels.c openssh-8.0p1/channels.c +--- openssh-8.0p1.orig/channels.c 2024-07-09 15:12:37.143394308 +0200 ++++ openssh-8.0p1/channels.c 2024-07-09 20:28:39.204371406 +0200 +@@ -220,6 +220,9 @@ + /* Setup helper */ + static void channel_handler_init(struct ssh_channels *sc); + ++static int hpn_disabled = 0; ++static int hpn_buffer_size = 2 * 1024 * 1024; ++ + /* -- channel core */ + + void +@@ -415,6 +418,7 @@ + c->local_window = window; + c->local_window_max = window; + c->local_maxpacket = maxpack; ++ c->dynamic_window = 0; + c->remote_name = xstrdup(remote_name); + c->ctl_chan = -1; + c->delayed = 1; /* prevent call to channel_post handler */ +@@ -1088,6 +1092,28 @@ + FD_SET(c->sock, writeset); + } + ++static int ++channel_tcpwinsz(struct ssh *ssh) ++{ ++ u_int32_t tcpwinsz = 0; ++ socklen_t optsz = sizeof(tcpwinsz); ++ int ret = -1; ++ ++ /* if we aren't on a socket return 128KB */ ++ if (!ssh_packet_connection_is_on_socket(ssh)) ++ return 128 * 1024; ++ ++ ret = getsockopt(ssh_packet_get_connection_in(ssh), ++ SOL_SOCKET, SO_RCVBUF, &tcpwinsz, &optsz); ++ /* return no more than SSHBUF_SIZE_MAX (currently 256MB) */ ++ if ((ret == 0) && tcpwinsz > SSHBUF_SIZE_MAX) ++ tcpwinsz = SSHBUF_SIZE_MAX; ++ ++ debug2("tcpwinsz: tcp connection %d, Receive window: %d", ++ ssh_packet_get_connection_in(ssh), tcpwinsz); ++ return tcpwinsz; ++} ++ + static void + channel_pre_open(struct ssh *ssh, Channel *c, + fd_set *readset, fd_set *writeset) +@@ -2188,21 +2214,30 @@ + c->local_maxpacket*3) || + c->local_window < c->local_window_max/2) && + c->local_consumed > 0) { ++ u_int addition = 0; ++ u_int32_t tcpwinsz = channel_tcpwinsz(ssh); ++ /* adjust max window size if we are in a dynamic environment */ ++ if (c->dynamic_window && (tcpwinsz > c->local_window_max)) { ++ /* grow the window somewhat aggressively to maintain pressure */ ++ addition = 1.5 * (tcpwinsz - c->local_window_max); ++ c->local_window_max += addition; ++ debug("Channel: Window growth to %d by %d bytes", c->local_window_max, addition); ++ } + if (!c->have_remote_id) + fatal(":%s: channel %d: no remote id", + __func__, 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_put_u32(ssh, c->local_consumed + addition)) != 0 || + (r = sshpkt_send(ssh)) != 0) { + fatal("%s: channel %i: %s", __func__, + c->self, ssh_err(r)); + } + 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 + addition); ++ c->local_window += c->local_consumed + addition; + c->local_consumed = 0; + } + return 1; +@@ -2599,7 +2634,6 @@ + c->self, ssh_err(r)); + } + c->remote_window -= plen; +- return; + } + + /* Enqueue packet for buffered data. */ +@@ -3384,6 +3418,14 @@ + return addr; + } + ++void ++channel_set_hpn(int external_hpn_disabled, int external_hpn_buffer_size) ++{ ++ hpn_disabled = external_hpn_disabled; ++ hpn_buffer_size = external_hpn_buffer_size; ++ debug("HPN Disabled: %d, HPN Buffer Size: %d", hpn_disabled, hpn_buffer_size); ++} ++ + static int + channel_setup_fwd_listener_tcpip(struct ssh *ssh, int type, + struct Forward *fwd, int *allocated_listen_port, +@@ -3524,8 +3566,10 @@ + } + + /* Allocate a channel number for the socket. */ ++ /* explicitly test for hpn disabled option. if true use smaller window size */ + c = channel_new(ssh, "port listener", type, sock, sock, -1, +- CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, ++ hpn_disabled ? CHAN_TCP_WINDOW_DEFAULT : hpn_buffer_size, ++ CHAN_TCP_PACKET_DEFAULT, + 0, "port listener", 1); + c->path = xstrdup(host); + c->host_port = fwd->connect_port; +@@ -4678,7 +4722,8 @@ + sock = socks[n]; + nc = channel_new(ssh, "x11 listener", + SSH_CHANNEL_X11_LISTENER, sock, sock, -1, +- CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, ++ hpn_disabled ? CHAN_X11_WINDOW_DEFAULT : hpn_buffer_size, ++ CHAN_X11_PACKET_DEFAULT, + 0, "X11 inet listener", 1); + nc->single_connection = single_connection; + (*chanids)[n] = nc->self; +diff -Nur openssh-8.0p1.orig/channels.h openssh-8.0p1/channels.h +--- openssh-8.0p1.orig/channels.h 2024-07-09 15:12:37.082394127 +0200 ++++ openssh-8.0p1/channels.h 2024-07-09 15:13:50.321612062 +0200 +@@ -161,6 +161,7 @@ + u_int local_window_max; + u_int local_consumed; + u_int local_maxpacket; ++ int dynamic_window; + int extended_usage; + int single_connection; + +@@ -355,4 +356,7 @@ + void chan_write_failed(struct ssh *, Channel *); + void chan_obuf_empty(struct ssh *, Channel *); + ++/* hpn handler */ ++void channel_set_hpn(int, int); ++ + #endif +diff -Nur openssh-8.0p1.orig/cipher.c openssh-8.0p1/cipher.c +--- openssh-8.0p1.orig/cipher.c 2024-07-09 15:12:37.122394246 +0200 ++++ openssh-8.0p1/cipher.c 2024-07-09 15:13:50.322612065 +0200 +@@ -51,6 +51,8 @@ + + #include "openbsd-compat/openssl-compat.h" + ++/* for multi-threaded aes-ctr cipher */ ++extern const EVP_CIPHER *evp_aes_ctr_mt(void); + + struct sshcipher_ctx { + int plaintext; +@@ -61,7 +63,7 @@ + const struct sshcipher *cipher; + }; + +-static const struct sshcipher ciphers[] = { ++static struct sshcipher ciphers[] = { + #ifdef WITH_OPENSSL + #ifndef OPENSSL_NO_DES + { "3des-cbc", 8, 24, 0, 0, CFLAG_CBC, EVP_des_ede3_cbc }, +@@ -121,6 +123,29 @@ + return ret; + } + ++/* used to get the cipher name so when force rekeying to handle the ++ * single to multithreaded ctr cipher swap we only rekey when appropriate ++ */ ++const char * ++cipher_ctx_name(const struct sshcipher_ctx *cc) ++{ ++ return cc->cipher->name; ++} ++ ++/* in order to get around sandbox and forking issues with a threaded cipher ++ * we set the initial pre-auth aes-ctr cipher to the default OpenSSH cipher ++ * post auth we set them to the new evp as defined by cipher-ctr-mt ++ */ ++#ifdef WITH_OPENSSL ++void ++cipher_reset_multithreaded(void) ++{ ++ cipher_by_name("aes128-ctr")->evptype = evp_aes_ctr_mt; ++ cipher_by_name("aes192-ctr")->evptype = evp_aes_ctr_mt; ++ cipher_by_name("aes256-ctr")->evptype = evp_aes_ctr_mt; ++} ++#endif ++ + u_int + cipher_blocksize(const struct sshcipher *c) + { +@@ -170,10 +195,10 @@ + return cc->plaintext; + } + +-const struct sshcipher * ++struct sshcipher * + cipher_by_name(const char *name) + { +- const struct sshcipher *c; ++ struct sshcipher *c; + for (c = ciphers; c->name != NULL; c++) + if (strcmp(c->name, name) == 0) + return c; +@@ -195,7 +220,8 @@ + for ((p = strsep(&cp, CIPHER_SEP)); p && *p != '\0'; + (p = strsep(&cp, CIPHER_SEP))) { + c = cipher_by_name(p); +- if (c == NULL || (c->flags & CFLAG_INTERNAL) != 0) { ++ if (c == NULL || ((c->flags & CFLAG_INTERNAL) != 0 && ++ (c->flags & CFLAG_NONE) != 0)) { + free(cipher_list); + return 0; + } +diff -Nur openssh-8.0p1.orig/cipher-ctr-mt.c openssh-8.0p1/cipher-ctr-mt.c +--- openssh-8.0p1.orig/cipher-ctr-mt.c 1970-01-01 01:00:00.000000000 +0100 ++++ openssh-8.0p1/cipher-ctr-mt.c 2024-07-09 23:57:03.995961792 +0200 +@@ -0,0 +1,679 @@ ++/* ++ * OpenSSH Multi-threaded AES-CTR Cipher ++ * ++ * Author: Benjamin Bennett ++ * Author: Mike Tasota ++ * Author: Chris Rapier ++ * Copyright (c) 2008-2013 Pittsburgh Supercomputing Center. All rights reserved. ++ * ++ * Based on original OpenSSH AES-CTR cipher. Small portions remain unchanged, ++ * Copyright (c) 2003 Markus Friedl ++ * ++ * 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(WITH_OPENSSL) ++#include ++ ++#include ++#include ++ ++#include ++ ++#include "xmalloc.h" ++#include "log.h" ++#include ++ ++/* compatibility with old or broken OpenSSL versions */ ++#include "openbsd-compat/openssl-compat.h" ++ ++#ifndef USE_BUILTIN_RIJNDAEL ++#include ++#endif ++ ++#include ++ ++/*-------------------- TUNABLES --------------------*/ ++/* maximum number of threads and queues */ ++#define MAX_THREADS 32 ++#define MAX_NUMKQ (MAX_THREADS * 2) ++ ++/* Number of pregen threads to use */ ++int cipher_threads = 2; ++ ++/* Number of keystream queues */ ++int numkq = 4; ++ ++/* Length of a keystream queue */ ++#define KQLEN 4096 ++ ++/* Processor cacheline length */ ++#define CACHELINE_LEN 64 ++ ++/* Collect thread stats and print at cancellation when in debug mode */ ++#define CIPHER_THREAD_STATS ++ ++/* Can the system do unaligned loads natively? */ ++#if defined(__aarch64__) || \ ++ defined(__i386__) || \ ++ defined(__powerpc__) || \ ++ defined(__x86_64__) ++# define CIPHER_UNALIGNED_OK ++#endif ++#if defined(__SIZEOF_INT128__) ++# define CIPHER_INT128_OK ++#endif ++/*-------------------- END TUNABLES --------------------*/ ++ ++ ++const EVP_CIPHER *evp_aes_ctr_mt(void); ++ ++#ifdef CIPHER_THREAD_STATS ++/* ++ * Struct to collect thread stats ++ */ ++struct thread_stats { ++ u_int fills; ++ u_int skips; ++ u_int waits; ++ u_int drains; ++}; ++ ++/* ++ * Debug print the thread stats ++ * Use with pthread_cleanup_push for displaying at thread cancellation ++ */ ++static void ++thread_loop_stats(void *x) ++{ ++ struct thread_stats *s = x; ++ ++ debug("AES-CTR MT tid %lu - %u fills, %u skips, %u waits", pthread_self(), ++ s->fills, s->skips, s->waits); ++} ++ ++# define STATS_STRUCT(s) struct thread_stats s ++# define STATS_INIT(s) { memset(&s, 0, sizeof(s)); } ++# define STATS_FILL(s) { s.fills++; } ++# define STATS_SKIP(s) { s.skips++; } ++# define STATS_WAIT(s) { s.waits++; } ++# define STATS_DRAIN(s) { s.drains++; } ++#else ++# define STATS_STRUCT(s) ++# define STATS_INIT(s) ++# define STATS_FILL(s) ++# define STATS_SKIP(s) ++# define STATS_WAIT(s) ++# define STATS_DRAIN(s) ++#endif ++ ++/* Keystream Queue state */ ++enum { ++ KQINIT, ++ KQEMPTY, ++ KQFILLING, ++ KQFULL, ++ KQDRAINING ++}; ++ ++/* Keystream Queue struct */ ++struct kq { ++ u_char keys[KQLEN][AES_BLOCK_SIZE]; ++ u_char ctr[AES_BLOCK_SIZE]; ++ u_char pad0[CACHELINE_LEN]; ++ int qstate; ++ pthread_mutex_t lock; ++ pthread_cond_t cond; ++ u_char pad1[CACHELINE_LEN]; ++}; ++ ++/* Context struct */ ++struct ssh_aes_ctr_ctx_mt ++{ ++ int struct_id; ++ struct kq q[MAX_NUMKQ]; ++ AES_KEY aes_ctx; ++ STATS_STRUCT(stats); ++ u_char aes_counter[AES_BLOCK_SIZE]; ++ pthread_t tid[MAX_THREADS]; ++ int id[MAX_THREADS]; ++ pthread_rwlock_t tid_lock; ++#ifdef __APPLE__ ++ pthread_rwlock_t stop_lock; ++ int exit_flag; ++#endif /* __APPLE__ */ ++ int state; ++ int qidx; ++ int ridx; ++}; ++ ++/* ++ * increment counter 'ctr', ++ * the counter is of size 'len' bytes and stored in network-byte-order. ++ * (LSB at ctr[len-1], MSB at ctr[0]) ++ */ ++static void ++ssh_ctr_inc(u_char *ctr, size_t len) ++{ ++ int i; ++ ++ for (i = len - 1; i >= 0; i--) ++ if (++ctr[i]) /* continue on overflow */ ++ return; ++} ++ ++/* ++ * Add num to counter 'ctr' ++ */ ++static void ++ssh_ctr_add(u_char *ctr, uint32_t num, u_int len) ++{ ++ int i; ++ uint16_t n; ++ ++ for (n = 0, i = len - 1; i >= 0 && (num || n); i--) { ++ n = ctr[i] + (num & 0xff) + n; ++ num >>= 8; ++ ctr[i] = n & 0xff; ++ n >>= 8; ++ } ++} ++ ++/* ++ * Threads may be cancelled in a pthread_cond_wait, we must free the mutex ++ */ ++static void ++thread_loop_cleanup(void *x) ++{ ++ pthread_mutex_unlock((pthread_mutex_t *)x); ++} ++ ++#ifdef __APPLE__ ++/* Check if we should exit, we are doing both cancel and exit condition ++ * since on OSX threads seem to occasionally fail to notice when they have ++ * been cancelled. We want to have a backup to make sure that we won't hang ++ * when the main process join()-s the cancelled thread. ++ */ ++static void ++thread_loop_check_exit(struct ssh_aes_ctr_ctx_mt *c) ++{ ++ int exit_flag; ++ ++ pthread_rwlock_rdlock(&c->stop_lock); ++ exit_flag = c->exit_flag; ++ pthread_rwlock_unlock(&c->stop_lock); ++ ++ if (exit_flag) ++ pthread_exit(NULL); ++} ++#else ++# define thread_loop_check_exit(s) ++#endif /* __APPLE__ */ ++ ++/* ++ * Helper function to terminate the helper threads ++ */ ++static void ++stop_and_join_pregen_threads(struct ssh_aes_ctr_ctx_mt *c) ++{ ++ int i; ++ ++#ifdef __APPLE__ ++ /* notify threads that they should exit */ ++ pthread_rwlock_wrlock(&c->stop_lock); ++ c->exit_flag = TRUE; ++ pthread_rwlock_unlock(&c->stop_lock); ++#endif /* __APPLE__ */ ++ ++ /* Cancel pregen threads */ ++ for (i = 0; i < cipher_threads; i++) { ++ debug ("Canceled %lu (%d,%d)", c->tid[i], c->struct_id, c->id[i]); ++ pthread_cancel(c->tid[i]); ++ } ++ /* shouldn't need this - see commit logs for hpn-7_7_P1 -cjr 11/7/19*/ ++ /* for (i = 0; i < numkq; i++) { */ ++ /* pthread_mutex_lock(&c->q[i].lock); */ ++ /* pthread_cond_broadcast(&c->q[i].cond); */ ++ /* pthread_mutex_unlock(&c->q[i].lock); */ ++ /* } */ ++ for (i = 0; i < cipher_threads; i++) { ++ if (pthread_kill(c->tid[i], 0) != 0) ++ debug3("AES-CTR MT pthread_join failure: Invalid thread id %lu in %s", c->tid[i], __FUNCTION__); ++ else { ++ debug ("Joining %lu (%d, %d)", c->tid[i], c->struct_id, c->id[i]); ++ pthread_join(c->tid[i], NULL); ++ } ++ } ++} ++ ++/* ++ * The life of a pregen thread: ++ * Find empty keystream queues and fill them using their counter. ++ * When done, update counter for the next fill. ++ */ ++static void * ++thread_loop(void *x) ++{ ++ AES_KEY key; ++ STATS_STRUCT(stats); ++ struct ssh_aes_ctr_ctx_mt *c = x; ++ struct kq *q; ++ int i; ++ int qidx; ++ pthread_t first_tid; ++ ++ /* Threads stats on cancellation */ ++ STATS_INIT(stats); ++#ifdef CIPHER_THREAD_STATS ++ pthread_cleanup_push(thread_loop_stats, &stats); ++#endif ++ ++ /* Thread local copy of AES key */ ++ memcpy(&key, &c->aes_ctx, sizeof(key)); ++ ++ pthread_rwlock_rdlock(&c->tid_lock); ++ first_tid = c->tid[0]; ++ pthread_rwlock_unlock(&c->tid_lock); ++ ++ /* ++ * Handle the special case of startup, one thread must fill ++ * the first KQ then mark it as draining. Lock held throughout. ++ */ ++ if (pthread_equal(pthread_self(), first_tid)) { ++ q = &c->q[0]; ++ pthread_mutex_lock(&q->lock); ++ if (q->qstate == KQINIT) { ++ for (i = 0; i < KQLEN; i++) { ++ AES_encrypt(q->ctr, q->keys[i], &key); ++ ssh_ctr_inc(q->ctr, AES_BLOCK_SIZE); ++ } ++ ssh_ctr_add(q->ctr, KQLEN * (numkq - 1), AES_BLOCK_SIZE); ++ q->qstate = KQDRAINING; ++ STATS_FILL(stats); ++ pthread_cond_broadcast(&q->cond); ++ } ++ pthread_mutex_unlock(&q->lock); ++ } else ++ STATS_SKIP(stats); ++ ++ /* ++ * Normal case is to find empty queues and fill them, skipping over ++ * queues already filled by other threads and stopping to wait for ++ * a draining queue to become empty. ++ * ++ * Multiple threads may be waiting on a draining queue and awoken ++ * when empty. The first thread to wake will mark it as filling, ++ * others will move on to fill, skip, or wait on the next queue. ++ */ ++ for (qidx = 1;; qidx = (qidx + 1) % numkq) { ++ /* Check if I was cancelled, also checked in cond_wait */ ++ pthread_testcancel(); ++ ++ /* Check if we should exit as well */ ++ thread_loop_check_exit(c); ++ ++ /* Lock queue and block if its draining */ ++ q = &c->q[qidx]; ++ pthread_mutex_lock(&q->lock); ++ pthread_cleanup_push(thread_loop_cleanup, &q->lock); ++ while (q->qstate == KQDRAINING || q->qstate == KQINIT) { ++ STATS_WAIT(stats); ++ thread_loop_check_exit(c); ++ pthread_cond_wait(&q->cond, &q->lock); ++ } ++ pthread_cleanup_pop(0); ++ ++ /* If filling or full, somebody else got it, skip */ ++ if (q->qstate != KQEMPTY) { ++ pthread_mutex_unlock(&q->lock); ++ STATS_SKIP(stats); ++ continue; ++ } ++ ++ /* ++ * Empty, let's fill it. ++ * Queue lock is relinquished while we do this so others ++ * can see that it's being filled. ++ */ ++ q->qstate = KQFILLING; ++ pthread_cond_broadcast(&q->cond); ++ pthread_mutex_unlock(&q->lock); ++ for (i = 0; i < KQLEN; i++) { ++ AES_encrypt(q->ctr, q->keys[i], &key); ++ ssh_ctr_inc(q->ctr, AES_BLOCK_SIZE); ++ } ++ ++ /* Re-lock, mark full and signal consumer */ ++ pthread_mutex_lock(&q->lock); ++ ssh_ctr_add(q->ctr, KQLEN * (numkq - 1), AES_BLOCK_SIZE); ++ q->qstate = KQFULL; ++ STATS_FILL(stats); ++ pthread_cond_broadcast(&q->cond); ++ pthread_mutex_unlock(&q->lock); ++ } ++ ++#ifdef CIPHER_THREAD_STATS ++ /* Stats */ ++ pthread_cleanup_pop(1); ++#endif ++ ++ return NULL; ++} ++ ++static int ++ssh_aes_ctr(EVP_CIPHER_CTX *ctx, u_char *dest, const u_char *src, ++ LIBCRYPTO_EVP_INL_TYPE len) ++{ ++ typedef union { ++#ifdef CIPHER_INT128_OK ++ __uint128_t *u128; ++#endif ++ uint64_t *u64; ++ uint32_t *u32; ++ uint8_t *u8; ++ const uint8_t *cu8; ++ uintptr_t u; ++ } ptrs_t; ++ ptrs_t destp, srcp, bufp; ++ uintptr_t align; ++ struct ssh_aes_ctr_ctx_mt *c; ++ struct kq *q, *oldq; ++ int ridx; ++ u_char *buf; ++ ++ if (len == 0) ++ return 1; ++ if ((c = EVP_CIPHER_CTX_get_app_data(ctx)) == NULL) ++ return 0; ++ ++ q = &c->q[c->qidx]; ++ ridx = c->ridx; ++ ++ /* src already padded to block multiple */ ++ srcp.cu8 = src; ++ destp.u8 = dest; ++ while (len > 0) { ++ buf = q->keys[ridx]; ++ bufp.u8 = buf; ++ ++ /* figure out the alignment on the fly */ ++#ifdef CIPHER_UNALIGNED_OK ++ align = 0; ++#else ++ align = destp.u | srcp.u | bufp.u; ++#endif ++ ++#ifdef CIPHER_INT128_OK ++ if ((align & 0xf) == 0) { ++ destp.u128[0] = srcp.u128[0] ^ bufp.u128[0]; ++ } else ++#endif ++ if ((align & 0x7) == 0) { ++ destp.u64[0] = srcp.u64[0] ^ bufp.u64[0]; ++ destp.u64[1] = srcp.u64[1] ^ bufp.u64[1]; ++ } else if ((align & 0x3) == 0) { ++ destp.u32[0] = srcp.u32[0] ^ bufp.u32[0]; ++ destp.u32[1] = srcp.u32[1] ^ bufp.u32[1]; ++ destp.u32[2] = srcp.u32[2] ^ bufp.u32[2]; ++ destp.u32[3] = srcp.u32[3] ^ bufp.u32[3]; ++ } else { ++ size_t i; ++ for (i = 0; i < AES_BLOCK_SIZE; ++i) ++ dest[i] = src[i] ^ buf[i]; ++ } ++ ++ destp.u += AES_BLOCK_SIZE; ++ srcp.u += AES_BLOCK_SIZE; ++ len -= AES_BLOCK_SIZE; ++ ssh_ctr_inc(c->aes_counter, AES_BLOCK_SIZE); ++ ++ /* Increment read index, switch queues on rollover */ ++ if ((ridx = (ridx + 1) % KQLEN) == 0) { ++ oldq = q; ++ ++ /* Mark next queue draining, may need to wait */ ++ c->qidx = (c->qidx + 1) % numkq; ++ q = &c->q[c->qidx]; ++ pthread_mutex_lock(&q->lock); ++ while (q->qstate != KQFULL) { ++ STATS_WAIT(c->stats); ++ pthread_cond_wait(&q->cond, &q->lock); ++ } ++ q->qstate = KQDRAINING; ++ pthread_cond_broadcast(&q->cond); ++ pthread_mutex_unlock(&q->lock); ++ ++ /* Mark consumed queue empty and signal producers */ ++ pthread_mutex_lock(&oldq->lock); ++ oldq->qstate = KQEMPTY; ++ STATS_DRAIN(c->stats); ++ pthread_cond_broadcast(&oldq->cond); ++ pthread_mutex_unlock(&oldq->lock); ++ } ++ } ++ c->ridx = ridx; ++ return 1; ++} ++ ++#define HAVE_NONE 0 ++#define HAVE_KEY 1 ++#define HAVE_IV 2 ++ ++int X = 0; ++ ++static int ++ssh_aes_ctr_init(EVP_CIPHER_CTX *ctx, const u_char *key, const u_char *iv, ++ int enc) ++{ ++ struct ssh_aes_ctr_ctx_mt *c; ++ int i; ++ ++ /* get the number of cores in the system */ ++ /* if it's not linux it currently defaults to 2 */ ++ /* divide by 2 to get threads for each direction (MODE_IN||MODE_OUT) */ ++#ifdef __linux__ ++ cipher_threads = sysconf(_SC_NPROCESSORS_ONLN) / 2; ++#endif /*__linux__*/ ++#ifdef __APPLE__ ++ cipher_threads = sysconf(_SC_NPROCESSORS_ONLN) / 2; ++#endif /*__APPLE__*/ ++#ifdef __FREEBSD__ ++ int req[2]; ++ size_t len; ++ ++ req[0] = CTL_HW; ++ req[1] = HW_NCPU; ++ ++ len = sizeof(ncpu); ++ sysctl(req, 2, &cipher_threads, &len, NULL, 0); ++ cipher_threads = cipher_threads / 2; ++#endif /*__FREEBSD__*/ ++ ++ /* if they have less than 4 cores spin up 4 threads anyway */ ++ if (cipher_threads < 2) ++ cipher_threads = 2; ++ ++ /* assure that we aren't trying to create more threads */ ++ /* than we have in the struct. cipher_threads is half the */ ++ /* total of allowable threads hence the odd looking math here */ ++ if (cipher_threads * 2 > MAX_THREADS) ++ cipher_threads = MAX_THREADS / 2; ++ ++ /* set the number of keystream queues */ ++ numkq = cipher_threads * 2; ++ ++ if ((c = EVP_CIPHER_CTX_get_app_data(ctx)) == NULL) { ++ c = xmalloc(sizeof(*c)); ++ pthread_rwlock_init(&c->tid_lock, NULL); ++#ifdef __APPLE__ ++ pthread_rwlock_init(&c->stop_lock, NULL); ++ c->exit_flag = FALSE; ++#endif /* __APPLE__ */ ++ ++ c->state = HAVE_NONE; ++ for (i = 0; i < numkq; i++) { ++ pthread_mutex_init(&c->q[i].lock, NULL); ++ pthread_cond_init(&c->q[i].cond, NULL); ++ } ++ ++ STATS_INIT(c->stats); ++ EVP_CIPHER_CTX_set_app_data(ctx, c); ++ } ++ ++ if (c->state == (HAVE_KEY | HAVE_IV)) { ++ /* tell the pregen threads to exit */ ++ stop_and_join_pregen_threads(c); ++ ++#ifdef __APPLE__ ++ /* reset the exit flag */ ++ c->exit_flag = FALSE; ++#endif /* __APPLE__ */ ++ ++ /* Start over getting key & iv */ ++ c->state = HAVE_NONE; ++ } ++ ++ if (key != NULL) { ++ AES_set_encrypt_key(key, EVP_CIPHER_CTX_key_length(ctx) * 8, ++ &c->aes_ctx); ++ c->state |= HAVE_KEY; ++ } ++ ++ if (iv != NULL) { ++ memcpy(c->aes_counter, iv, AES_BLOCK_SIZE); ++ c->state |= HAVE_IV; ++ } ++ ++ if (c->state == (HAVE_KEY | HAVE_IV)) { ++ /* Clear queues */ ++ memcpy(c->q[0].ctr, c->aes_counter, AES_BLOCK_SIZE); ++ c->q[0].qstate = KQINIT; ++ for (i = 1; i < numkq; i++) { ++ memcpy(c->q[i].ctr, c->aes_counter, AES_BLOCK_SIZE); ++ ssh_ctr_add(c->q[i].ctr, i * KQLEN, AES_BLOCK_SIZE); ++ c->q[i].qstate = KQEMPTY; ++ } ++ c->qidx = 0; ++ c->ridx = 0; ++ ++ /* Start threads */ ++ for (i = 0; i < cipher_threads; i++) { ++ pthread_rwlock_wrlock(&c->tid_lock); ++ if (pthread_create(&c->tid[i], NULL, thread_loop, c) != 0) ++ debug ("AES-CTR MT Could not create thread in %s", __FUNCTION__); /*should die here */ ++ else { ++ if (!c->struct_id) ++ c->struct_id = X++; ++ c->id[i] = i; ++ debug ("AES-CTR MT spawned a thread with id %lu in %s (%d, %d)", c->tid[i], __FUNCTION__, c->struct_id, c->id[i]); ++ } ++ pthread_rwlock_unlock(&c->tid_lock); ++ } ++ pthread_mutex_lock(&c->q[0].lock); ++ while (c->q[0].qstate == KQINIT) ++ pthread_cond_wait(&c->q[0].cond, &c->q[0].lock); ++ pthread_mutex_unlock(&c->q[0].lock); ++ } ++ return 1; ++} ++ ++/* this function is no longer used but might prove handy in the future ++ * this comment also applies to ssh_aes_ctr_thread_reconstruction ++ */ ++void ++ssh_aes_ctr_thread_destroy(EVP_CIPHER_CTX *ctx) ++{ ++ struct ssh_aes_ctr_ctx_mt *c; ++ ++ c = EVP_CIPHER_CTX_get_app_data(ctx); ++ stop_and_join_pregen_threads(c); ++} ++ ++void ++ssh_aes_ctr_thread_reconstruction(EVP_CIPHER_CTX *ctx) ++{ ++ struct ssh_aes_ctr_ctx_mt *c; ++ int i; ++ c = EVP_CIPHER_CTX_get_app_data(ctx); ++ /* reconstruct threads */ ++ for (i = 0; i < cipher_threads; i++) { ++ pthread_rwlock_wrlock(&c->tid_lock); ++ if (pthread_create(&c->tid[i], NULL, thread_loop, c) !=0 ) ++ debug("AES-CTR MT could not create thread in %s", __FUNCTION__); ++ else { ++ c->struct_id = X++; ++ c->id[i] = i; ++ debug ("AES-CTR MT spawned a thread with id %lu in %s (%d, %d)", c->tid[i], __FUNCTION__, c->struct_id, c->id[i]); ++ debug("AES-CTR MT spawned a thread with id %lu in %s", c->tid[i], __FUNCTION__); ++ } ++ pthread_rwlock_unlock(&c->tid_lock); ++ } ++} ++ ++static int ++ssh_aes_ctr_cleanup(EVP_CIPHER_CTX *ctx) ++{ ++ struct ssh_aes_ctr_ctx_mt *c; ++ ++ if ((c = EVP_CIPHER_CTX_get_app_data(ctx)) != NULL) { ++#ifdef CIPHER_THREAD_STATS ++ debug("AES-CTR MT main thread: %u drains, %u waits", c->stats.drains, ++ c->stats.waits); ++#endif ++ stop_and_join_pregen_threads(c); ++ ++ memset(c, 0, sizeof(*c)); ++ free(c); ++ EVP_CIPHER_CTX_set_app_data(ctx, NULL); ++ } ++ return 1; ++} ++ ++/* */ ++const EVP_CIPHER * ++evp_aes_ctr_mt(void) ++{ ++# if OPENSSL_VERSION_NUMBER >= 0x10100000UL ++ static EVP_CIPHER *aes_ctr; ++ aes_ctr = EVP_CIPHER_meth_new(NID_undef, 16/*block*/, 16/*key*/); ++ EVP_CIPHER_meth_set_iv_length(aes_ctr, AES_BLOCK_SIZE); ++ EVP_CIPHER_meth_set_init(aes_ctr, ssh_aes_ctr_init); ++ EVP_CIPHER_meth_set_cleanup(aes_ctr, ssh_aes_ctr_cleanup); ++ EVP_CIPHER_meth_set_do_cipher(aes_ctr, ssh_aes_ctr); ++# ifndef SSH_OLD_EVP ++ EVP_CIPHER_meth_set_flags(aes_ctr, EVP_CIPH_CBC_MODE ++ | EVP_CIPH_VARIABLE_LENGTH ++ | EVP_CIPH_ALWAYS_CALL_INIT ++ | EVP_CIPH_CUSTOM_IV); ++# endif /*SSH_OLD_EVP*/ ++ return (aes_ctr); ++# else /*earlier versions of openssl*/ ++ static EVP_CIPHER aes_ctr; ++ memset(&aes_ctr, 0, sizeof(EVP_CIPHER)); ++ aes_ctr.nid = NID_undef; ++ aes_ctr.block_size = AES_BLOCK_SIZE; ++ aes_ctr.iv_len = AES_BLOCK_SIZE; ++ aes_ctr.key_len = 16; ++ aes_ctr.init = ssh_aes_ctr_init; ++ aes_ctr.cleanup = ssh_aes_ctr_cleanup; ++ aes_ctr.do_cipher = ssh_aes_ctr; ++# ifndef SSH_OLD_EVP ++ aes_ctr.flags = EVP_CIPH_CBC_MODE | EVP_CIPH_VARIABLE_LENGTH | ++ EVP_CIPH_ALWAYS_CALL_INIT | EVP_CIPH_CUSTOM_IV; ++# endif /*SSH_OLD_EVP*/ ++ return &aes_ctr; ++# endif /*OPENSSH_VERSION_NUMBER*/ ++} ++ ++#endif /* defined(WITH_OPENSSL) */ +diff -Nur openssh-8.0p1.orig/cipher.h openssh-8.0p1/cipher.h +--- openssh-8.0p1.orig/cipher.h 2024-07-09 15:12:37.122394246 +0200 ++++ openssh-8.0p1/cipher.h 2024-07-09 15:13:50.323612068 +0200 +@@ -66,7 +66,9 @@ + + struct sshcipher_ctx; + +-const struct sshcipher *cipher_by_name(const char *); ++void ssh_aes_ctr_thread_destroy(EVP_CIPHER_CTX *ctx); // defined in cipher-ctr-mt.c ++void ssh_aes_ctr_thread_reconstruction(EVP_CIPHER_CTX *ctx); ++struct sshcipher *cipher_by_name(const char *); + const char *cipher_warning_message(const struct sshcipher_ctx *); + int ciphers_valid(const char *); + char *cipher_alg_list(char, int); +@@ -83,6 +85,8 @@ + u_int cipher_authlen(const struct sshcipher *); + u_int cipher_ivlen(const struct sshcipher *); + u_int cipher_is_cbc(const struct sshcipher *); ++void cipher_reset_multithreaded(void); ++const char *cipher_ctx_name(const struct sshcipher_ctx *); + + u_int cipher_ctx_is_plaintext(struct sshcipher_ctx *); + +diff -Nur openssh-8.0p1.orig/clientloop.c openssh-8.0p1/clientloop.c +--- openssh-8.0p1.orig/clientloop.c 2024-07-09 15:12:37.134394282 +0200 ++++ openssh-8.0p1/clientloop.c 2024-07-09 20:04:27.376111681 +0200 +@@ -1607,7 +1607,9 @@ + 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); ++ /* again is this really necessary for X11? */ ++ options.hpn_disabled ? CHAN_TCP_WINDOW_DEFAULT : options.hpn_buffer_size, ++ CHAN_X11_PACKET_DEFAULT, 0, "x11", 1); + c->force_drain = 1; + return c; + } +@@ -1632,7 +1634,8 @@ + } + c = channel_new(ssh, "authentication agent connection", + SSH_CHANNEL_OPEN, sock, sock, -1, +- CHAN_X11_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, ++ options.hpn_disabled ? CHAN_X11_WINDOW_DEFAULT : options.hpn_buffer_size, ++ CHAN_TCP_PACKET_DEFAULT, 0, + "authentication agent connection", 1); + c->force_drain = 1; + return c; +@@ -1659,7 +1662,8 @@ + 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); ++ options.hpn_disabled ? CHAN_TCP_WINDOW_DEFAULT : options.hpn_buffer_size, ++ CHAN_TCP_PACKET_DEFAULT, 0, "tun", 1); + c->datagram = 1; + + #if defined(SSH_TUN_FILTER) +diff -Nur openssh-8.0p1.orig/compat.c openssh-8.0p1/compat.c +--- openssh-8.0p1.orig/compat.c 2019-04-18 00:52:57.000000000 +0200 ++++ openssh-8.0p1/compat.c 2024-07-09 15:13:50.324612071 +0200 +@@ -150,6 +150,13 @@ + debug("match: %s pat %s compat 0x%08x", + version, check[i].pat, check[i].bugs); + datafellows = check[i].bugs; /* XXX for now */ ++ /* Check to see if the remote side is OpenSSH and not HPN */ ++ if (strstr(version, "OpenSSH") != NULL) { ++ if (strstr(version, "hpn") == NULL) { ++ datafellows |= SSH_BUG_LARGEWINDOW; ++ debug("Remote is NON-HPN aware"); ++ } ++ } + return check[i].bugs; + } + } +diff -Nur openssh-8.0p1.orig/compat.h openssh-8.0p1/compat.h +--- openssh-8.0p1.orig/compat.h 2019-04-18 00:52:57.000000000 +0200 ++++ openssh-8.0p1/compat.h 2024-07-09 15:13:50.325612074 +0200 +@@ -62,6 +62,7 @@ + #define SSH_BUG_CURVE25519PAD 0x10000000 + #define SSH_BUG_HOSTKEYS 0x20000000 + #define SSH_BUG_DHGEX_LARGE 0x40000000 ++#define SSH_BUG_LARGEWINDOW 0x80000000 + + u_int compat_datafellows(const char *); + int proto_spec(const char *); +diff -Nur openssh-8.0p1.orig/HPN-README openssh-8.0p1/HPN-README +--- openssh-8.0p1.orig/HPN-README 1970-01-01 01:00:00.000000000 +0100 ++++ openssh-8.0p1/HPN-README 2024-07-09 20:39:56.028357828 +0200 +@@ -0,0 +1,130 @@ ++Notes: ++ ++MULTI-THREADED CIPHER: ++The AES cipher in CTR mode has been multithreaded (MTR-AES-CTR). This will allow ssh installations ++on hosts with multiple cores to use more than one processing core during encryption. ++Tests have show significant throughput performance increases when using MTR-AES-CTR up ++to and including a full gigabit per second on quad core systems. It should be possible to ++achieve full line rate on dual core systems but OS and data management overhead makes this ++more difficult to achieve. The cipher stream from MTR-AES-CTR is entirely compatible with single ++thread AES-CTR (ST-AES-CTR) implementations and should be 100% backward compatible. Optimal ++performance requires the MTR-AES-CTR mode be enabled on both ends of the connection. ++The MTR-AES-CTR replaces ST-AES-CTR and is used in exactly the same way with the same ++nomenclature. ++Use examples: ++ ssh -caes128-ctr you@host.com ++ scp -oCipher=aes256-ctr file you@host.com:~/file ++ ++NONE CIPHER: ++To use the NONE option you must have the NoneEnabled switch set on the server and ++you *must* have *both* NoneEnabled and NoneSwitch set to yes on the client. The NONE ++feature works with ALL ssh subsystems (as far as we can tell) *AS LONG AS* a tty is not ++spawned. If a user uses the -T switch to prevent a tty being created the NONE cipher will ++be disabled. ++ ++The performance increase will only be as good as the network and TCP stack tuning ++on the reciever side of the connection allows. As a rule of thumb a user will need ++at least 10Mb/s connection with a 100ms RTT to see a doubling of performance. The ++HPN-SSH home page describes this in greater detail. ++ ++http://www.psc.edu/networking/projects/hpn-ssh ++ ++BUFFER SIZES: ++ ++If HPN is disabled the receive buffer size will be set to the ++OpenSSH default of 64K. ++ ++If an HPN system connects to a nonHPN system the receive buffer will ++be set to the HPNBufferSize value. The default is 2MB but user adjustable. ++ ++If an HPN to HPN connection is established a number of different things might ++happen based on the user options and conditions. ++ ++Conditions: HPNBufferSize NOT Set, TCPRcvBufPoll enabled, TCPRcvBuf NOT Set ++HPN Buffer Size = up to 64MB ++This is the default state. The HPN buffer size will grow to a maximum of 64MB ++as the TCP receive buffer grows. The maximum HPN Buffer size of 64MB is ++geared towards 10GigE transcontinental connections. ++ ++Conditions: HPNBufferSize NOT Set, TCPRcvBufPoll disabled, TCPRcvBuf NOT Set ++HPN Buffer Size = TCP receive buffer value. ++Users on non-autotuning systems should disable TCPRcvBufPoll in the ++ssh_config and sshd_config ++ ++Conditions: HPNBufferSize SET, TCPRcvBufPoll disabled, TCPRcvBuf NOT Set ++HPN Buffer Size = minimum of TCP receive buffer and HPNBufferSize. ++This would be the system defined TCP receive buffer (RWIN). ++ ++Conditions: HPNBufferSize SET, TCPRcvBufPoll disabled, TCPRcvBuf SET ++HPN Buffer Size = minimum of TCPRcvBuf and HPNBufferSize. ++Generally there is no need to set both. ++ ++Conditions: HPNBufferSize SET, TCPRcvBufPoll enabled, TCPRcvBuf NOT Set ++HPN Buffer Size = grows to HPNBufferSize ++The buffer will grow up to the maximum size specified here. ++ ++Conditions: HPNBufferSize SET, TCPRcvBufPoll enabled, TCPRcvBuf SET ++HPN Buffer Size = minimum of TCPRcvBuf and HPNBufferSize. ++Generally there is no need to set both of these, especially on autotuning ++systems. However, if the users wishes to override the autotuning this would be ++one way to do it. ++ ++Conditions: HPNBufferSize NOT Set, TCPRcvBufPoll enabled, TCPRcvBuf SET ++HPN Buffer Size = TCPRcvBuf. ++This will override autotuning and set the TCP recieve buffer to the user defined ++value. ++ ++ ++HPN Specific Configuration options ++ ++TcpRcvBuf=[int]KB client ++ Set the TCP socket receive buffer to n Kilobytes. It can be set up to the ++maximum socket size allowed by the system. This is useful in situations where ++the tcp receive window is set low but the maximum buffer size is set ++higher (as is typical). This works on a per TCP connection basis. You can also ++use this to artifically limit the transfer rate of the connection. In these ++cases the throughput will be no more than n/RTT. The minimum buffer size is 1KB. ++Default is the current system wide tcp receive buffer size. ++ ++TcpRcvBufPoll=[yes/no] client/server ++ Enable of disable the polling of the tcp receive buffer through the life ++of the connection. You would want to make sure that this option is enabled ++for systems making use of autotuning kernels (linux 2.4.24+, 2.6, MS Vista) ++default is yes. ++ ++NoneEnabled=[yes/no] client/server ++ Enable or disable the use of the None cipher. Care must always be used ++when enabling this as it will allow users to send data in the clear. However, ++it is important to note that authentication information remains encrypted ++even if this option is enabled. Set to no by default. ++ ++NoneSwitch=[yes/no] client ++ Switch the encryption cipher being used to the None cipher after ++authentication takes place. NoneEnabled must be enabled on both the client ++and server side of the connection. When the connection switches to the NONE ++cipher a warning is sent to STDERR. The connection attempt will fail with an ++error if a client requests a NoneSwitch from the server that does not explicitly ++have NoneEnabled set to yes. Note: The NONE cipher cannot be used in ++interactive (shell) sessions and it will fail silently. Set to no by default. ++ ++HPNDisabled=[yes/no] client/server ++ In some situations, such as transfers on a local area network, the impact ++of the HPN code produces a net decrease in performance. In these cases it is ++helpful to disable the HPN functionality. By default HPNDisabled is set to no. ++ ++HPNBufferSize=[int]KB client/server ++ This is the default buffer size the HPN functionality uses when interacting ++with nonHPN SSH installations. Conceptually this is similar to the TcpRcvBuf ++option as applied to the internal SSH flow control. This value can range from ++1KB to 64MB (1-65536). Use of oversized or undersized buffers can cause performance ++problems depending on the length of the network path. The default size of this buffer ++is 2MB. ++ ++ ++Credits: This patch was conceived, designed, and led by Chris Rapier (rapier@psc.edu) ++ The majority of the actual coding for versions up to HPN12v1 was performed ++ by Michael Stevens (mstevens@andrew.cmu.edu). The MT-AES-CTR cipher was ++ implemented by Ben Bennet (ben@psc.edu) and improved by Mike Tasota ++ (tasota@gmail.com) an NSF REU grant recipient for 2013. ++ This work was financed, in part, by Cisco System, Inc., the National ++ Library of Medicine, and the National Science Foundation. +diff -Nur openssh-8.0p1.orig/kex.c openssh-8.0p1/kex.c +--- openssh-8.0p1.orig/kex.c 2024-07-09 15:12:37.153394338 +0200 ++++ openssh-8.0p1/kex.c 2024-07-09 20:34:06.280331402 +0200 +@@ -62,6 +62,7 @@ + + #include "ssherr.h" + #include "sshbuf.h" ++#include "canohost.h" + #include "digest.h" + #include "audit.h" + +@@ -942,6 +943,11 @@ + int nenc, nmac, ncomp; + u_int mode, ctos, need, dh_need, authlen; + int r, first_kex_follows; ++ int auth_flag; ++ int log_flag = 0; ++ ++ auth_flag = packet_authentication_state(ssh); ++ debug("AUTH STATE IS %d", auth_flag); + + debug2("local %s KEXINIT proposal", kex->server ? "server" : "client"); + if ((r = kex_buf2prop(kex->my, NULL, &my)) != 0) +@@ -1032,11 +1038,35 @@ + peer[ncomp] = NULL; + goto out; + } ++ debug("REQUESTED ENC.NAME is '%s'", newkeys->enc.name); ++ if (strcmp(newkeys->enc.name, "none") == 0) { ++ debug("Requesting NONE. Authflag is %d", auth_flag); ++ if (auth_flag == 1) ++ debug("None requested post authentication."); ++ else ++ fatal("Pre-authentication none cipher requests are not allowed."); ++ } + 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); ++ /* ++ * client starts with ctos = 0 && log flag = 0 and no log. ++ * 2nd client pass ctos = 1 and flag = 1 so no log. ++ * server starts with ctos = 1 && log_flag = 0 so log. ++ * 2nd sever pass ctos = 1 && log flag = 1 so no log. ++ * -cjr ++ */ ++ if (ctos && !log_flag) { ++ logit("SSH: Server;Ltype: Kex;Remote: %s-%d;Enc: %s;MAC: %s;Comp: %s", ++ ssh_remote_ipaddr(ssh), ++ ssh_remote_port(ssh), ++ newkeys->enc.name, ++ authlen == 0 ? newkeys->mac.name : "", ++ newkeys->comp.name); ++ } ++ log_flag = 1; + } + need = dh_need = 0; + for (mode = 0; mode < MODE_MAX; mode++) { +@@ -1375,7 +1405,7 @@ + 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, ++ PROTOCOL_MAJOR_2, PROTOCOL_MINOR_2, SSH_RELEASE, + version_addendum == NULL ? "" : " ", + version_addendum == NULL ? "" : version_addendum)) != 0) { + error("%s: sshbuf_putf: %s", __func__, ssh_err(r)); +@@ -1509,6 +1539,14 @@ + r = SSH_ERR_INVALID_FORMAT; + goto out; + } ++ ++ /* report the version information to syslog if this is the server */ ++ if (timeout_ms == -1) { /* only the server uses this value */ ++ logit("SSH: Server;Ltype: Version;Remote: %s-%d;Protocol: %d.%d;Client: %.100s", ++ ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), ++ remote_major, remote_minor, remote_version); ++ } ++ + debug("Remote protocol version %d.%d, remote software version %.100s", + remote_major, remote_minor, remote_version); + ssh->compat = compat_datafellows(remote_version); +diff -Nur openssh-8.0p1.orig/Makefile.in openssh-8.0p1/Makefile.in +--- openssh-8.0p1.orig/Makefile.in 2024-07-09 15:12:37.193394457 +0200 ++++ openssh-8.0p1/Makefile.in 2024-07-09 15:16:22.114063733 +0200 +@@ -47,7 +47,7 @@ + LD=@LD@ + CFLAGS=@CFLAGS@ + CPPFLAGS=-I. -I$(srcdir) @CPPFLAGS@ $(PATHS) @DEFS@ +-LIBS=@LIBS@ ++LIBS=@LIBS@ -lpthread + LDAPLIBS=@LDAPLIBS@ + K5LIBS=@K5LIBS@ + GSSLIBS=@GSSLIBS@ +@@ -94,7 +94,7 @@ + LIBSSH_OBJS=${LIBOPENSSH_OBJS} \ + authfd.o authfile.o \ + canohost.o channels.o cipher.o cipher-aes.o cipher-aesctr.o \ +- cipher-ctr.o cleanup.o \ ++ cipher-ctr.o cleanup.o cipher-ctr-mt.o \ + compat.o crc32.o fatal.o hostfile.o \ + log.o match.o moduli.o nchan.o packet.o \ + readpass.o ttymodes.o xmalloc.o addrmatch.o \ +diff -Nur openssh-8.0p1.orig/packet.c openssh-8.0p1/packet.c +--- openssh-8.0p1.orig/packet.c 2024-07-09 15:12:37.154394341 +0200 ++++ openssh-8.0p1/packet.c 2024-07-09 20:11:49.905409843 +0200 +@@ -288,7 +288,7 @@ + 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"); ++ struct sshcipher *none = cipher_by_name("none"); + int r; + + if (none == NULL) { +@@ -942,6 +942,24 @@ + return 0; + } + ++/* this supports the forced rekeying required for the NONE cipher */ ++int rekey_requested = 0; ++void ++packet_request_rekeying(void) ++{ ++ rekey_requested = 1; ++} ++ ++/* used to determine if pre or post auth when rekeying for aes-ctr ++ * and none cipher switch */ ++int ++packet_authentication_state(const struct ssh *ssh) ++{ ++ struct session_state *state = ssh->state; ++ ++ return state->after_authentication; ++} ++ + #define MAX_PACKETS (1U<<31) + static int + ssh_packet_need_rekeying(struct ssh *ssh, u_int outbound_packet_len) +@@ -968,6 +986,13 @@ + if (state->p_send.packets == 0 && state->p_read.packets == 0) + return 0; + ++ /* used to force rekeying when called for by the none ++ * cipher switch and aes-mt-ctr methods -cjr */ ++ if (rekey_requested == 1) { ++ rekey_requested = 0; ++ return 1; ++ } ++ + /* Time-based rekeying */ + if (state->rekey_interval != 0 && + (int64_t)state->rekey_time + state->rekey_interval <= monotime()) +@@ -1872,17 +1897,21 @@ + switch (r) { + case SSH_ERR_CONN_CLOSED: + ssh_packet_clear_keys(ssh); ++ sshpkt_final_log_entry(ssh); + logdie("Connection closed by %s", remote_id); + case SSH_ERR_CONN_TIMEOUT: + ssh_packet_clear_keys(ssh); ++ sshpkt_final_log_entry(ssh); + logdie("Connection %s %s timed out", + ssh->state->server_side ? "from" : "to", remote_id); + case SSH_ERR_DISCONNECTED: + ssh_packet_clear_keys(ssh); ++ sshpkt_final_log_entry(ssh); + logdie("Disconnected from %s", remote_id); + case SSH_ERR_SYSTEM_ERROR: + if (errno == ECONNRESET) { + ssh_packet_clear_keys(ssh); ++ sshpkt_final_log_entry(ssh); + logdie("Connection reset by %s", remote_id); + } + /* FALLTHROUGH */ +@@ -1924,6 +1953,24 @@ + logdie("%s: should have exited", __func__); + } + ++/* this prints out the final log entry */ ++void ++sshpkt_final_log_entry (struct ssh *ssh) { ++ double total_time; ++ ++ if (ssh->start_time < 1) ++ /* this will produce a NaN in the output. -cjr */ ++ total_time = 0; ++ else ++ total_time = monotime_double() - ssh->start_time; ++ ++ logit("SSH: Server;LType: Throughput;Remote: %s-%d;IN: %lu;OUT: %lu;Duration: %.1f;tPut_in: %.1f;tPut_out: %.1f", ++ ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), ++ ssh->stdin_bytes, ssh->fdout_bytes, total_time, ++ ssh->stdin_bytes / total_time, ++ ssh->fdout_bytes / total_time); ++} ++ + /* + * Logs the error plus constructs and sends a disconnect packet, closes the + * connection, and exits. This function never returns. The error message +@@ -1993,6 +2040,8 @@ + return SSH_ERR_CONN_CLOSED; + if ((r = sshbuf_consume(state->output, len)) != 0) + return r; ++ else ++ ssh->stdin_bytes += len; + } + return 0; + } +@@ -2821,3 +2870,10 @@ + ssh->state->extra_pad = pad; + return 0; + } ++ ++/* need this for the moment for the aes-ctr cipher */ ++void * ++ssh_packet_get_send_context(struct ssh *ssh) ++{ ++ return ssh->state->send_context; ++} +diff -Nur openssh-8.0p1.orig/packet.h openssh-8.0p1/packet.h +--- openssh-8.0p1.orig/packet.h 2024-07-09 15:12:37.126394258 +0200 ++++ openssh-8.0p1/packet.h 2024-07-09 20:13:09.998644820 +0200 +@@ -86,6 +86,11 @@ + + /* APP data */ + void *app_data; ++ ++ /* logging data for ServerLogging patch*/ ++ double start_time; ++ u_long fdout_bytes; ++ u_long stdin_bytes; + }; + + typedef int (ssh_packet_hook_fn)(struct ssh *, struct sshbuf *, +@@ -155,6 +160,8 @@ + int ssh_packet_set_maxsize(struct ssh *, u_int); + u_int ssh_packet_get_maxsize(struct ssh *); + ++int packet_authentication_state(const struct ssh *); ++ + int ssh_packet_get_state(struct ssh *, struct sshbuf *); + int ssh_packet_set_state(struct ssh *, struct sshbuf *); + +@@ -169,6 +176,13 @@ + + void *ssh_packet_get_input(struct ssh *); + void *ssh_packet_get_output(struct ssh *); ++void *ssh_packet_get_receive_context(struct ssh *); ++void *ssh_packet_get_send_context(struct ssh *); ++ ++/* for forced packet rekeying post auth */ ++void packet_request_rekeying(void); ++/* final log entry support */ ++void sshpkt_final_log_entry (struct ssh *); + + /* new API */ + int sshpkt_start(struct ssh *ssh, u_char type); +diff -Nur openssh-8.0p1.orig/progressmeter.c openssh-8.0p1/progressmeter.c +--- openssh-8.0p1.orig/progressmeter.c 2019-04-18 00:52:57.000000000 +0200 ++++ openssh-8.0p1/progressmeter.c 2024-07-09 20:14:38.398904169 +0200 +@@ -68,6 +68,8 @@ + static off_t start_pos; /* initial position of transfer */ + static off_t end_pos; /* ending position of transfer */ + static off_t cur_pos; /* transfer position as of last refresh */ ++static off_t last_pos; ++static off_t max_delta_pos = 0; + static volatile off_t *counter; /* progress counter */ + static long stalled; /* how long we have been stalled */ + static int bytes_per_second; /* current speed in bytes per second */ +@@ -127,6 +129,7 @@ + int cur_speed; + int hours, minutes, seconds; + int file_len; ++ off_t delta_pos; + + if ((!force_update && !alarm_fired && !win_resized) || !can_output()) + return; +@@ -142,6 +145,10 @@ + now = monotime_double(); + bytes_left = end_pos - cur_pos; + ++ delta_pos = cur_pos - last_pos; ++ if (delta_pos > max_delta_pos) ++ max_delta_pos = delta_pos; ++ + if (bytes_left > 0) + elapsed = now - last_update; + else { +@@ -166,7 +173,7 @@ + + /* filename */ + buf[0] = '\0'; +- file_len = win_size - 36; ++ file_len = win_size - 45; + if (file_len > 0) { + buf[0] = '\r'; + snmprintf(buf+1, sizeof(buf)-1 , &file_len, "%*s", +@@ -191,6 +198,15 @@ + (off_t)bytes_per_second); + strlcat(buf, "/s ", win_size); + ++ /* instantaneous rate */ ++ if (bytes_left > 0) ++ format_rate(buf + strlen(buf), win_size - strlen(buf), ++ delta_pos); ++ else ++ format_rate(buf + strlen(buf), win_size - strlen(buf), ++ max_delta_pos); ++ strlcat(buf, "/s ", win_size); ++ + /* ETA */ + if (!transferred) + stalled += elapsed; +@@ -227,6 +243,7 @@ + + atomicio(vwrite, STDOUT_FILENO, buf, win_size - 1); + last_update = now; ++ last_pos = cur_pos; + } + + /*ARGSUSED*/ +diff -Nur openssh-8.0p1.orig/readconf.c openssh-8.0p1/readconf.c +--- openssh-8.0p1.orig/readconf.c 2024-07-09 15:12:37.196394466 +0200 ++++ openssh-8.0p1/readconf.c 2024-07-10 00:06:22.102591028 +0200 +@@ -68,6 +68,7 @@ + #include "myproposal.h" + #include "digest.h" + #include "ssh-gss.h" ++#include "sshbuf.h" + + /* Format of the configuration file: + +@@ -170,6 +171,9 @@ + oHashKnownHosts, + oTunnel, oTunnelDevice, + oLocalCommand, oPermitLocalCommand, oRemoteCommand, ++ oTcpRcvBufPoll, oTcpRcvBuf, oHPNDisabled, oHPNBufferSize, ++ oNoneEnabled, oNoneSwitch, ++ oDisableMTAES, + oVisualHostKey, + oKexAlgorithms, oIPQoS, oRequestTTY, oIgnoreUnknown, oProxyUseFdpass, + oCanonicalDomains, oCanonicalizeHostname, oCanonicalizeMaxDots, +@@ -310,6 +314,9 @@ + { "kexalgorithms", oKexAlgorithms }, + { "ipqos", oIPQoS }, + { "requesttty", oRequestTTY }, ++ { "noneenabled", oNoneEnabled }, ++ { "noneswitch", oNoneSwitch }, ++ { "disablemtaes", oDisableMTAES }, + { "proxyusefdpass", oProxyUseFdpass }, + { "canonicaldomains", oCanonicalDomains }, + { "canonicalizefallbacklocal", oCanonicalizeFallbackLocal }, +@@ -326,6 +333,11 @@ + { "ignoreunknown", oIgnoreUnknown }, + { "proxyjump", oProxyJump }, + ++ { "tcprcvbufpoll", oTcpRcvBufPoll }, ++ { "tcprcvbuf", oTcpRcvBuf }, ++ { "hpndisabled", oHPNDisabled }, ++ { "hpnbuffersize", oHPNBufferSize }, ++ + { NULL, oBadOption } + }; + +@@ -1047,6 +1059,42 @@ + intptr = &options->check_host_ip; + goto parse_flag; + ++ case oHPNDisabled: ++ intptr = &options->hpn_disabled; ++ goto parse_flag; ++ ++ case oHPNBufferSize: ++ intptr = &options->hpn_buffer_size; ++ goto parse_int; ++ ++ case oTcpRcvBufPoll: ++ intptr = &options->tcp_rcv_buf_poll; ++ goto parse_flag; ++ ++ case oNoneEnabled: ++ intptr = &options->none_enabled; ++ goto parse_flag; ++ ++ case oDisableMTAES: ++ intptr = &options->disable_multithreaded; ++ goto parse_flag; ++ ++ /* ++ * We check to see if the command comes from the command ++ * line or not. If it does then enable it otherwise fail. ++ * NONE should never be a default configuration. ++ */ ++ case oNoneSwitch: ++ if (strcmp(filename, "command-line") == 0) { ++ intptr = &options->none_switch; ++ goto parse_flag; ++ } else { ++ error("NoneSwitch is found in %.200s.\nYou may only use this configuration option from the command line", filename); ++ error("Continuing..."); ++ debug("NoneSwitch directive found in %.200s.", filename); ++ return 0; ++ } ++ + case oVerifyHostKeyDNS: + intptr = &options->verify_host_key_dns; + multistate_ptr = multistate_yesnoask; +@@ -1242,6 +1290,10 @@ + *intptr = value; + break; + ++ case oTcpRcvBuf: ++ intptr = &options->tcp_rcv_buf; ++ goto parse_int; ++ + case oCiphers: + arg = strdelim(&s); + if (!arg || *arg == '\0') +@@ -1984,6 +2036,13 @@ + options->ip_qos_interactive = -1; + options->ip_qos_bulk = -1; + options->request_tty = -1; ++ options->none_switch = -1; ++ options->none_enabled = -1; ++ options->disable_multithreaded = -1; ++ options->hpn_disabled = -1; ++ options->hpn_buffer_size = -1; ++ options->tcp_rcv_buf_poll = -1; ++ options->tcp_rcv_buf = -1; + options->proxy_use_fdpass = -1; + options->ignored_unknown = NULL; + options->num_canonical_domains = 0; +@@ -2141,6 +2200,32 @@ + options->server_alive_interval = 0; + if (options->server_alive_count_max == -1) + options->server_alive_count_max = 3; ++ if (options->hpn_disabled == -1) ++ options->hpn_disabled = 0; ++ if (options->hpn_buffer_size > -1) { ++ /* if a user tries to set the size to 0 set it to 1KB */ ++ if (options->hpn_buffer_size == 0) ++ options->hpn_buffer_size = 1; ++ /* limit the buffer to SSHBUF_SIZE_MAX (currently 256MB) */ ++ if (options->hpn_buffer_size > (SSHBUF_SIZE_MAX / 1024)) { ++ options->hpn_buffer_size = SSHBUF_SIZE_MAX; ++ debug("User requested buffer larger than 256MB. Request reverted to 256MB"); ++ } else ++ options->hpn_buffer_size *= 1024; ++ debug("hpn_buffer_size set to %d", options->hpn_buffer_size); ++ } ++ if (options->tcp_rcv_buf == 0) ++ options->tcp_rcv_buf = 1; ++ if (options->tcp_rcv_buf > -1) ++ options->tcp_rcv_buf *=1024; ++ if (options->tcp_rcv_buf_poll == -1) ++ options->tcp_rcv_buf_poll = 1; ++ if (options->none_switch == -1) ++ options->none_switch = 0; ++ if (options->none_enabled == -1) ++ options->none_enabled = 0; ++ if (options->disable_multithreaded == -1) ++ options->disable_multithreaded = 0; + if (options->control_master == -1) + options->control_master = 0; + if (options->control_persist == -1) { +diff -Nur openssh-8.0p1.orig/readconf.h openssh-8.0p1/readconf.h +--- openssh-8.0p1.orig/readconf.h 2024-07-09 15:12:37.196394466 +0200 ++++ openssh-8.0p1/readconf.h 2024-07-09 23:35:37.786207345 +0200 +@@ -56,6 +56,10 @@ + int strict_host_key_checking; /* Strict host key checking. */ + int compression; /* Compress packets in both directions. */ + int tcp_keep_alive; /* Set SO_KEEPALIVE. */ ++ int tcp_rcv_buf; /* user switch to set tcp recv buffer */ ++ int tcp_rcv_buf_poll; /* Option to poll recv buf every window transfer */ ++ int hpn_disabled; /* Switch to disable HPN buffer management */ ++ int hpn_buffer_size; /* User definable size for HPN buffer window */ + 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. */ +@@ -121,6 +125,11 @@ + int enable_ssh_keysign; + int64_t rekey_limit; + int rekey_interval; ++ ++ int none_switch; /* Use none cipher */ ++ int none_enabled; /* Allow none to be used */ ++ int disable_multithreaded; /*disable multithreaded aes-ctr*/ ++ + int no_host_authentication_for_localhost; + int identities_only; + int server_alive_interval; +diff -Nur openssh-8.0p1.orig/sandbox-seccomp-filter.c openssh-8.0p1/sandbox-seccomp-filter.c +--- openssh-8.0p1.orig/sandbox-seccomp-filter.c 2024-07-09 15:12:37.137394291 +0200 ++++ openssh-8.0p1/sandbox-seccomp-filter.c 2024-07-09 15:13:50.331612092 +0200 +@@ -181,6 +181,9 @@ + #ifdef __NR_geteuid32 + SC_ALLOW(__NR_geteuid32), + #endif ++#ifdef __NR_getpeername /* not defined on archs that go via socketcall(2) */ ++ SC_ALLOW(__NR_getpeername), ++#endif + #ifdef __NR_getpgid + SC_ALLOW(__NR_getpgid), + #endif +diff -Nur openssh-8.0p1.orig/scp.c openssh-8.0p1/scp.c +--- openssh-8.0p1.orig/scp.c 2024-07-09 15:12:37.160394359 +0200 ++++ openssh-8.0p1/scp.c 2024-07-09 15:13:50.332612095 +0200 +@@ -1079,7 +1079,7 @@ + off_t i, statbytes; + size_t amt, nr; + int fd = -1, haderr, indx; +- char *last, *name, buf[2048], encname[PATH_MAX]; ++ char *last, *name, buf[16384], encname[PATH_MAX]; + int len; + + for (indx = 0; indx < argc; ++indx) { +@@ -1252,7 +1252,7 @@ + off_t size, statbytes; + unsigned long long ull; + int setimes, targisdir, wrerrno = 0; +- char ch, *cp, *np, *targ, *why, *vect[1], buf[2048], visbuf[2048]; ++ char ch, *cp, *np, *targ, *why, *vect[1], buf[16384], visbuf[16384]; + char **patterns = NULL; + size_t n, npatterns = 0; + struct timeval tv[2]; +diff -Nur openssh-8.0p1.orig/servconf.c openssh-8.0p1/servconf.c +--- openssh-8.0p1.orig/servconf.c 2024-07-09 15:12:37.197394469 +0200 ++++ openssh-8.0p1/servconf.c 2024-07-09 15:35:44.556537651 +0200 +@@ -193,6 +193,11 @@ + options->authorized_principals_file = NULL; + options->authorized_principals_command = NULL; + options->authorized_principals_command_user = NULL; ++ options->tcp_rcv_buf_poll = -1; ++ options->hpn_disabled = -1; ++ options->hpn_buffer_size = -1; ++ options->none_enabled = -1; ++ options->disable_multithreaded = -1; + options->ip_qos_interactive = -1; + options->ip_qos_bulk = -1; + options->version_addendum = NULL; +@@ -293,6 +298,10 @@ + fill_default_server_options(ServerOptions *options) + { + u_int i; ++ /* needed for hpn socket tests */ ++ int sock; ++ int socksize; ++ int socksizelen = sizeof(int); + + /* Portable-specific options */ + if (options->use_pam == -1) +@@ -451,6 +460,45 @@ + } + if (options->permit_tun == -1) + options->permit_tun = SSH_TUNMODE_NO; ++ if (options->none_enabled == -1) ++ options->none_enabled = 0; ++ if (options->disable_multithreaded == -1) ++ options->disable_multithreaded = 0; ++ if (options->hpn_disabled == -1) ++ options->hpn_disabled = 0; ++ ++ if (options->hpn_buffer_size == -1) { ++ /* option not explicitly set. Now we have to figure out */ ++ /* what value to use */ ++ if (options->hpn_disabled == 1) { ++ options->hpn_buffer_size = CHAN_SES_WINDOW_DEFAULT; ++ } else { ++ /* get the current RCV size and set it to that */ ++ /*create a socket but don't connect it */ ++ /* we use that the get the rcv socket size */ ++ sock = socket(AF_INET, SOCK_STREAM, 0); ++ getsockopt(sock, SOL_SOCKET, SO_RCVBUF, ++ &socksize, &socksizelen); ++ close(sock); ++ options->hpn_buffer_size = socksize; ++ debug("HPN Buffer Size: %d", options->hpn_buffer_size); ++ } ++ } else { ++ /* we have to do this in case the user sets both values in a contradictory */ ++ /* manner. hpn_disabled overrrides hpn_buffer_size*/ ++ if (options->hpn_disabled <= 0) { ++ if (options->hpn_buffer_size == 0) ++ options->hpn_buffer_size = 1; ++ /* limit the maximum buffer to SSHBUF_SIZE_MAX (currently 256MB) */ ++ if (options->hpn_buffer_size > (SSHBUF_SIZE_MAX / 1024)) { ++ options->hpn_buffer_size = SSHBUF_SIZE_MAX; ++ } else { ++ options->hpn_buffer_size *= 1024; ++ } ++ } else ++ options->hpn_buffer_size = CHAN_TCP_WINDOW_DEFAULT; ++ } ++ + if (options->ip_qos_interactive == -1) + options->ip_qos_interactive = IPTOS_DSCP_AF21; + if (options->ip_qos_bulk == -1) +@@ -529,6 +577,9 @@ + sPasswordAuthentication, sKbdInteractiveAuthentication, + sListenAddress, sAddressFamily, + sPrintMotd, sPrintLastLog, sIgnoreRhosts, ++ sNoneEnabled, ++ sDisableMTAES, ++ sTcpRcvBufPoll, sHPNDisabled, sHPNBufferSize, + sX11Forwarding, sX11DisplayOffset, sX11MaxDisplays, sX11UseLocalhost, + sPermitTTY, sStrictModes, sEmptyPasswd, sTCPKeepAlive, + sPermitUserEnvironment, sAllowTcpForwarding, sCompression, +@@ -722,6 +773,11 @@ + { "revokedkeys", sRevokedKeys, SSHCFG_ALL }, + { "trustedusercakeys", sTrustedUserCAKeys, SSHCFG_ALL }, + { "authorizedprincipalsfile", sAuthorizedPrincipalsFile, SSHCFG_ALL }, ++ { "hpndisabled", sHPNDisabled, SSHCFG_ALL }, ++ { "hpnbuffersize", sHPNBufferSize, SSHCFG_ALL }, ++ { "tcprcvbufpoll", sTcpRcvBufPoll, SSHCFG_ALL }, ++ { "noneenabled", sNoneEnabled, SSHCFG_ALL }, ++ { "disableMTAES", sDisableMTAES, SSHCFG_ALL }, + { "kexalgorithms", sKexAlgorithms, SSHCFG_GLOBAL }, + { "include", sInclude, SSHCFG_ALL }, + { "ipqos", sIPQoS, SSHCFG_ALL }, +@@ -783,6 +839,7 @@ + + for (i = 0; keywords[i].name; i++) + if (strcasecmp(cp, keywords[i].name) == 0) { ++ debug("Config token is %s", keywords[i].name); + *flags = keywords[i].flags; + return keywords[i].opcode; + } +@@ -1516,10 +1573,31 @@ + multistate_ptr = multistate_flag; + goto parse_multistate; + ++ ++ case sTcpRcvBufPoll: ++ intptr = &options->tcp_rcv_buf_poll; ++ goto parse_flag; ++ ++ case sHPNDisabled: ++ intptr = &options->hpn_disabled; ++ goto parse_flag; ++ ++ case sHPNBufferSize: ++ intptr = &options->hpn_buffer_size; ++ goto parse_int; ++ + case sIgnoreUserKnownHosts: + intptr = &options->ignore_user_known_hosts; + goto parse_flag; + ++ case sNoneEnabled: ++ intptr = &options->none_enabled; ++ goto parse_flag; ++ ++ case sDisableMTAES: ++ intptr = &options->disable_multithreaded; ++ goto parse_flag; ++ + case sHostbasedAuthentication: + intptr = &options->hostbased_authentication; + goto parse_flag; +diff -Nur openssh-8.0p1.orig/servconf.h openssh-8.0p1/servconf.h +--- openssh-8.0p1.orig/servconf.h 2024-07-09 15:12:37.197394469 +0200 ++++ openssh-8.0p1/servconf.h 2024-07-09 20:20:31.751940828 +0200 +@@ -199,6 +199,14 @@ + int use_pam; /* Enable auth via PAM */ + int permit_pam_user_change; /* Allow PAM to change user name */ + ++ int tcp_rcv_buf_poll; /* poll tcp rcv window in autotuning kernels */ ++ int hpn_disabled; /* disable hpn functionality. false by default */ ++ int hpn_buffer_size; /* set the hpn buffer size - default 3MB */ ++ ++ int none_enabled; /* Enable NONE cipher switch */ ++ ++ int disable_multithreaded; /* disable multithreaded aes-ctr cipher */ ++ + int permit_tun; + + char **permitted_opens; /* May also be one of PERMITOPEN_* */ +diff -Nur openssh-8.0p1.orig/serverloop.c openssh-8.0p1/serverloop.c +--- openssh-8.0p1.orig/serverloop.c 2024-07-09 15:12:37.146394317 +0200 ++++ openssh-8.0p1/serverloop.c 2024-07-09 15:13:50.335612104 +0200 +@@ -344,6 +344,7 @@ + != 0) + fatal("%s: ssh_packet_process_incoming: %s", + __func__, ssh_err(r)); ++ ssh->fdout_bytes += len; + } + } + return 0; +@@ -402,6 +403,7 @@ + u_int64_t rekey_timeout_ms = 0; + + debug("Entering interactive session for SSH2."); ++ ssh->start_time = monotime_double(); + + signal(SIGCHLD, sigchld_handler); + child_terminated = 0; +@@ -440,6 +442,7 @@ + + if (received_sigterm) { + logit("Exiting on signal %d", (int)received_sigterm); ++ sshpkt_final_log_entry(ssh); + /* Clean up sessions, utmp, etc. */ + cleanup_exit(255); + } +@@ -459,6 +462,9 @@ + /* free all channels, no more reads and writes */ + channel_free_all(ssh); + ++ /* final entry must come after channels close -cjr */ ++ sshpkt_final_log_entry(ssh); ++ + /* free remaining sessions, e.g. remove wtmp entries */ + session_destroy_all(ssh, NULL); + } +@@ -609,7 +615,8 @@ + 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); ++ options.hpn_disabled ? CHAN_TCP_WINDOW_DEFAULT : options.hpn_buffer_size, ++ CHAN_TCP_PACKET_DEFAULT, 0, "tun", 1); + c->datagram = 1; + #if defined(SSH_TUN_FILTER) + if (mode == SSH_TUNMODE_POINTOPOINT) +@@ -660,6 +667,8 @@ + c = channel_new(ssh, "session", SSH_CHANNEL_LARVAL, + -1, -1, -1, /*window size*/0, CHAN_SES_PACKET_DEFAULT, + 0, "server-session", 1); ++ if ((options.tcp_rcv_buf_poll) && (!options.hpn_disabled)) ++ c->dynamic_window = 1; + if (session_open(the_authctxt, c->self) != 1) { + debug("session open failed, free channel %d", c->self); + channel_free(ssh, c); +diff -Nur openssh-8.0p1.orig/session.c openssh-8.0p1/session.c +--- openssh-8.0p1.orig/session.c 2024-07-09 15:12:37.132394275 +0200 ++++ openssh-8.0p1/session.c 2024-07-09 15:13:50.336612107 +0200 +@@ -228,6 +228,7 @@ + goto authsock_err; + + /* Allocate a channel for the authentication agent socket. */ ++ /* this shouldn't matter if its hpn or not - cjr */ + nc = channel_new(ssh, "auth socket", + SSH_CHANNEL_AUTH_SOCKET, sock, sock, -1, + CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, +@@ -2363,7 +2364,8 @@ + channel_set_fds(ssh, s->chanid, + fdout, fdin, fderr, + ignore_fderr ? CHAN_EXTENDED_IGNORE : CHAN_EXTENDED_READ, +- 1, is_tty, CHAN_SES_WINDOW_DEFAULT); ++ 1, is_tty, ++ options.hpn_disabled ? CHAN_SES_WINDOW_DEFAULT : options.hpn_buffer_size); + } + + /* +diff -Nur openssh-8.0p1.orig/sftp.1 openssh-8.0p1/sftp.1 +--- openssh-8.0p1.orig/sftp.1 2019-04-18 00:52:57.000000000 +0200 ++++ openssh-8.0p1/sftp.1 2024-07-09 15:13:50.336612107 +0200 +@@ -286,7 +286,8 @@ + Specify how many requests may be outstanding at any one time. + Increasing this may slightly improve file transfer speed + but will increase memory usage. +-The default is 64 outstanding requests. ++The default is 256 outstanding requests providing for 8MB ++of outstanding data with a 32KB buffer. + .It Fl r + Recursively copy entire directories when uploading and downloading. + Note that +diff -Nur openssh-8.0p1.orig/sftp.c openssh-8.0p1/sftp.c +--- openssh-8.0p1.orig/sftp.c 2024-07-09 15:12:37.115394225 +0200 ++++ openssh-8.0p1/sftp.c 2024-07-09 15:13:50.337612110 +0200 +@@ -72,7 +72,7 @@ + #include "sftp-client.h" + + #define DEFAULT_COPY_BUFLEN 32768 /* Size of buffer for up/download */ +-#define DEFAULT_NUM_REQUESTS 64 /* # concurrent outstanding requests */ ++#define DEFAULT_NUM_REQUESTS 256 /* # concurrent outstanding requests */ + + /* File to read commands from */ + FILE* infile; +diff -Nur openssh-8.0p1.orig/sshbuf.h openssh-8.0p1/sshbuf.h +--- openssh-8.0p1.orig/sshbuf.h 2019-04-18 00:52:57.000000000 +0200 ++++ openssh-8.0p1/sshbuf.h 2024-07-09 15:13:50.337612110 +0200 +@@ -28,7 +28,7 @@ + # endif /* OPENSSL_HAS_ECC */ + #endif /* WITH_OPENSSL */ + +-#define SSHBUF_SIZE_MAX 0x8000000 /* Hard maximum size */ ++#define SSHBUF_SIZE_MAX 0xF000000 /* Hard maximum size 256MB */ + #define SSHBUF_REFS_MAX 0x100000 /* Max child buffers */ + #define SSHBUF_MAX_BIGNUM (16384 / 8) /* Max bignum *bytes* */ + #define SSHBUF_MAX_ECPOINT ((528 * 2 / 8) + 1) /* Max EC point *bytes* */ +diff -Nur openssh-8.0p1.orig/ssh.c openssh-8.0p1/ssh.c +--- openssh-8.0p1.orig/ssh.c 2024-07-09 15:12:37.198394472 +0200 ++++ openssh-8.0p1/ssh.c 2024-07-09 15:13:50.338612113 +0200 +@@ -1035,6 +1035,10 @@ + break; + case 'T': + options.request_tty = REQUEST_TTY_NO; ++ /* ensure that the user doesn't try to backdoor a */ ++ /* null cipher switch on an interactive session */ ++ /* so explicitly disable it no matter what */ ++ options.none_switch=0; + break; + case 'o': + line = xstrdup(optarg); +@@ -1681,6 +1685,8 @@ + setproctitle("%s [mux]", options.control_path); + } + ++extern const EVP_CIPHER *evp_aes_ctr_mt(void); ++ + /* Do fork() after authentication. Used by "ssh -f" */ + static void + fork_postauth(void) +@@ -1925,6 +1931,78 @@ + NULL, fileno(stdin), command, environ); + } + ++static void ++hpn_options_init(void) ++{ ++ /* ++ * We need to check to see if what they want to do about buffer ++ * sizes here. In a hpn to nonhpn connection we want to limit ++ * the window size to something reasonable in case the far side ++ * has the large window bug. In hpn to hpn connection we want to ++ * use the max window size but allow the user to override it ++ * lastly if they disabled hpn then use the ssh std window size. ++ * ++ * So why don't we just do a getsockopt() here and set the ++ * ssh window to that? In the case of a autotuning receive ++ * window the window would get stuck at the initial buffer ++ * size generally less than 96k. Therefore we need to set the ++ * maximum ssh window size to the maximum hpn buffer size ++ * unless the user has specifically set the tcprcvbufpoll ++ * to no. In which case we *can* just set the window to the ++ * minimum of the hpn buffer size and tcp receive buffer size. ++ */ ++ ++ if (tty_flag) ++ options.hpn_buffer_size = CHAN_SES_WINDOW_DEFAULT; ++ else ++ options.hpn_buffer_size = 2 * 1024 * 1024; ++ ++ if (datafellows & SSH_BUG_LARGEWINDOW) { ++ debug("HPN to Non-HPN Connection"); ++ } else { ++ int sock, socksize; ++ socklen_t socksizelen; ++ if (options.tcp_rcv_buf_poll <= 0) { ++ sock = socket(AF_INET, SOCK_STREAM, 0); ++ socksizelen = sizeof(socksize); ++ getsockopt(sock, SOL_SOCKET, SO_RCVBUF, ++ &socksize, &socksizelen); ++ close(sock); ++ debug("socksize %d", socksize); ++ options.hpn_buffer_size = socksize; ++ debug("HPNBufferSize set to TCP RWIN: %d", options.hpn_buffer_size); ++ } else { ++ if (options.tcp_rcv_buf > 0) { ++ /* ++ * Create a socket but don't connect it: ++ * we use that the get the rcv socket size ++ */ ++ sock = socket(AF_INET, SOCK_STREAM, 0); ++ /* ++ * If they are using the tcp_rcv_buf option, ++ * attempt to set the buffer size to that. ++ */ ++ if (options.tcp_rcv_buf) { ++ socksizelen = sizeof(options.tcp_rcv_buf); ++ setsockopt(sock, SOL_SOCKET, SO_RCVBUF, ++ &options.tcp_rcv_buf, socksizelen); ++ } ++ socksizelen = sizeof(socksize); ++ getsockopt(sock, SOL_SOCKET, SO_RCVBUF, ++ &socksize, &socksizelen); ++ close(sock); ++ debug("socksize %d", socksize); ++ options.hpn_buffer_size = socksize; ++ debug("HPNBufferSize set to user TCPRcvBuf: %d", options.hpn_buffer_size); ++ } ++ } ++ } ++ ++ debug("Final hpn_buffer_size = %d", options.hpn_buffer_size); ++ ++ channel_set_hpn(options.hpn_disabled, options.hpn_buffer_size); ++} ++ + /* open new channel for a session */ + static int + ssh_session2_open(struct ssh *ssh) +@@ -1943,9 +2021,11 @@ + if (in < 0 || out < 0 || err < 0) + fatal("dup() in/out/err failed"); + +- window = CHAN_SES_WINDOW_DEFAULT; ++ window = options.hpn_buffer_size; ++ + packetmax = CHAN_SES_PACKET_DEFAULT; + if (tty_flag) { ++ window = CHAN_SES_WINDOW_DEFAULT; + window >>= 1; + packetmax >>= 1; + } +@@ -1954,6 +2034,10 @@ + window, packetmax, CHAN_EXTENDED_WRITE, + "client-session", CHANNEL_NONBLOCK_STDIO); + ++ if ((options.tcp_rcv_buf_poll > 0) && (!options.hpn_disabled)) { ++ c->dynamic_window = 1; ++ debug("Enabled Dynamic Window Scaling"); ++ } + debug3("%s: channel_new: %d", __func__, c->self); + + channel_send_open(ssh, c->self); +@@ -1970,6 +2054,13 @@ + int r, devnull, id = -1; + char *cp, *tun_fwd_ifname = NULL; + ++ /* ++ * We need to initialize this early because the forwarding logic below ++ * might open channels that use the hpn buffer sizes. We can't send a ++ * window of -1 (the default) to the server as it breaks things. ++ */ ++ hpn_options_init(); ++ + /* XXX should be pre-session */ + if (!options.control_persist) + ssh_init_stdio_forwarding(ssh); +diff -Nur openssh-8.0p1.orig/sshconnect2.c openssh-8.0p1/sshconnect2.c +--- openssh-8.0p1.orig/sshconnect2.c 2024-07-09 15:12:37.199394475 +0200 ++++ openssh-8.0p1/sshconnect2.c 2024-07-09 20:21:33.938123264 +0200 +@@ -83,6 +83,13 @@ + extern Options options; + + /* ++ * tty_flag is set in ssh.c. Use this in ssh_userauth2: ++ * if it is set, then prevent the switch to the null cipher. ++ */ ++ ++extern int tty_flag; ++ ++/* + * SSH2 key exchange + */ + +@@ -202,6 +209,8 @@ + return ret; + } + ++static char *myproposal[PROPOSAL_MAX]; ++static const char *myproposal_default[PROPOSAL_MAX] = { KEX_CLIENT }; + void + ssh_kex2(struct ssh *ssh, char *host, struct sockaddr *hostaddr, u_short port) + { +@@ -214,6 +223,8 @@ + char *gss_host = NULL; + #endif + ++ memcpy(&myproposal, &myproposal_default, sizeof(myproposal)); ++ + xxx_host = host; + xxx_hostaddr = hostaddr; + +@@ -553,6 +564,47 @@ + + if (!authctxt.success) + fatal("Authentication failed."); ++ ++ /* ++ * If the user wants to use the none cipher, do it post authentication ++ * and only if the right conditions are met -- both of the NONE commands ++ * must be true and there must be no tty allocated. ++ */ ++ if ((options.none_switch == 1) && (options.none_enabled == 1)) { ++ if (!tty_flag) { /* no null on tty sessions */ ++ debug("Requesting none rekeying..."); ++ memcpy(&myproposal, &myproposal_default, sizeof(myproposal)); ++ myproposal[PROPOSAL_ENC_ALGS_STOC] = "none"; ++ myproposal[PROPOSAL_ENC_ALGS_CTOS] = "none"; ++ kex_prop2buf(ssh->kex->my, myproposal); ++ packet_request_rekeying(); ++ fprintf(stderr, "WARNING: ENABLED NONE CIPHER\n"); ++ } else { ++ /* requested NONE cipher when in a tty */ ++ debug("Cannot switch to NONE cipher with tty allocated"); ++ fprintf(stderr, "NONE cipher switch disabled when a TTY is allocated\n"); ++ } ++ } ++ ++#ifdef WITH_OPENSSL ++ if (options.disable_multithreaded == 0) { ++ /* if we are using aes-ctr there can be issues in either a fork or sandbox ++ * so the initial aes-ctr is defined to point to the original single process ++ * evp. After authentication we'll be past the fork and the sandboxed privsep ++ * so we repoint the define to the multithreaded evp. To start the threads we ++ * then force a rekey ++ */ ++ const void *cc = ssh_packet_get_send_context(ssh); ++ ++ /* only do this for the ctr cipher. otherwise gcm mode breaks. Don't know why though */ ++ if (strstr(cipher_ctx_name(cc), "ctr")) { ++ debug("Single to Multithread CTR cipher swap - client request"); ++ cipher_reset_multithreaded(); ++ packet_request_rekeying(); ++ } ++ } ++#endif ++ + debug("Authentication succeeded (%s).", authctxt.method->name); + } + +diff -Nur openssh-8.0p1.orig/sshconnect.c openssh-8.0p1/sshconnect.c +--- openssh-8.0p1.orig/sshconnect.c 2019-04-18 00:52:57.000000000 +0200 ++++ openssh-8.0p1/sshconnect.c 2024-07-09 20:22:42.338323994 +0200 +@@ -356,6 +356,30 @@ + #endif + + /* ++ * Set TCP receive buffer if requested. ++ * Note: tuning needs to happen after the socket is ++ * created but before the connection happens ++ * so winscale is negotiated properly -cjr ++ */ ++static void ++ssh_set_socket_recvbuf(int sock) ++{ ++ void *buf = (void *)&options.tcp_rcv_buf; ++ int sz = sizeof(options.tcp_rcv_buf); ++ int socksize; ++ int socksizelen = sizeof(int); ++ ++ debug("setsockopt Attempting to set SO_RCVBUF to %d", options.tcp_rcv_buf); ++ if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, buf, sz) >= 0) { ++ getsockopt(sock, SOL_SOCKET, SO_RCVBUF, &socksize, &socksizelen); ++ debug("setsockopt SO_RCVBUF: %.100s %d", strerror(errno), socksize); ++ } ++ else ++ error("Couldn't set socket receive buffer to %d: %.100s", ++ options.tcp_rcv_buf, strerror(errno)); ++} ++ ++/* + * Creates a socket for use as the ssh connection. + */ + static int +@@ -377,6 +401,9 @@ + } + fcntl(sock, F_SETFD, FD_CLOEXEC); + ++ if (options.tcp_rcv_buf > 0) ++ ssh_set_socket_recvbuf(sock); ++ + /* Bind the socket to an alternative local IP address */ + if (options.bind_address == NULL && options.bind_interface == NULL) + return sock; +diff -Nur openssh-8.0p1.orig/sshd.c openssh-8.0p1/sshd.c +--- openssh-8.0p1.orig/sshd.c 2024-07-09 15:12:37.200394478 +0200 ++++ openssh-8.0p1/sshd.c 2024-07-09 20:24:22.566618167 +0200 +@@ -1058,6 +1058,8 @@ + int ret, listen_sock; + struct addrinfo *ai; + char ntop[NI_MAXHOST], strport[NI_MAXSERV]; ++ int socksize; ++ int socksizelen = sizeof(int); + + for (ai = la->addrs; ai; ai = ai->ai_next) { + if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) +@@ -1103,6 +1105,11 @@ + + debug("Bind to port %s on %s.", strport, ntop); + ++ getsockopt(listen_sock, SOL_SOCKET, SO_RCVBUF, ++ &socksize, &socksizelen); ++ debug("Server TCP RWIN socket size: %d", socksize); ++ debug("HPN Buffer Size: %d", options.hpn_buffer_size); ++ + /* Bind the socket to the desired port. */ + if (bind(listen_sock, ai->ai_addr, ai->ai_addrlen) < 0) { + error("Bind to port %s on %s failed: %.200s.", +@@ -1771,6 +1778,13 @@ + /* Fill in default values for those options not explicitly set. */ + fill_default_server_options(&options); + ++ if (options.none_enabled == 1) { ++ char *old_ciphers = options.ciphers; ++ ++ xasprintf(&options.ciphers, "%s,none", old_ciphers); ++ free(old_ciphers); ++ } ++ + /* challenge-response is implemented via keyboard interactive */ + if (options.challenge_response_authentication) + options.kbd_interactive_authentication = 1; +@@ -2208,6 +2222,9 @@ + rdomain == NULL ? "" : "\""); + free(laddr); + ++ /* set the HPN options for the child */ ++ channel_set_hpn(options.hpn_disabled, options.hpn_buffer_size); ++ + /* + * We don't want to listen forever unless the other side + * successfully authenticates itself. So we set up an alarm which is +@@ -2319,6 +2336,25 @@ + /* Try to send all our hostkeys to the client */ + notify_hostkeys(ssh); + ++#ifdef WITH_OPENSSL ++ if (options.disable_multithreaded == 0) { ++ /* if we are using aes-ctr there can be issues in either a fork or sandbox ++ * so the initial aes-ctr is defined to point ot the original single process ++ * evp. After authentication we'll be past the fork and the sandboxed privsep ++ * so we repoint the define to the multithreaded evp. To start the threads we ++ * then force a rekey ++ */ ++ const void *cc = ssh_packet_get_send_context(the_active_state); ++ ++ /* only rekey if necessary. If we don't do this gcm mode cipher breaks */ ++ if (strstr(cipher_ctx_name(cc), "ctr")) { ++ debug("Single to Multithreaded CTR cipher swap - server request"); ++ cipher_reset_multithreaded(); ++ packet_request_rekeying(); ++ } ++ } ++#endif ++ + /* Start session. */ + do_authenticated(ssh, authctxt); + +@@ -2392,6 +2428,9 @@ + char *cp; + int r; + ++ if (options.none_enabled == 1) ++ debug("WARNING: None cipher enabled"); ++ + if ((cp = kex_names_cat(options.kex_algorithms, + "kex-strict-s-v00@openssh.com")) == NULL) + fatal("kex_names_cat"); +diff -Nur openssh-8.0p1.orig/sshd_config openssh-8.0p1/sshd_config +--- openssh-8.0p1.orig/sshd_config 2024-07-09 15:12:37.200394478 +0200 ++++ openssh-8.0p1/sshd_config 2024-07-09 15:13:50.341612123 +0200 +@@ -143,6 +143,19 @@ + # override default of no subsystems + Subsystem sftp /usr/libexec/sftp-server + ++# the following are HPN related configuration options ++# tcp receive buffer polling. disable in non autotuning kernels ++#TcpRcvBufPoll yes ++ ++# disable hpn performance boosts ++#HPNDisabled no ++ ++# buffer size for hpn to non-hpn connections ++#HPNBufferSize 2048 ++ ++# allow the use of the none cipher ++#NoneEnabled no ++ + # Example of overriding settings on a per-user basis + #Match User anoncvs + # X11Forwarding no +diff -Nur openssh-8.0p1.orig/version.h openssh-8.0p1/version.h +--- openssh-8.0p1.orig/version.h 2024-07-09 15:12:37.201394481 +0200 ++++ openssh-8.0p1/version.h 2024-07-09 20:24:59.213725731 +0200 +@@ -16,5 +16,6 @@ + + #define SSH_PORTABLE "p1" + #define GSI_PORTABLE "c-GSI" ++#define SSH_HPN "-hpn14v19" + #define SSH_RELEASE SSH_VERSION SSH_PORTABLE GSI_PORTABLE \ +- GSI_VERSION KRB5_VERSION ++ GSI_VERSION KRB5_VERSION SSH_HPN diff --git a/openssh-8.0p1-ipv6-process.patch b/openssh-8.0p1-ipv6-process.patch new file mode 100644 index 0000000..cb76938 --- /dev/null +++ b/openssh-8.0p1-ipv6-process.patch @@ -0,0 +1,27 @@ +diff --git a/sftp.c b/sftp.c +index 04881c83..03c7a5c7 100644 +--- a/sftp.c ++++ b/sftp.c +@@ -2527,12 +2527,17 @@ main(int argc, char **argv) + port = tmp; + break; + default: ++ /* Try with user, host and path. */ + if (parse_user_host_path(*argv, &user, &host, +- &file1) == -1) { +- /* Treat as a plain hostname. */ +- host = xstrdup(*argv); +- host = cleanhostname(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); diff --git a/openssh-8.0p1-keygen-sha2.patch b/openssh-8.0p1-keygen-sha2.patch new file mode 100644 index 0000000..31927fa --- /dev/null +++ b/openssh-8.0p1-keygen-sha2.patch @@ -0,0 +1,107 @@ +From 4a41d245d6b13bd3882c8dc058dbd2e2b39a9f67 Mon Sep 17 00:00:00 2001 +From: "djm@openbsd.org" +Date: Fri, 24 Jan 2020 00:27:04 +0000 +Subject: [PATCH] upstream: when signing a certificate with an RSA key, default + to + +a safe signature algorithm (rsa-sha-512) if not is explicitly specified by +the user; ok markus@ + +OpenBSD-Commit-ID: e05f638f0be6c0266e1d3d799716b461011e83a9 +--- + ssh-keygen.c | 14 +++++++++----- + 1 file changed, 9 insertions(+), 5 deletions(-) + +diff --git a/ssh-keygen.c b/ssh-keygen.c +index 564c3c481..f2192edb9 100644 +--- a/ssh-keygen.c ++++ b/ssh-keygen.c +@@ -1788,10 +1788,14 @@ do_ca_sign(struct passwd *pw, const char *ca_key_path, int prefer_agent, + } + free(tmp); + +- if (key_type_name != NULL && +- 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); ++ 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"; + } + + for (i = 0; i < argc; i++) { + +From 476e3551b2952ef73acc43d995e832539bf9bc4d Mon Sep 17 00:00:00 2001 +From: "djm@openbsd.org" +Date: Mon, 20 May 2019 00:20:35 +0000 +Subject: [PATCH] upstream: When signing certificates with an RSA key, default + to + +using the rsa-sha2-512 signature algorithm. Certificates signed by RSA keys +will therefore be incompatible with OpenSSH < 7.2 unless the default is +overridden. + +Document the ability of the ssh-keygen -t flag to override the +signature algorithm when signing certificates, and the new default. + +ok deraadt@ + +OpenBSD-Commit-ID: 400c9c15013978204c2cb80f294b03ae4cfc8b95 +--- + ssh-keygen.1 | 13 +++++++++++-- + sshkey.c | 9 ++++++++- + 2 files changed, 19 insertions(+), 3 deletions(-) + +diff --git a/ssh-keygen.1 b/ssh-keygen.1 +index f29774249..673bf6e2f 100644 +--- a/ssh-keygen.1 ++++ b/ssh-keygen.1 +@@ -35,7 +35,7 @@ + .\" (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: March 5 2019 $ ++.Dd $Mdocdate: May 20 2019 $ + .Dt SSH-KEYGEN 1 + .Os + .Sh NAME +@@ -577,6 +577,15 @@ The possible values are + .Dq ed25519 , + or + .Dq rsa . ++.Pp ++This flag may also be used to specify the desired signature type when ++signing certificates using a RSA CA key. ++The available RSA signature variants are ++.Dq ssh-rsa ++(SHA1 signatures, not recommended), ++.Dq rsa-sha2-256 ++.Dq rsa-sha2-512 ++(the default). + .It Fl U + When used in combination with + .Fl s , +diff --git a/sshkey.c b/sshkey.c +index 9849cb237..379a579cf 100644 +--- a/sshkey.c ++++ b/sshkey.c +@@ -2528,6 +2528,13 @@ sshkey_certify_custom(struct sshkey *k, struct sshkey *ca, const char *alg, + 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; + + diff --git a/openssh-8.0p1-keygen-strip-doseol.patch b/openssh-8.0p1-keygen-strip-doseol.patch new file mode 100644 index 0000000..3117a7a --- /dev/null +++ b/openssh-8.0p1-keygen-strip-doseol.patch @@ -0,0 +1,12 @@ +diff -up openssh-8.0p1/ssh-keygen.c.strip-doseol openssh-8.0p1/ssh-keygen.c +--- openssh-8.0p1/ssh-keygen.c.strip-doseol 2021-03-18 17:41:34.472404994 +0100 ++++ openssh-8.0p1/ssh-keygen.c 2021-03-18 17:41:55.255538761 +0100 +@@ -901,7 +901,7 @@ do_fingerprint(struct passwd *pw) + while (getline(&line, &linesize, f) != -1) { + lnum++; + cp = line; +- cp[strcspn(cp, "\n")] = '\0'; ++ cp[strcspn(cp, "\r\n")] = '\0'; + /* Trim leading space and comments */ + cp = line + strspn(line, " \t"); + if (*cp == '#' || *cp == '\0') diff --git a/openssh-8.0p1-keyscan-rsa-sha2.patch b/openssh-8.0p1-keyscan-rsa-sha2.patch new file mode 100644 index 0000000..954ece9 --- /dev/null +++ b/openssh-8.0p1-keyscan-rsa-sha2.patch @@ -0,0 +1,33 @@ +From 7250879c72d28275a53f2f220e49646c3e42ef18 Mon Sep 17 00:00:00 2001 +From: "djm@openbsd.org" +Date: Fri, 12 Jul 2019 04:08:39 +0000 +Subject: [PATCH] upstream: include SHA2-variant RSA key algorithms in KEX + proposal; + +allows ssh-keyscan to harvest keys from servers that disable olde SHA1 +ssh-rsa. bz#3029 from Jakub Jelen + +OpenBSD-Commit-ID: 9f95ebf76a150c2f727ca4780fb2599d50bbab7a +--- + ssh-keyscan.c | 9 +++++++-- + 1 file changed, 7 insertions(+), 2 deletions(-) + +diff --git a/ssh-keyscan.c b/ssh-keyscan.c +index d95ba1b37..d383b57b9 100644 +--- a/ssh-keyscan.c ++++ b/ssh-keyscan.c +@@ -233,7 +233,12 @@ keygrab_ssh2(con *c) + break; + case KT_RSA: + myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ? +- "ssh-rsa-cert-v01@openssh.com" : "ssh-rsa"; ++ "rsa-sha2-512-cert-v01@openssh.com," ++ "rsa-sha2-256-cert-v01@openssh.com," ++ "ssh-rsa-cert-v01@openssh.com" : ++ "rsa-sha2-512," ++ "rsa-sha2-256," ++ "ssh-rsa"; + break; + case KT_ED25519: + myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ? + diff --git a/openssh-8.0p1-openssl-evp.patch b/openssh-8.0p1-openssl-evp.patch new file mode 100644 index 0000000..ade0bbb --- /dev/null +++ b/openssh-8.0p1-openssl-evp.patch @@ -0,0 +1,720 @@ +From ed7ec0cdf577ffbb0b15145340cf51596ca3eb89 Mon Sep 17 00:00:00 2001 +From: Jakub Jelen +Date: Tue, 14 May 2019 10:45:45 +0200 +Subject: [PATCH] Use high-level OpenSSL API for signatures + +--- + digest-openssl.c | 16 ++++ + digest.h | 6 ++ + ssh-dss.c | 65 ++++++++++------ + ssh-ecdsa.c | 69 ++++++++++------- + ssh-rsa.c | 193 +++++++++-------------------------------------- + sshkey.c | 77 +++++++++++++++++++ + sshkey.h | 4 + + 7 files changed, 221 insertions(+), 209 deletions(-) + +diff --git a/digest-openssl.c b/digest-openssl.c +index da7ed72bc..6a21d8adb 100644 +--- a/digest-openssl.c ++++ b/digest-openssl.c +@@ -63,6 +63,22 @@ const struct ssh_digest digests[] = { + { -1, NULL, 0, NULL }, + }; + ++const EVP_MD * ++ssh_digest_to_md(int digest_type) ++{ ++ switch (digest_type) { ++ case SSH_DIGEST_SHA1: ++ return EVP_sha1(); ++ case SSH_DIGEST_SHA256: ++ return EVP_sha256(); ++ case SSH_DIGEST_SHA384: ++ return EVP_sha384(); ++ case SSH_DIGEST_SHA512: ++ return EVP_sha512(); ++ } ++ return NULL; ++} ++ + static const struct ssh_digest * + ssh_digest_by_alg(int alg) + { +diff --git a/digest.h b/digest.h +index 274574d0e..c7ceeb36f 100644 +--- a/digest.h ++++ b/digest.h +@@ -32,6 +32,12 @@ + struct sshbuf; + struct ssh_digest_ctx; + ++#ifdef WITH_OPENSSL ++#include ++/* Converts internal digest representation to the OpenSSL one */ ++const EVP_MD *ssh_digest_to_md(int digest_type); ++#endif ++ + /* Looks up a digest algorithm by name */ + int ssh_digest_alg_by_name(const char *name); + +diff --git a/ssh-dss.c b/ssh-dss.c +index a23c383dc..ea45e7275 100644 +--- a/ssh-dss.c ++++ b/ssh-dss.c +@@ -52,11 +52,15 @@ int + ssh_dss_sign(const struct sshkey *key, u_char **sigp, size_t *lenp, + const u_char *data, size_t datalen, u_int compat) + { ++ EVP_PKEY *pkey = NULL; + DSA_SIG *sig = NULL; + const BIGNUM *sig_r, *sig_s; +- u_char digest[SSH_DIGEST_MAX_LENGTH], sigblob[SIGBLOB_LEN]; +- size_t rlen, slen, len, dlen = ssh_digest_bytes(SSH_DIGEST_SHA1); ++ u_char sigblob[SIGBLOB_LEN]; ++ size_t rlen, slen; ++ int len; + struct sshbuf *b = NULL; ++ u_char *sigb = NULL; ++ const u_char *psig = NULL; + int ret = SSH_ERR_INVALID_ARGUMENT; + + if (lenp != NULL) +@@ -67,17 +71,24 @@ ssh_dss_sign(const struct sshkey *key, u_char **sigp, size_t *lenp, + if (key == NULL || key->dsa == NULL || + sshkey_type_plain(key->type) != KEY_DSA) + return SSH_ERR_INVALID_ARGUMENT; +- if (dlen == 0) +- return SSH_ERR_INTERNAL_ERROR; + +- if ((ret = ssh_digest_memory(SSH_DIGEST_SHA1, data, datalen, +- digest, sizeof(digest))) != 0) ++ if ((pkey = EVP_PKEY_new()) == NULL || ++ EVP_PKEY_set1_DSA(pkey, key->dsa) != 1) ++ return SSH_ERR_ALLOC_FAIL; ++ ret = sshkey_calculate_signature(pkey, SSH_DIGEST_SHA1, &sigb, &len, ++ data, datalen); ++ EVP_PKEY_free(pkey); ++ if (ret < 0) { + goto out; ++ } + +- if ((sig = DSA_do_sign(digest, dlen, key->dsa)) == NULL) { ++ psig = sigb; ++ if ((sig = d2i_DSA_SIG(NULL, &psig, len)) == NULL) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } ++ free(sigb); ++ sigb = NULL; + + DSA_SIG_get0(sig, &sig_r, &sig_s); + rlen = BN_num_bytes(sig_r); +@@ -110,7 +121,7 @@ ssh_dss_sign(const struct sshkey *key, u_char **sigp, size_t *lenp, + *lenp = len; + ret = 0; + out: +- explicit_bzero(digest, sizeof(digest)); ++ free(sigb); + DSA_SIG_free(sig); + sshbuf_free(b); + return ret; +@@ -121,20 +132,20 @@ ssh_dss_verify(const struct sshkey *key, + const u_char *signature, size_t signaturelen, + const u_char *data, size_t datalen, u_int compat) + { ++ EVP_PKEY *pkey = NULL; + DSA_SIG *sig = NULL; + BIGNUM *sig_r = NULL, *sig_s = NULL; +- u_char digest[SSH_DIGEST_MAX_LENGTH], *sigblob = NULL; +- size_t len, dlen = ssh_digest_bytes(SSH_DIGEST_SHA1); ++ u_char *sigblob = NULL; ++ size_t len, slen; + int ret = SSH_ERR_INTERNAL_ERROR; + struct sshbuf *b = NULL; + char *ktype = NULL; ++ u_char *sigb = NULL, *psig = NULL; + + if (key == NULL || key->dsa == NULL || + sshkey_type_plain(key->type) != KEY_DSA || + signature == NULL || signaturelen == 0) + return SSH_ERR_INVALID_ARGUMENT; +- if (dlen == 0) +- return SSH_ERR_INTERNAL_ERROR; + + /* fetch signature */ + if ((b = sshbuf_from(signature, signaturelen)) == NULL) +@@ -176,25 +187,31 @@ ssh_dss_verify(const struct sshkey *key, + } + sig_r = sig_s = NULL; /* transferred */ + +- /* sha1 the data */ +- if ((ret = ssh_digest_memory(SSH_DIGEST_SHA1, data, datalen, +- digest, sizeof(digest))) != 0) ++ if ((slen = i2d_DSA_SIG(sig, NULL)) == 0) { ++ ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; +- +- switch (DSA_do_verify(digest, dlen, sig, key->dsa)) { +- case 1: +- ret = 0; +- break; +- case 0: +- ret = SSH_ERR_SIGNATURE_INVALID; ++ } ++ if ((sigb = malloc(slen)) == NULL) { ++ ret = SSH_ERR_ALLOC_FAIL; + goto out; +- default: ++ } ++ psig = sigb; ++ if ((slen = i2d_DSA_SIG(sig, &psig)) == 0) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + ++ if ((pkey = EVP_PKEY_new()) == NULL || ++ EVP_PKEY_set1_DSA(pkey, key->dsa) != 1) { ++ ret = SSH_ERR_ALLOC_FAIL; ++ goto out; ++ } ++ ret = sshkey_verify_signature(pkey, SSH_DIGEST_SHA1, data, datalen, ++ sigb, slen); ++ EVP_PKEY_free(pkey); ++ + out: +- explicit_bzero(digest, sizeof(digest)); ++ free(sigb); + DSA_SIG_free(sig); + BN_clear_free(sig_r); + BN_clear_free(sig_s); +diff --git a/ssh-ecdsa.c b/ssh-ecdsa.c +index 599c7199d..b036796e8 100644 +--- a/ssh-ecdsa.c ++++ b/ssh-ecdsa.c +@@ -50,11 +50,13 @@ int + ssh_ecdsa_sign(const struct sshkey *key, u_char **sigp, size_t *lenp, + const u_char *data, size_t datalen, u_int compat) + { ++ EVP_PKEY *pkey = NULL; + ECDSA_SIG *sig = NULL; ++ unsigned char *sigb = NULL; ++ const unsigned char *psig; + const BIGNUM *sig_r, *sig_s; + int hash_alg; +- u_char digest[SSH_DIGEST_MAX_LENGTH]; +- size_t len, dlen; ++ int len; + struct sshbuf *b = NULL, *bb = NULL; + int ret = SSH_ERR_INTERNAL_ERROR; + +@@ -67,18 +69,24 @@ ssh_ecdsa_sign(const struct sshkey *key, u_char **sigp, size_t *lenp, + sshkey_type_plain(key->type) != KEY_ECDSA) + return SSH_ERR_INVALID_ARGUMENT; + +- if ((hash_alg = sshkey_ec_nid_to_hash_alg(key->ecdsa_nid)) == -1 || +- (dlen = ssh_digest_bytes(hash_alg)) == 0) ++ if ((hash_alg = sshkey_ec_nid_to_hash_alg(key->ecdsa_nid)) == -1) + return SSH_ERR_INTERNAL_ERROR; +- if ((ret = ssh_digest_memory(hash_alg, data, datalen, +- digest, sizeof(digest))) != 0) ++ ++ if ((pkey = EVP_PKEY_new()) == NULL || ++ EVP_PKEY_set1_EC_KEY(pkey, key->ecdsa) != 1) ++ return SSH_ERR_ALLOC_FAIL; ++ ret = sshkey_calculate_signature(pkey, hash_alg, &sigb, &len, data, ++ datalen); ++ EVP_PKEY_free(pkey); ++ if (ret < 0) { + goto out; ++ } + +- if ((sig = ECDSA_do_sign(digest, dlen, key->ecdsa)) == NULL) { ++ psig = sigb; ++ if ((sig = d2i_ECDSA_SIG(NULL, &psig, len)) == NULL) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } +- + if ((bb = sshbuf_new()) == NULL || (b = sshbuf_new()) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; +@@ -102,7 +110,7 @@ ssh_ecdsa_sign(const struct sshkey *key, u_char **sigp, size_t *lenp, + *lenp = len; + ret = 0; + out: +- explicit_bzero(digest, sizeof(digest)); ++ free(sigb); + sshbuf_free(b); + sshbuf_free(bb); + ECDSA_SIG_free(sig); +@@ -115,22 +123,21 @@ ssh_ecdsa_verify(const struct sshkey *key, + const u_char *signature, size_t signaturelen, + const u_char *data, size_t datalen, u_int compat) + { ++ EVP_PKEY *pkey = NULL; + ECDSA_SIG *sig = NULL; + BIGNUM *sig_r = NULL, *sig_s = NULL; +- int hash_alg; +- u_char digest[SSH_DIGEST_MAX_LENGTH]; +- size_t dlen; ++ int hash_alg, len; + int ret = SSH_ERR_INTERNAL_ERROR; + struct sshbuf *b = NULL, *sigbuf = NULL; + char *ktype = NULL; ++ unsigned char *sigb = NULL, *psig = NULL; + + if (key == NULL || key->ecdsa == NULL || + sshkey_type_plain(key->type) != KEY_ECDSA || + signature == NULL || signaturelen == 0) + return SSH_ERR_INVALID_ARGUMENT; + +- if ((hash_alg = sshkey_ec_nid_to_hash_alg(key->ecdsa_nid)) == -1 || +- (dlen = ssh_digest_bytes(hash_alg)) == 0) ++ if ((hash_alg = sshkey_ec_nid_to_hash_alg(key->ecdsa_nid)) == -1) + return SSH_ERR_INTERNAL_ERROR; + + /* fetch signature */ +@@ -166,28 +173,36 @@ ssh_ecdsa_verify(const struct sshkey *key, + } + sig_r = sig_s = NULL; /* transferred */ + +- if (sshbuf_len(sigbuf) != 0) { +- ret = SSH_ERR_UNEXPECTED_TRAILING_DATA; ++ /* Figure out the length */ ++ if ((len = i2d_ECDSA_SIG(sig, NULL)) == 0) { ++ ret = SSH_ERR_LIBCRYPTO_ERROR; ++ goto out; ++ } ++ if ((sigb = malloc(len)) == NULL) { ++ ret = SSH_ERR_ALLOC_FAIL; + goto out; + } +- if ((ret = ssh_digest_memory(hash_alg, data, datalen, +- digest, sizeof(digest))) != 0) ++ psig = sigb; ++ if ((len = i2d_ECDSA_SIG(sig, &psig)) == 0) { ++ ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; ++ } + +- switch (ECDSA_do_verify(digest, dlen, sig, key->ecdsa)) { +- case 1: +- ret = 0; +- break; +- case 0: +- ret = SSH_ERR_SIGNATURE_INVALID; ++ if (sshbuf_len(sigbuf) != 0) { ++ ret = SSH_ERR_UNEXPECTED_TRAILING_DATA; + goto out; +- default: +- ret = SSH_ERR_LIBCRYPTO_ERROR; ++ } ++ ++ if ((pkey = EVP_PKEY_new()) == NULL || ++ EVP_PKEY_set1_EC_KEY(pkey, key->ecdsa) != 1) { ++ ret = SSH_ERR_ALLOC_FAIL; + goto out; + } ++ ret = sshkey_verify_signature(pkey, hash_alg, data, datalen, sigb, len); ++ EVP_PKEY_free(pkey); + + out: +- explicit_bzero(digest, sizeof(digest)); ++ free(sigb); + sshbuf_free(sigbuf); + sshbuf_free(b); + ECDSA_SIG_free(sig); +diff --git a/ssh-rsa.c b/ssh-rsa.c +index 9b14f9a9a..8ef3a6aca 100644 +--- a/ssh-rsa.c ++++ b/ssh-rsa.c +@@ -37,7 +37,7 @@ + + #include "openbsd-compat/openssl-compat.h" + +-static int openssh_RSA_verify(int, u_char *, size_t, u_char *, size_t, RSA *); ++static int openssh_RSA_verify(int, const u_char *, size_t, u_char *, size_t, EVP_PKEY *); + + static const char * + rsa_hash_alg_ident(int hash_alg) +@@ -90,21 +90,6 @@ rsa_hash_id_from_keyname(const char *alg) + return -1; + } + +-static int +-rsa_hash_alg_nid(int type) +-{ +- switch (type) { +- case SSH_DIGEST_SHA1: +- return NID_sha1; +- case SSH_DIGEST_SHA256: +- return NID_sha256; +- case SSH_DIGEST_SHA512: +- return NID_sha512; +- default: +- return -1; +- } +-} +- + int + ssh_rsa_complete_crt_parameters(struct sshkey *key, const BIGNUM *iqmp) + { +@@ -164,11 +149,10 @@ int + ssh_rsa_sign(const struct sshkey *key, u_char **sigp, size_t *lenp, + const u_char *data, size_t datalen, const char *alg_ident) + { +- const BIGNUM *rsa_n; +- u_char digest[SSH_DIGEST_MAX_LENGTH], *sig = NULL; +- size_t slen = 0; +- u_int dlen, len; +- int nid, hash_alg, ret = SSH_ERR_INTERNAL_ERROR; ++ EVP_PKEY *pkey = NULL; ++ u_char *sig = NULL; ++ int len, slen = 0; ++ int hash_alg, ret = SSH_ERR_INTERNAL_ERROR; + struct sshbuf *b = NULL; + + if (lenp != NULL) +@@ -180,33 +164,24 @@ ssh_rsa_sign(const struct sshkey *key, u_char **sigp, size_t *lenp, + hash_alg = SSH_DIGEST_SHA1; + else + hash_alg = rsa_hash_id_from_keyname(alg_ident); ++ + if (key == NULL || key->rsa == NULL || hash_alg == -1 || + sshkey_type_plain(key->type) != KEY_RSA) + return SSH_ERR_INVALID_ARGUMENT; +- RSA_get0_key(key->rsa, &rsa_n, NULL, NULL); +- if (BN_num_bits(rsa_n) < SSH_RSA_MINIMUM_MODULUS_SIZE) +- return SSH_ERR_KEY_LENGTH; + slen = RSA_size(key->rsa); +- if (slen <= 0 || slen > SSHBUF_MAX_BIGNUM) +- return SSH_ERR_INVALID_ARGUMENT; +- +- /* hash the data */ +- nid = rsa_hash_alg_nid(hash_alg); +- if ((dlen = ssh_digest_bytes(hash_alg)) == 0) +- return SSH_ERR_INTERNAL_ERROR; +- if ((ret = ssh_digest_memory(hash_alg, data, datalen, +- digest, sizeof(digest))) != 0) +- goto out; ++ if (RSA_bits(key->rsa) < SSH_RSA_MINIMUM_MODULUS_SIZE) ++ return SSH_ERR_KEY_LENGTH; + +- if ((sig = malloc(slen)) == NULL) { +- ret = SSH_ERR_ALLOC_FAIL; ++ if ((pkey = EVP_PKEY_new()) == NULL || ++ EVP_PKEY_set1_RSA(pkey, key->rsa) != 1) ++ return SSH_ERR_ALLOC_FAIL; ++ ret = sshkey_calculate_signature(pkey, hash_alg, &sig, &len, data, ++ datalen); ++ EVP_PKEY_free(pkey); ++ if (ret < 0) { + goto out; + } + +- if (RSA_sign(nid, digest, dlen, sig, &len, key->rsa) != 1) { +- ret = SSH_ERR_LIBCRYPTO_ERROR; +- goto out; +- } + if (len < slen) { + size_t diff = slen - len; + memmove(sig + diff, sig, len); +@@ -215,6 +190,7 @@ ssh_rsa_sign(const struct sshkey *key, u_char **sigp, size_t *lenp, + ret = SSH_ERR_INTERNAL_ERROR; + goto out; + } ++ + /* encode signature */ + if ((b = sshbuf_new()) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; +@@ -235,7 +211,6 @@ ssh_rsa_sign(const struct sshkey *key, u_char **sigp, size_t *lenp, + *lenp = len; + ret = 0; + out: +- explicit_bzero(digest, sizeof(digest)); + freezero(sig, slen); + sshbuf_free(b); + return ret; +@@ -246,10 +221,10 @@ ssh_rsa_verify(const struct sshkey *key, + const u_char *sig, size_t siglen, const u_char *data, size_t datalen, + const char *alg) + { +- const BIGNUM *rsa_n; ++ EVP_PKEY *pkey = NULL; + char *sigtype = NULL; + int hash_alg, want_alg, ret = SSH_ERR_INTERNAL_ERROR; +- size_t len = 0, diff, modlen, dlen; ++ size_t len = 0, diff, modlen; + struct sshbuf *b = NULL; + u_char digest[SSH_DIGEST_MAX_LENGTH], *osigblob, *sigblob = NULL; + +@@ -257,8 +232,7 @@ ssh_rsa_verify(const struct sshkey *key, + sshkey_type_plain(key->type) != KEY_RSA || + sig == NULL || siglen == 0) + return SSH_ERR_INVALID_ARGUMENT; +- RSA_get0_key(key->rsa, &rsa_n, NULL, NULL); +- if (BN_num_bits(rsa_n) < SSH_RSA_MINIMUM_MODULUS_SIZE) ++ if (RSA_bits(key->rsa) < SSH_RSA_MINIMUM_MODULUS_SIZE) + return SSH_ERR_KEY_LENGTH; + + if ((b = sshbuf_from(sig, siglen)) == NULL) +@@ -310,16 +284,15 @@ ssh_rsa_verify(const struct sshkey *key, + explicit_bzero(sigblob, diff); + len = modlen; + } +- if ((dlen = ssh_digest_bytes(hash_alg)) == 0) { +- ret = SSH_ERR_INTERNAL_ERROR; ++ ++ if ((pkey = EVP_PKEY_new()) == NULL || ++ EVP_PKEY_set1_RSA(pkey, key->rsa) != 1) { ++ ret = SSH_ERR_ALLOC_FAIL; + goto out; + } +- if ((ret = ssh_digest_memory(hash_alg, data, datalen, +- digest, sizeof(digest))) != 0) +- goto out; ++ ret = openssh_RSA_verify(hash_alg, data, datalen, sigblob, len, pkey); ++ EVP_PKEY_free(pkey); + +- ret = openssh_RSA_verify(hash_alg, digest, dlen, sigblob, len, +- key->rsa); + out: + freezero(sigblob, len); + free(sigtype); +@@ -328,122 +301,26 @@ ssh_rsa_verify(const struct sshkey *key, + return ret; + } + +-/* +- * See: +- * http://www.rsasecurity.com/rsalabs/pkcs/pkcs-1/ +- * ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1.asn +- */ +- +-/* +- * id-sha1 OBJECT IDENTIFIER ::= { iso(1) identified-organization(3) +- * oiw(14) secsig(3) algorithms(2) 26 } +- */ +-static const u_char id_sha1[] = { +- 0x30, 0x21, /* type Sequence, length 0x21 (33) */ +- 0x30, 0x09, /* type Sequence, length 0x09 */ +- 0x06, 0x05, /* type OID, length 0x05 */ +- 0x2b, 0x0e, 0x03, 0x02, 0x1a, /* id-sha1 OID */ +- 0x05, 0x00, /* NULL */ +- 0x04, 0x14 /* Octet string, length 0x14 (20), followed by sha1 hash */ +-}; +- +-/* +- * See http://csrc.nist.gov/groups/ST/crypto_apps_infra/csor/algorithms.html +- * id-sha256 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) country(16) us(840) +- * organization(1) gov(101) csor(3) nistAlgorithm(4) hashAlgs(2) +- * id-sha256(1) } +- */ +-static const u_char id_sha256[] = { +- 0x30, 0x31, /* type Sequence, length 0x31 (49) */ +- 0x30, 0x0d, /* type Sequence, length 0x0d (13) */ +- 0x06, 0x09, /* type OID, length 0x09 */ +- 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, /* id-sha256 */ +- 0x05, 0x00, /* NULL */ +- 0x04, 0x20 /* Octet string, length 0x20 (32), followed by sha256 hash */ +-}; +- +-/* +- * See http://csrc.nist.gov/groups/ST/crypto_apps_infra/csor/algorithms.html +- * id-sha512 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) country(16) us(840) +- * organization(1) gov(101) csor(3) nistAlgorithm(4) hashAlgs(2) +- * id-sha256(3) } +- */ +-static const u_char id_sha512[] = { +- 0x30, 0x51, /* type Sequence, length 0x51 (81) */ +- 0x30, 0x0d, /* type Sequence, length 0x0d (13) */ +- 0x06, 0x09, /* type OID, length 0x09 */ +- 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, /* id-sha512 */ +- 0x05, 0x00, /* NULL */ +- 0x04, 0x40 /* Octet string, length 0x40 (64), followed by sha512 hash */ +-}; +- + static int +-rsa_hash_alg_oid(int hash_alg, const u_char **oidp, size_t *oidlenp) ++openssh_RSA_verify(int hash_alg, const u_char *data, size_t datalen, ++ u_char *sigbuf, size_t siglen, EVP_PKEY *pkey) + { +- switch (hash_alg) { +- case SSH_DIGEST_SHA1: +- *oidp = id_sha1; +- *oidlenp = sizeof(id_sha1); +- break; +- case SSH_DIGEST_SHA256: +- *oidp = id_sha256; +- *oidlenp = sizeof(id_sha256); +- break; +- case SSH_DIGEST_SHA512: +- *oidp = id_sha512; +- *oidlenp = sizeof(id_sha512); +- break; +- default: +- return SSH_ERR_INVALID_ARGUMENT; +- } +- return 0; +-} ++ size_t rsasize = 0; ++ const RSA *rsa; ++ int ret; + +-static int +-openssh_RSA_verify(int hash_alg, u_char *hash, size_t hashlen, +- u_char *sigbuf, size_t siglen, RSA *rsa) +-{ +- size_t rsasize = 0, oidlen = 0, hlen = 0; +- int ret, len, oidmatch, hashmatch; +- const u_char *oid = NULL; +- u_char *decrypted = NULL; +- +- if ((ret = rsa_hash_alg_oid(hash_alg, &oid, &oidlen)) != 0) +- return ret; +- ret = SSH_ERR_INTERNAL_ERROR; +- hlen = ssh_digest_bytes(hash_alg); +- if (hashlen != hlen) { +- ret = SSH_ERR_INVALID_ARGUMENT; +- goto done; +- } ++ rsa = EVP_PKEY_get0_RSA(pkey); + rsasize = RSA_size(rsa); + if (rsasize <= 0 || rsasize > SSHBUF_MAX_BIGNUM || + siglen == 0 || siglen > rsasize) { + ret = SSH_ERR_INVALID_ARGUMENT; + goto done; + } +- if ((decrypted = malloc(rsasize)) == NULL) { +- ret = SSH_ERR_ALLOC_FAIL; +- goto done; +- } +- if ((len = RSA_public_decrypt(siglen, sigbuf, decrypted, rsa, +- RSA_PKCS1_PADDING)) < 0) { +- ret = SSH_ERR_LIBCRYPTO_ERROR; +- goto done; +- } +- if (len < 0 || (size_t)len != hlen + oidlen) { +- ret = SSH_ERR_INVALID_FORMAT; +- goto done; +- } +- oidmatch = timingsafe_bcmp(decrypted, oid, oidlen) == 0; +- hashmatch = timingsafe_bcmp(decrypted + oidlen, hash, hlen) == 0; +- if (!oidmatch || !hashmatch) { +- ret = SSH_ERR_SIGNATURE_INVALID; +- goto done; +- } +- ret = 0; ++ ++ ret = sshkey_verify_signature(pkey, hash_alg, data, datalen, ++ sigbuf, siglen); ++ + done: +- freezero(decrypted, rsasize); + return ret; + } + #endif /* WITH_OPENSSL */ +diff --git a/sshkey.c b/sshkey.c +index ad1957762..b95ed0b10 100644 +--- a/sshkey.c ++++ b/sshkey.c +@@ -358,6 +358,83 @@ sshkey_type_plain(int type) + } + + #ifdef WITH_OPENSSL ++int ++sshkey_calculate_signature(EVP_PKEY *pkey, int hash_alg, u_char **sigp, ++ int *lenp, const u_char *data, size_t datalen) ++{ ++ EVP_MD_CTX *ctx = NULL; ++ u_char *sig = NULL; ++ int ret, slen, len; ++ ++ if (sigp == NULL || lenp == NULL) { ++ return SSH_ERR_INVALID_ARGUMENT; ++ } ++ ++ slen = EVP_PKEY_size(pkey); ++ if (slen <= 0 || slen > SSHBUF_MAX_BIGNUM) ++ return SSH_ERR_INVALID_ARGUMENT; ++ ++ len = slen; ++ if ((sig = malloc(slen)) == NULL) { ++ return SSH_ERR_ALLOC_FAIL; ++ } ++ ++ if ((ctx = EVP_MD_CTX_new()) == NULL) { ++ ret = SSH_ERR_ALLOC_FAIL; ++ goto error; ++ } ++ if (EVP_SignInit_ex(ctx, ssh_digest_to_md(hash_alg), NULL) <= 0 || ++ EVP_SignUpdate(ctx, data, datalen) <= 0 || ++ EVP_SignFinal(ctx, sig, &len, pkey) <= 0) { ++ ret = SSH_ERR_LIBCRYPTO_ERROR; ++ goto error; ++ } ++ ++ *sigp = sig; ++ *lenp = len; ++ /* Now owned by the caller */ ++ sig = NULL; ++ ret = 0; ++ ++error: ++ EVP_MD_CTX_free(ctx); ++ free(sig); ++ return ret; ++} ++ ++int ++sshkey_verify_signature(EVP_PKEY *pkey, int hash_alg, const u_char *data, ++ size_t datalen, u_char *sigbuf, int siglen) ++{ ++ EVP_MD_CTX *ctx = NULL; ++ int ret; ++ ++ if ((ctx = EVP_MD_CTX_new()) == NULL) { ++ return SSH_ERR_ALLOC_FAIL; ++ } ++ if (EVP_VerifyInit_ex(ctx, ssh_digest_to_md(hash_alg), NULL) <= 0 || ++ EVP_VerifyUpdate(ctx, data, datalen) <= 0) { ++ ret = SSH_ERR_LIBCRYPTO_ERROR; ++ goto done; ++ } ++ ret = EVP_VerifyFinal(ctx, sigbuf, siglen, pkey); ++ switch (ret) { ++ case 1: ++ ret = 0; ++ break; ++ case 0: ++ ret = SSH_ERR_SIGNATURE_INVALID; ++ break; ++ default: ++ ret = SSH_ERR_LIBCRYPTO_ERROR; ++ break; ++ } ++ ++done: ++ EVP_MD_CTX_free(ctx); ++ return ret; ++} ++ + /* XXX: these are really begging for a table-driven approach */ + int + sshkey_curve_name_to_nid(const char *name) +diff --git a/sshkey.h b/sshkey.h +index a91e60436..270901a87 100644 +--- a/sshkey.h ++++ b/sshkey.h +@@ -179,6 +179,10 @@ const char *sshkey_ssh_name(const struct sshkey *); + const char *sshkey_ssh_name_plain(const struct sshkey *); + int sshkey_names_valid2(const char *, int); + char *sshkey_alg_list(int, int, int, char); ++int sshkey_calculate_signature(EVP_PKEY*, int, u_char **, ++ int *, const u_char *, size_t); ++int sshkey_verify_signature(EVP_PKEY *, int, const u_char *, ++ size_t, u_char *, int); + + int sshkey_from_blob(const u_char *, size_t, struct sshkey **); + int sshkey_fromb(struct sshbuf *, struct sshkey **); + diff --git a/openssh-8.0p1-openssl-kdf.patch b/openssh-8.0p1-openssl-kdf.patch new file mode 100644 index 0000000..1db95c3 --- /dev/null +++ b/openssh-8.0p1-openssl-kdf.patch @@ -0,0 +1,137 @@ +commit 2c3ef499bfffce3cfd315edeebf202850ba4e00a +Author: Jakub Jelen +Date: Tue Apr 16 15:35:18 2019 +0200 + + Use the new OpenSSL KDF + +diff --git a/configure.ac b/configure.ac +index 2a455e4e..e01c3d43 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -2712,6 +2712,7 @@ if test "x$openssl" = "xyes" ; then + HMAC_CTX_init \ + RSA_generate_key_ex \ + RSA_get_default_method \ ++ EVP_KDF_CTX_new_id \ + ]) + + # OpenSSL_add_all_algorithms may be a macro. +diff --git a/kex.c b/kex.c +index b6f041f4..1fbce2bb 100644 +--- a/kex.c ++++ b/kex.c +@@ -38,6 +38,9 @@ + #ifdef WITH_OPENSSL + #include + #include ++# ifdef HAVE_EVP_KDF_CTX_NEW_ID ++# include ++# endif + #endif + + #include "ssh.h" +@@ -942,6 +945,95 @@ kex_choose_conf(struct ssh *ssh) + return r; + } + ++#ifdef HAVE_EVP_KDF_CTX_NEW_ID ++static const EVP_MD * ++digest_to_md(int digest_type) ++{ ++ switch (digest_type) { ++ case SSH_DIGEST_SHA1: ++ return EVP_sha1(); ++ case SSH_DIGEST_SHA256: ++ return EVP_sha256(); ++ case SSH_DIGEST_SHA384: ++ return EVP_sha384(); ++ case SSH_DIGEST_SHA512: ++ return EVP_sha512(); ++ } ++ return NULL; ++} ++ ++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; ++ EVP_KDF_CTX *ctx = NULL; ++ u_char *key = NULL; ++ int r, key_len; ++ ++ if ((key_len = ssh_digest_bytes(kex->hash_alg)) == 0) ++ return SSH_ERR_INVALID_ARGUMENT; ++ key_len = ROUNDUP(need, key_len); ++ if ((key = calloc(1, key_len)) == NULL) { ++ r = SSH_ERR_ALLOC_FAIL; ++ goto out; ++ } ++ ++ ctx = EVP_KDF_CTX_new_id(EVP_KDF_SSHKDF); ++ if (!ctx) { ++ r = SSH_ERR_LIBCRYPTO_ERROR; ++ goto out; ++ } ++ ++ r = EVP_KDF_ctrl(ctx, EVP_KDF_CTRL_SET_MD, digest_to_md(kex->hash_alg)); ++ if (r != 1) { ++ r = SSH_ERR_LIBCRYPTO_ERROR; ++ goto out; ++ } ++ r = EVP_KDF_ctrl(ctx, EVP_KDF_CTRL_SET_KEY, ++ sshbuf_ptr(shared_secret), sshbuf_len(shared_secret)); ++ if (r != 1) { ++ r = SSH_ERR_LIBCRYPTO_ERROR; ++ goto out; ++ } ++ r = EVP_KDF_ctrl(ctx, EVP_KDF_CTRL_SET_SSHKDF_XCGHASH, hash, hashlen); ++ if (r != 1) { ++ r = SSH_ERR_LIBCRYPTO_ERROR; ++ goto out; ++ } ++ r = EVP_KDF_ctrl(ctx, EVP_KDF_CTRL_SET_SSHKDF_TYPE, id); ++ if (r != 1) { ++ r = SSH_ERR_LIBCRYPTO_ERROR; ++ goto out; ++ } ++ r = EVP_KDF_ctrl(ctx, EVP_KDF_CTRL_SET_SSHKDF_SESSION_ID, ++ kex->session_id, kex->session_id_len); ++ if (r != 1) { ++ r = SSH_ERR_LIBCRYPTO_ERROR; ++ goto out; ++ } ++ r = EVP_KDF_derive(ctx, key, key_len); ++ if (r != 1) { ++ r = SSH_ERR_LIBCRYPTO_ERROR; ++ goto out; ++ } ++#ifdef DEBUG_KEX ++ fprintf(stderr, "key '%c'== ", id); ++ dump_digest("key", key, key_len); ++#endif ++ *keyp = key; ++ key = NULL; ++ r = 0; ++ ++out: ++ free (key); ++ EVP_KDF_CTX_free(ctx); ++ if (r < 0) { ++ return r; ++ } ++ return 0; ++} ++#else + 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) +@@ -1004,6 +1096,7 @@ derive_key(struct ssh *ssh, int id, u_int need, u_char *hash, u_int hashlen, + ssh_digest_free(hashctx); + return r; + } ++#endif /* HAVE_OPENSSL_EVP_KDF_CTX_NEW_ID */ + + #define NKEYS 6 + int + diff --git a/openssh-8.0p1-openssl-pem.patch b/openssh-8.0p1-openssl-pem.patch new file mode 100644 index 0000000..7e4fa81 --- /dev/null +++ b/openssh-8.0p1-openssl-pem.patch @@ -0,0 +1,324 @@ +From eb0d8e708a1f958aecd2d6e2ff2450af488d4c2a Mon Sep 17 00:00:00 2001 +From: "djm@openbsd.org" +Date: Mon, 15 Jul 2019 13:16:29 +0000 +Subject: [PATCH] upstream: support PKCS8 as an optional format for storage of + +private keys, enabled via "ssh-keygen -m PKCS8" on operations that save +private keys to disk. + +The OpenSSH native key format remains the default, but PKCS8 is a +superior format to PEM if interoperability with non-OpenSSH software +is required, as it may use a less terrible KDF (IIRC PEM uses a single +round of MD5 as a KDF). + +adapted from patch by Jakub Jelen via bz3013; ok markus + +OpenBSD-Commit-ID: 027824e3bc0b1c243dc5188504526d73a55accb1 +--- + authfile.c | 6 ++-- + ssh-keygen.1 | 9 +++--- + ssh-keygen.c | 25 +++++++++-------- + sshkey.c | 78 +++++++++++++++++++++++++++++++++++++--------------- + sshkey.h | 11 ++++++-- + 5 files changed, 87 insertions(+), 42 deletions(-) + +diff --git a/authfile.c b/authfile.c +index 2166c1689..851c1a8a1 100644 +--- a/authfile.c ++++ b/authfile.c +@@ -74,7 +74,7 @@ sshkey_save_private_blob(struct sshbuf *keybuf, const char *filename) + int + sshkey_save_private(struct sshkey *key, const char *filename, + const char *passphrase, const char *comment, +- int force_new_format, const char *new_format_cipher, int new_format_rounds) ++ int format, const char *openssh_format_cipher, int openssh_format_rounds) + { + struct sshbuf *keyblob = NULL; + int r; +@@ -82,7 +82,7 @@ sshkey_save_private(struct sshkey *key, const char *filename, + if ((keyblob = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((r = sshkey_private_to_fileblob(key, keyblob, passphrase, comment, +- force_new_format, new_format_cipher, new_format_rounds)) != 0) ++ format, openssh_format_cipher, openssh_format_rounds)) != 0) + goto out; + if ((r = sshkey_save_private_blob(keyblob, filename)) != 0) + goto out; +diff --git a/ssh-keygen.1 b/ssh-keygen.1 +index f42127c60..8184a1797 100644 +--- a/ssh-keygen.1 ++++ b/ssh-keygen.1 +@@ -419,11 +419,12 @@ The supported key formats are: + .Dq RFC4716 + (RFC 4716/SSH2 public or private key), + .Dq PKCS8 +-(PEM PKCS8 public key) ++(PKCS8 public or private key) + or + .Dq PEM + (PEM public key). +-The default conversion format is ++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 +diff --git a/ssh-keygen.c b/ssh-keygen.c +index b019a02ff..5dcad1f61 100644 +--- a/ssh-keygen.c ++++ b/ssh-keygen.c +@@ -147,11 +147,11 @@ static char *key_type_name = NULL; + /* Load key from this PKCS#11 provider */ + static char *pkcs11provider = NULL; + +-/* Use new OpenSSH private key format when writing SSH2 keys instead of PEM */ +-static int use_new_format = 1; ++/* Format for writing private keys */ ++static int private_key_format = SSHKEY_PRIVATE_OPENSSH; + + /* Cipher for new-format private keys */ +-static char *new_format_cipher = NULL; ++static char *openssh_format_cipher = NULL; + + /* + * Number of KDF rounds to derive new format keys / +@@ -1048,7 +1048,8 @@ do_gen_all_hostkeys(struct passwd *pw) + snprintf(comment, sizeof comment, "%s@%s", pw->pw_name, + hostname); + if ((r = sshkey_save_private(private, prv_tmp, "", +- comment, use_new_format, new_format_cipher, rounds)) != 0) { ++ comment, private_key_format, openssh_format_cipher, ++ rounds)) != 0) { + error("Saving key \"%s\" failed: %s", + prv_tmp, ssh_err(r)); + goto failnext; +@@ -1391,7 +1392,7 @@ do_change_passphrase(struct passwd *pw) + + /* Save the file using the new passphrase. */ + if ((r = sshkey_save_private(private, identity_file, passphrase1, +- comment, use_new_format, new_format_cipher, rounds)) != 0) { ++ comment, private_key_format, openssh_format_cipher, rounds)) != 0) { + error("Saving key \"%s\" failed: %s.", + identity_file, ssh_err(r)); + explicit_bzero(passphrase1, strlen(passphrase1)); +@@ -1480,7 +1481,7 @@ do_change_comment(struct passwd *pw, const char *identity_comment) + } + + if (private->type != KEY_ED25519 && private->type != KEY_XMSS && +- !use_new_format) { ++ private_key_format != SSHKEY_PRIVATE_OPENSSH) { + error("Comments are only supported for keys stored in " + "the new format (-o)."); + explicit_bzero(passphrase, strlen(passphrase)); +@@ -1514,7 +1515,8 @@ do_change_comment(struct passwd *pw, const char *identity_comment) + + /* Save the file using the new passphrase. */ + if ((r = sshkey_save_private(private, identity_file, passphrase, +- new_comment, use_new_format, new_format_cipher, rounds)) != 0) { ++ new_comment, private_key_format, openssh_format_cipher, ++ rounds)) != 0) { + error("Saving key \"%s\" failed: %s", + identity_file, ssh_err(r)); + explicit_bzero(passphrase, strlen(passphrase)); +@@ -2525,11 +2527,12 @@ main(int argc, char **argv) + } + 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; +- use_new_format = 0; ++ private_key_format = SSHKEY_PRIVATE_PEM; + break; + } + fatal("Unsupported conversion format \"%s\"", optarg); +@@ -2567,7 +2570,7 @@ main(int argc, char **argv) + add_cert_option(optarg); + break; + case 'Z': +- new_format_cipher = optarg; ++ openssh_format_cipher = optarg; + break; + case 'C': + identity_comment = optarg; +@@ -2912,7 +2915,7 @@ main(int argc, char **argv) + + /* Save the key with the given passphrase and comment. */ + if ((r = sshkey_save_private(private, identity_file, passphrase1, +- comment, use_new_format, new_format_cipher, rounds)) != 0) { ++ comment, private_key_format, openssh_format_cipher, rounds)) != 0) { + error("Saving key \"%s\" failed: %s", + identity_file, ssh_err(r)); + explicit_bzero(passphrase1, strlen(passphrase1)); +diff --git a/sshkey.c b/sshkey.c +index 6b5ff0485..a0cea9257 100644 +--- a/sshkey.c ++++ b/sshkey.c +@@ -3975,10 +3975,10 @@ sshkey_parse_private2(struct sshbuf *blob, int type, const char *passphrase, + + + #ifdef WITH_OPENSSL +-/* convert SSH v2 key in OpenSSL PEM format */ ++/* convert SSH v2 key to PEM or PKCS#8 format */ + static int +-sshkey_private_pem_to_blob(struct sshkey *key, struct sshbuf *blob, +- const char *_passphrase, const char *comment) ++sshkey_private_to_blob_pem_pkcs8(struct sshkey *key, struct sshbuf *blob, ++ int format, const char *_passphrase, const char *comment) + { + int success, r; + int blen, len = strlen(_passphrase); +@@ -3988,26 +3988,46 @@ sshkey_private_pem_to_blob(struct sshkey *key, struct sshbuf *buf, + const EVP_CIPHER *cipher = (len > 0) ? EVP_aes_128_cbc() : NULL; + char *bptr; + BIO *bio = NULL; ++ EVP_PKEY *pkey = NULL; + + if (len > 0 && len <= 4) + return SSH_ERR_PASSPHRASE_TOO_SHORT; +- if ((bio = BIO_new(BIO_s_mem())) == 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; ++ } + + switch (key->type) { + case KEY_DSA: +- success = PEM_write_bio_DSAPrivateKey(bio, key->dsa, +- cipher, passphrase, len, NULL, NULL); ++ 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: +- success = PEM_write_bio_ECPrivateKey(bio, key->ecdsa, +- cipher, passphrase, len, NULL, NULL); ++ 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: +- success = PEM_write_bio_RSAPrivateKey(bio, key->rsa, +- cipher, passphrase, len, NULL, NULL); ++ 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; +@@ -4023,6 +4040,13 @@ sshkey_private_pem_to_blob(struct sshkey *key, struct sshbuf *buf, + 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; +@@ -4035,6 +4059,7 @@ sshkey_private_pem_to_blob(struct sshkey *key, struct sshbuf *buf, + goto out; + r = 0; + out: ++ EVP_PKEY_free(pkey); + BIO_free(bio); + return r; + } +@@ -4046,29 +4071,38 @@ sshkey_private_pem_to_blob(struct sshkey *key, struct sshbuf *buf, + int + sshkey_private_to_fileblob(struct sshkey *key, struct sshbuf *blob, + const char *passphrase, const char *comment, +- int force_new_format, const char *new_format_cipher, int new_format_rounds) ++ 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: +- if (force_new_format) { +- return sshkey_private_to_blob2(key, blob, passphrase, +- comment, new_format_cipher, new_format_rounds); +- } +- return sshkey_private_pem_to_blob(key, blob, +- passphrase, comment); ++ break; /* see below */ + #endif /* WITH_OPENSSL */ + case KEY_ED25519: + #ifdef WITH_XMSS + case KEY_XMSS: + #endif /* WITH_XMSS */ + return sshkey_private_to_blob2(key, blob, passphrase, +- comment, new_format_cipher, new_format_rounds); ++ 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 */ + } + + +diff --git a/sshkey.h b/sshkey.h +index 41d159a1b..d30a69cc9 100644 +--- a/sshkey.h ++++ b/sshkey.h +@@ -88,6 +88,13 @@ enum sshkey_serialize_rep { + SSHKEY_SERIALIZE_INFO = 254, + }; + ++/* Private key disk formats */ ++enum sshkey_private_format { ++ SSHKEY_PRIVATE_OPENSSH = 0, ++ SSHKEY_PRIVATE_PEM = 1, ++ SSHKEY_PRIVATE_PKCS8 = 2, ++}; ++ + /* key is stored in external hardware */ + #define SSHKEY_FLAG_EXT 0x0001 + +@@ -221,7 +228,7 @@ int sshkey_private_deserialize(struct sshbuf *buf, struct sshkey **keyp); + /* private key file format parsing and serialisation */ + int sshkey_private_to_fileblob(struct sshkey *key, struct sshbuf *blob, + const char *passphrase, const char *comment, +- int force_new_format, const char *new_format_cipher, int new_format_rounds); ++ int format, const char *openssh_format_cipher, int openssh_format_rounds); + int sshkey_parse_private_fileblob(struct sshbuf *buffer, + const char *passphrase, struct sshkey **keyp, char **commentp); + int sshkey_parse_private_fileblob_type(struct sshbuf *blob, int type, + diff --git a/openssh-8.0p1-pkcs11-uri.patch b/openssh-8.0p1-pkcs11-uri.patch new file mode 100644 index 0000000..80af3e0 --- /dev/null +++ b/openssh-8.0p1-pkcs11-uri.patch @@ -0,0 +1,3140 @@ +diff --git a/Makefile.in b/Makefile.in +index 6f001bb3..c9424f1e 100644 +--- a/Makefile.in ++++ b/Makefile.in +@@ -93,7 +93,7 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \ + atomicio.o dispatch.o mac.o uuencode.o misc.o utf8.o \ + monitor_fdpass.o rijndael.o ssh-dss.o ssh-ecdsa.o ssh-rsa.o dh.o \ + msg.o progressmeter.o dns.o entropy.o gss-genr.o umac.o umac128.o \ +- ssh-pkcs11.o smult_curve25519_ref.o \ ++ ssh-pkcs11.o ssh-pkcs11-uri.o smult_curve25519_ref.o \ + poly1305.o chacha.o cipher-chachapoly.o \ + ssh-ed25519.o digest-openssl.o digest-libc.o hmac.o \ + sc25519.o ge25519.o fe25519.o ed25519.o verify.o hash.o \ +@@ -250,6 +250,8 @@ clean: regressclean + rm -f regress/unittests/match/test_match$(EXEEXT) + rm -f regress/unittests/utf8/*.o + rm -f regress/unittests/utf8/test_utf8$(EXEEXT) ++ rm -f regress/unittests/pkcs11/*.o ++ rm -f regress/unittests/pkcs11/test_pkcs11$(EXEEXT) + rm -f regress/misc/kexfuzz/*.o + rm -f regress/misc/kexfuzz/kexfuzz$(EXEEXT) + (cd openbsd-compat && $(MAKE) clean) +@@ -280,6 +282,8 @@ distclean: regressclean + rm -f regress/unittests/match/test_match + rm -f regress/unittests/utf8/*.o + rm -f regress/unittests/utf8/test_utf8 ++ rm -f regress/unittests/pkcs11/*.o ++ rm -f regress/unittests/pkcs11/test_pkcs11 + rm -f regress/misc/kexfuzz/*.o + rm -f regress/misc/kexfuzz/kexfuzz$(EXEEXT) + (cd openbsd-compat && $(MAKE) distclean) +@@ -442,6 +446,7 @@ regress-prep: + $(MKDIR_P) `pwd`/regress/unittests/kex + $(MKDIR_P) `pwd`/regress/unittests/match + $(MKDIR_P) `pwd`/regress/unittests/utf8 ++ $(MKDIR_P) `pwd`/regress/unittests/pkcs11 + $(MKDIR_P) `pwd`/regress/misc/kexfuzz + [ -f `pwd`/regress/Makefile ] || \ + ln -s `cd $(srcdir) && pwd`/regress/Makefile `pwd`/regress/Makefile +@@ -565,6 +570,16 @@ regress/unittests/utf8/test_utf8$(EXEEXT): \ + regress/unittests/test_helper/libtest_helper.a \ + -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) + ++UNITTESTS_TEST_PKCS11_OBJS=\ ++ regress/unittests/pkcs11/tests.o ++ ++regress/unittests/pkcs11/test_pkcs11$(EXEEXT): \ ++ ${UNITTESTS_TEST_PKCS11_OBJS} \ ++ regress/unittests/test_helper/libtest_helper.a libssh.a ++ $(LD) -o $@ $(LDFLAGS) $(UNITTESTS_TEST_PKCS11_OBJS) \ ++ regress/unittests/test_helper/libtest_helper.a \ ++ -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) ++ + MISC_KEX_FUZZ_OBJS=\ + regress/misc/kexfuzz/kexfuzz.o + +@@ -585,6 +600,7 @@ regress-binaries: regress/modpipe$(EXEEXT) \ + regress/unittests/kex/test_kex$(EXEEXT) \ + regress/unittests/match/test_match$(EXEEXT) \ + regress/unittests/utf8/test_utf8$(EXEEXT) \ ++ regress/unittests/pkcs11/test_pkcs11$(EXEEXT) \ + regress/misc/kexfuzz/kexfuzz$(EXEEXT) + + tests interop-tests t-exec unit: regress-prep regress-binaries $(TARGETS) +diff --git a/authfd.c b/authfd.c +index 95348abf..5383df92 100644 +--- a/authfd.c ++++ b/authfd.c +@@ -312,6 +312,8 @@ ssh_free_identitylist(struct ssh_identitylist *idl) + if (idl->comments != NULL) + free(idl->comments[i]); + } ++ free(idl->keys); ++ free(idl->comments); + free(idl); + } + +diff --git a/configure.ac b/configure.ac +index 30be6c18..82459746 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -1854,12 +1854,14 @@ AC_LINK_IFELSE( + [AC_DEFINE([HAVE_ISBLANK], [1], [Define if you have isblank(3C).]) + ]) + ++SCARD_MSG="yes" + disable_pkcs11= + AC_ARG_ENABLE([pkcs11], + [ --disable-pkcs11 disable PKCS#11 support code [no]], + [ + if test "x$enableval" = "xno" ; then + disable_pkcs11=1 ++ SCARD_MSG="no" + fi + ] + ) +@@ -1875,6 +1877,40 @@ if test "x$openssl" = "xyes" && test "x$disable_pkcs11" = "x"; then + ) + fi + ++# Check whether we have a p11-kit, we got default provider on command line ++DEFAULT_PKCS11_PROVIDER_MSG="no" ++AC_ARG_WITH([default-pkcs11-provider], ++ [ --with-default-pkcs11-provider[[=PATH]] Use default pkcs11 provider (p11-kit detected by default)], ++ [ if test "x$withval" != "xno" && test "x$disable_pkcs11" = "x"; then ++ if test "x$withval" = "xyes" ; then ++ AC_PATH_TOOL([PKGCONFIG], [pkg-config], [no]) ++ if test "x$PKGCONFIG" != "xno"; then ++ AC_MSG_CHECKING([if $PKGCONFIG knows about p11-kit]) ++ if "$PKGCONFIG" "p11-kit-1"; then ++ AC_MSG_RESULT([yes]) ++ use_pkgconfig_for_p11kit=yes ++ else ++ AC_MSG_RESULT([no]) ++ fi ++ fi ++ else ++ PKCS11_PATH="${withval}" ++ fi ++ if test "x$use_pkgconfig_for_p11kit" = "xyes"; then ++ PKCS11_PATH=`$PKGCONFIG --variable=proxy_module p11-kit-1` ++ fi ++ AC_CHECK_FILE("$PKCS11_PATH", ++ [ AC_DEFINE_UNQUOTED([PKCS11_DEFAULT_PROVIDER], ["$PKCS11_PATH"], [Path to default PKCS#11 provider (p11-kit proxy)]) ++ DEFAULT_PKCS11_PROVIDER_MSG="$PKCS11_PATH" ++ ], ++ [ AC_MSG_ERROR([Requested PKCS11 provided not found]) ] ++ ) ++ else ++ AC_MSG_WARN([Needs PKCS11 support to enable default pkcs11 provider]) ++ fi ] ++) ++ ++ + # IRIX has a const char return value for gai_strerror() + AC_CHECK_FUNCS([gai_strerror], [ + AC_DEFINE([HAVE_GAI_STRERROR]) +@@ -5256,6 +5292,7 @@ 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 " Default PKCS#11 provider: $DEFAULT_PKCS11_PROVIDER_MSG" + + echo "" + +diff --git a/regress/Makefile b/regress/Makefile +index 925edf71..94bb25e9 100644 +--- a/regress/Makefile ++++ b/regress/Makefile +@@ -109,9 +109,11 @@ CLEANFILES= *.core actual agent-key.* authorized_keys_${USERNAME} \ + 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 \ ++ remote_pid revoked-* rsa rsa-agent rsa-agent.pub \ ++ rsa-agent-cert.pub rsa.pub \ + rsa1 rsa1-agent rsa1-agent.pub rsa1.pub rsa_ssh2_cr.prv \ +- rsa_ssh2_crnl.prv scp-ssh-wrapper.exe \ ++ pkcs11*.crt pkcs11*.key \ ++ pkcs11.info 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_config ssh_config.* ssh_proxy ssh_proxy_bak \ +@@ -231,6 +233,7 @@ unit: + V="" ; \ + test "x${USE_VALGRIND}" = "x" || \ + V=${.CURDIR}/valgrind-unit.sh ; \ ++ $$V ${.OBJDIR}/unittests/pkcs11/test_pkcs11 ; \ + $$V ${.OBJDIR}/unittests/sshbuf/test_sshbuf ; \ + $$V ${.OBJDIR}/unittests/sshkey/test_sshkey \ + -d ${.CURDIR}/unittests/sshkey/testdata ; \ +diff --git a/regress/agent-pkcs11.sh b/regress/agent-pkcs11.sh +index 5205d906..5ca49be5 100644 +--- a/regress/agent-pkcs11.sh ++++ b/regress/agent-pkcs11.sh +@@ -29,6 +29,13 @@ fi + + test -f "$TEST_SSH_PKCS11" || fatal "$TEST_SSH_PKCS11 does not exist" + ++# requires ssh-agent built with correct path to ssh-pkcs11-helper ++# otherwise it fails to start the helper ++strings ${TEST_SSH_SSHAGENT} | grep "$TEST_SSH_SSHPKCS11HELPER" ++if [ $? -ne 0 ]; then ++ fatal "Needs to reconfigure with --libexecdir=\`pwd\` or so" ++fi ++ + # setup environment for softhsm2 token + DIR=$OBJ/SOFTHSM + rm -rf $DIR +@@ -113,7 +120,7 @@ else + done + + trace "remove pkcs11 keys" +- echo ${TEST_SSH_PIN} | notty ${SSHADD} -e ${TEST_SSH_PKCS11} > /dev/null 2>&1 ++ ${SSHADD} -e ${TEST_SSH_PKCS11} > /dev/null 2>&1 + r=$? + if [ $r -ne 0 ]; then + fail "ssh-add -e failed: exit code $r" +diff --git a/regress/pkcs11.sh b/regress/pkcs11.sh +new file mode 100644 +index 00000000..19fc8169 +--- /dev/null ++++ b/regress/pkcs11.sh +@@ -0,0 +1,352 @@ ++# ++# Copyright (c) 2017 Red Hat ++# ++# Authors: Jakub Jelen ++# ++# 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. ++ ++tid="pkcs11 tests with soft token" ++ ++try_token_libs() { ++ for _lib in "$@" ; do ++ if test -f "$_lib" ; then ++ verbose "Using token library $_lib" ++ TEST_SSH_PKCS11="$_lib" ++ return ++ fi ++ done ++ echo "skipped: Unable to find PKCS#11 token library" ++ exit 0 ++} ++ ++try_token_libs \ ++ /usr/local/lib/softhsm/libsofthsm2.so \ ++ /usr/lib64/pkcs11/libsofthsm2.so \ ++ /usr/lib/x86_64-linux-gnu/softhsm/libsofthsm2.so ++ ++TEST_SSH_PIN=1234 ++TEST_SSH_SOPIN=12345678 ++if [ "x$TEST_SSH_SSHPKCS11HELPER" != "x" ]; then ++ SSH_PKCS11_HELPER="${TEST_SSH_SSHPKCS11HELPER}" ++ export SSH_PKCS11_HELPER ++fi ++ ++test -f "$TEST_SSH_PKCS11" || fatal "$TEST_SSH_PKCS11 does not exist" ++ ++# requires ssh-agent built with correct path to ssh-pkcs11-helper ++# otherwise it fails to start the helper ++strings ${TEST_SSH_SSHAGENT} | grep "$TEST_SSH_SSHPKCS11HELPER" ++if [ $? -ne 0 ]; then ++ fatal "Needs to reconfigure with --libexecdir=\`pwd\` or so" ++fi ++ ++# setup environment for softhsm token ++DIR=$OBJ/SOFTHSM ++rm -rf $DIR ++TOKEN=$DIR/tokendir ++mkdir -p $TOKEN ++SOFTHSM2_CONF=$DIR/softhsm2.conf ++export SOFTHSM2_CONF ++cat > $SOFTHSM2_CONF << EOF ++# SoftHSM v2 configuration file ++directories.tokendir = ${TOKEN} ++objectstore.backend = file ++# ERROR, WARNING, INFO, DEBUG ++log.level = DEBUG ++# If CKF_REMOVABLE_DEVICE flag should be set ++slots.removable = false ++EOF ++out=$(softhsm2-util --init-token --free --label token-slot-0 --pin "$TEST_SSH_PIN" --so-pin "$TEST_SSH_SOPIN") ++slot=$(echo -- $out | sed 's/.* //') ++ ++# prevent ssh-agent from calling ssh-askpass ++SSH_ASKPASS=/usr/bin/true ++export SSH_ASKPASS ++unset DISPLAY ++# We need interactive access to test PKCS# since it prompts for PIN ++sed -i 's/.*BatchMode.*//g' $OBJ/ssh_proxy ++ ++# start command w/o tty, so ssh accepts pin from stdin (from agent-pkcs11.sh) ++notty() { ++ perl -e 'use POSIX; POSIX::setsid(); ++ if (fork) { wait; exit($? >> 8); } else { exec(@ARGV) }' "$@" ++} ++ ++trace "generating keys" ++ID1="02" ++ID2="04" ++RSA=${DIR}/RSA ++EC=${DIR}/EC ++openssl genpkey -algorithm rsa > $RSA ++openssl pkcs8 -nocrypt -in $RSA |\ ++ softhsm2-util --slot "$slot" --label "SSH RSA Key $ID1" --id $ID1 \ ++ --pin "$TEST_SSH_PIN" --import /dev/stdin ++openssl genpkey \ ++ -genparam \ ++ -algorithm ec \ ++ -pkeyopt ec_paramgen_curve:prime256v1 |\ ++ openssl genpkey \ ++ -paramfile /dev/stdin > $EC ++openssl pkcs8 -nocrypt -in $EC |\ ++ softhsm2-util --slot "$slot" --label "SSH ECDSA Key $ID2" --id $ID2 \ ++ --pin "$TEST_SSH_PIN" --import /dev/stdin ++ ++trace "List the keys in the ssh-keygen with PKCS#11 URIs" ++${SSHKEYGEN} -D ${TEST_SSH_PKCS11} > $OBJ/token_keys ++if [ $? -ne 0 ]; then ++ fail "keygen fails to enumerate keys on PKCS#11 token" ++fi ++grep "pkcs11:" $OBJ/token_keys > /dev/null ++if [ $? -ne 0 ]; then ++ fail "The keys from ssh-keygen do not contain PKCS#11 URI as a comment" ++fi ++tail -n 1 $OBJ/token_keys > $OBJ/authorized_keys_$USER ++ ++trace "Simple connect with ssh (without PKCS#11 URI)" ++echo ${TEST_SSH_PIN} | notty ${SSH} -I ${TEST_SSH_PKCS11} \ ++ -F $OBJ/ssh_proxy somehost exit 5 ++r=$? ++if [ $r -ne 5 ]; then ++ fail "ssh connect with pkcs11 failed (exit code $r)" ++fi ++ ++trace "Connect with PKCS#11 URI" ++trace " (second key should succeed)" ++echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \ ++ -i "pkcs11:id=${ID2}?module-path=${TEST_SSH_PKCS11}" somehost exit 5 ++r=$? ++if [ $r -ne 5 ]; then ++ fail "ssh connect with PKCS#11 URI failed (exit code $r)" ++fi ++ ++trace " (first key should fail)" ++echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \ ++ -i "pkcs11:id=${ID1}?module-path=${TEST_SSH_PKCS11}" somehost exit 5 ++r=$? ++if [ $r -eq 5 ]; then ++ fail "ssh connect with PKCS#11 URI succeeded (should fail)" ++fi ++ ++trace "Connect with PKCS#11 URI including PIN should not prompt" ++trace " (second key should succeed)" ++${SSH} -F $OBJ/ssh_proxy -i \ ++ "pkcs11:id=${ID2}?module-path=${TEST_SSH_PKCS11}&pin-value=${TEST_SSH_PIN}" somehost exit 5 ++r=$? ++if [ $r -ne 5 ]; then ++ fail "ssh connect with PKCS#11 URI failed (exit code $r)" ++fi ++ ++trace " (first key should fail)" ++${SSH} -F $OBJ/ssh_proxy -i \ ++ "pkcs11:id=${ID1}?module-path=${TEST_SSH_PKCS11}&pin-value=${TEST_SSH_PIN}" somehost exit 5 ++r=$? ++if [ $r -eq 5 ]; then ++ fail "ssh connect with PKCS#11 URI succeeded (should fail)" ++fi ++ ++trace "Connect with various filtering options in PKCS#11 URI" ++trace " (by object label, second key should succeed)" ++echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \ ++ -i "pkcs11:object=SSH%20RSA%20Key%202?module-path=${TEST_SSH_PKCS11}" somehost exit 5 ++r=$? ++if [ $r -ne 5 ]; then ++ fail "ssh connect with PKCS#11 URI failed (exit code $r)" ++fi ++ ++trace " (by object label, first key should fail)" ++echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \ ++ -i "pkcs11:object=SSH%20RSA%20Key?module-path=${TEST_SSH_PKCS11}" somehost exit 5 ++r=$? ++if [ $r -eq 5 ]; then ++ fail "ssh connect with PKCS#11 URI succeeded (should fail)" ++fi ++ ++trace " (by token label, second key should succeed)" ++echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \ ++ -i "pkcs11:id=${ID2};token=SoftToken%20(token)?module-path=${TEST_SSH_PKCS11}" somehost exit 5 ++r=$? ++if [ $r -ne 5 ]; then ++ fail "ssh connect with PKCS#11 URI failed (exit code $r)" ++fi ++ ++trace " (by wrong token label, should fail)" ++echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \ ++ -i "pkcs11:token=SoftToken?module-path=${TEST_SSH_PKCS11}" somehost exit 5 ++r=$? ++if [ $r -eq 5 ]; then ++ fail "ssh connect with PKCS#11 URI succeeded (should fail)" ++fi ++ ++ ++ ++ ++trace "Test PKCS#11 URI specification in configuration files" ++echo "IdentityFile \"pkcs11:id=${ID2}?module-path=${TEST_SSH_PKCS11}\"" \ ++ >> $OBJ/ssh_proxy ++trace " (second key should succeed)" ++echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy somehost exit 5 ++r=$? ++if [ $r -ne 5 ]; then ++ fail "ssh connect with PKCS#11 URI in config failed (exit code $r)" ++fi ++ ++trace " (first key should fail)" ++head -n 1 $OBJ/token_keys > $OBJ/authorized_keys_$USER ++echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy somehost exit 5 ++r=$? ++if [ $r -eq 5 ]; then ++ fail "ssh connect with PKCS#11 URI in config succeeded (should fail)" ++fi ++sed -i -e "/IdentityFile/d" $OBJ/ssh_proxy ++ ++trace "Test PKCS#11 URI specification in configuration files with bogus spaces" ++echo "IdentityFile \" pkcs11:id=${ID1}?module-path=${TEST_SSH_PKCS11} \"" \ ++ >> $OBJ/ssh_proxy ++echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy somehost exit 5 ++r=$? ++if [ $r -ne 5 ]; then ++ fail "ssh connect with PKCS#11 URI with bogus spaces in config failed" \ ++ "(exit code $r)" ++fi ++sed -i -e "/IdentityFile/d" $OBJ/ssh_proxy ++ ++ ++trace "Combination of PKCS11Provider and PKCS11URI on commandline" ++trace " (first key should succeed)" ++echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \ ++ -i "pkcs11:id=${ID1}" -I ${TEST_SSH_PKCS11} somehost exit 5 ++r=$? ++if [ $r -ne 5 ]; then ++ fail "ssh connect with PKCS#11 URI and provider combination" \ ++ "failed (exit code $r)" ++fi ++ ++trace "Regress: Missing provider in PKCS11URI option" ++${SSH} -F $OBJ/ssh_proxy \ ++ -o IdentityFile=\"pkcs11:token=segfault\" somehost exit 5 ++r=$? ++if [ $r -eq 139 ]; then ++ fail "ssh connect with missing provider_id from configuration option" \ ++ "crashed (exit code $r)" ++fi ++ ++ ++trace "SSH Agent can work with PKCS#11 URI" ++trace "start the agent" ++eval `${SSHAGENT} -s -P "${OBJ}/*"` > /dev/null ++ ++r=$? ++if [ $r -ne 0 ]; then ++ fail "could not start ssh-agent: exit code $r" ++else ++ trace "add whole provider to agent" ++ echo ${TEST_SSH_PIN} | notty ${SSHADD} \ ++ "pkcs11:?module-path=${TEST_SSH_PKCS11}" > /dev/null 2>&1 ++ r=$? ++ if [ $r -ne 0 ]; then ++ fail "ssh-add failed with whole provider: exit code $r" ++ fi ++ ++ trace " pkcs11 list via agent (all keys)" ++ ${SSHADD} -l > /dev/null 2>&1 ++ r=$? ++ if [ $r -ne 0 ]; then ++ fail "ssh-add -l failed with whole provider: exit code $r" ++ fi ++ ++ trace " pkcs11 connect via agent (all keys)" ++ ${SSH} -F $OBJ/ssh_proxy somehost exit 5 ++ r=$? ++ if [ $r -ne 5 ]; then ++ fail "ssh connect failed with whole provider (exit code $r)" ++ fi ++ ++ trace " remove pkcs11 keys (all keys)" ++ ${SSHADD} -d "pkcs11:?module-path=${TEST_SSH_PKCS11}" > /dev/null 2>&1 ++ r=$? ++ if [ $r -ne 0 ]; then ++ fail "ssh-add -d failed with whole provider: exit code $r" ++ fi ++ ++ trace "add only first key to the agent" ++ echo ${TEST_SSH_PIN} | notty ${SSHADD} \ ++ "pkcs11:id=${ID1}?module-path=${TEST_SSH_PKCS11}" > /dev/null 2>&1 ++ r=$? ++ if [ $r -ne 0 ]; then ++ fail "ssh-add failed with first key: exit code $r" ++ fi ++ ++ trace " pkcs11 connect via agent (first key)" ++ ${SSH} -F $OBJ/ssh_proxy somehost exit 5 ++ r=$? ++ if [ $r -ne 5 ]; then ++ fail "ssh connect failed with first key (exit code $r)" ++ fi ++ ++ trace " remove first pkcs11 key" ++ ${SSHADD} -d "pkcs11:id=${ID1}?module-path=${TEST_SSH_PKCS11}" \ ++ > /dev/null 2>&1 ++ r=$? ++ if [ $r -ne 0 ]; then ++ fail "ssh-add -d failed with first key: exit code $r" ++ fi ++ ++ trace "add only second key to the agent" ++ echo ${TEST_SSH_PIN} | notty ${SSHADD} \ ++ "pkcs11:id=${ID2}?module-path=${TEST_SSH_PKCS11}" > /dev/null 2>&1 ++ r=$? ++ if [ $r -ne 0 ]; then ++ fail "ssh-add failed with second key: exit code $r" ++ fi ++ ++ trace " pkcs11 connect via agent (second key should fail)" ++ ${SSH} -F $OBJ/ssh_proxy somehost exit 5 ++ r=$? ++ if [ $r -eq 5 ]; then ++ fail "ssh connect passed without key (should fail)" ++ fi ++ ++ trace "add also the first key to the agent" ++ echo ${TEST_SSH_PIN} | notty ${SSHADD} \ ++ "pkcs11:id=${ID1}?module-path=${TEST_SSH_PKCS11}" > /dev/null 2>&1 ++ r=$? ++ if [ $r -ne 0 ]; then ++ fail "ssh-add failed with first key: exit code $r" ++ fi ++ ++ trace " remove second pkcs11 key" ++ ${SSHADD} -d "pkcs11:id=${ID2}?module-path=${TEST_SSH_PKCS11}" \ ++ > /dev/null 2>&1 ++ r=$? ++ if [ $r -ne 0 ]; then ++ fail "ssh-add -d failed with second key: exit code $r" ++ fi ++ ++ trace " remove already-removed pkcs11 key should fail" ++ ${SSHADD} -d "pkcs11:id=${ID2}?module-path=${TEST_SSH_PKCS11}" \ ++ > /dev/null 2>&1 ++ r=$? ++ if [ $r -eq 0 ]; then ++ fail "ssh-add -d passed with non-existing key (should fail)" ++ fi ++ ++ trace " pkcs11 connect via agent (the first key should be still usable)" ++ ${SSH} -F $OBJ/ssh_proxy somehost exit 5 ++ r=$? ++ if [ $r -ne 5 ]; then ++ fail "ssh connect failed with first key (after removing second): exit code $r" ++ fi ++ ++ trace "kill agent" ++ ${SSHAGENT} -k > /dev/null ++fi +diff --git a/regress/unittests/Makefile b/regress/unittests/Makefile +index e464b085..a0e5a37c 100644 +--- a/regress/unittests/Makefile ++++ b/regress/unittests/Makefile +@@ -2,6 +2,6 @@ + + REGRESS_FAIL_EARLY?= yes + SUBDIR= test_helper sshbuf sshkey bitmap kex hostkeys utf8 match conversion +-SUBDIR+=authopt ++SUBDIR+=pkcs11 authopt + + .include +diff --git a/regress/unittests/pkcs11/tests.c b/regress/unittests/pkcs11/tests.c +new file mode 100644 +index 00000000..b637cb13 +--- /dev/null ++++ b/regress/unittests/pkcs11/tests.c +@@ -0,0 +1,337 @@ ++/* ++ * Copyright (c) 2017 Red Hat ++ * ++ * Authors: Jakub Jelen ++ * ++ * 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 "../test_helper/test_helper.h" ++ ++#include "sshbuf.h" ++#include "ssh-pkcs11-uri.h" ++ ++#define EMPTY_URI compose_uri(NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL) ++ ++/* prototypes are not public -- specify them here internally for tests */ ++struct sshbuf *percent_encode(const char *, size_t, char *); ++int percent_decode(char *, char **); ++ ++void ++compare_uri(struct pkcs11_uri *a, struct pkcs11_uri *b) ++{ ++ ASSERT_PTR_NE(a, NULL); ++ ASSERT_PTR_NE(b, NULL); ++ ASSERT_SIZE_T_EQ(a->id_len, b->id_len); ++ ASSERT_MEM_EQ(a->id, b->id, a->id_len); ++ if (b->object != NULL) ++ ASSERT_STRING_EQ(a->object, b->object); ++ else /* both should be null */ ++ ASSERT_PTR_EQ(a->object, b->object); ++ if (b->module_path != NULL) ++ ASSERT_STRING_EQ(a->module_path, b->module_path); ++ else /* both should be null */ ++ ASSERT_PTR_EQ(a->module_path, b->module_path); ++ if (b->token != NULL) ++ ASSERT_STRING_EQ(a->token, b->token); ++ else /* both should be null */ ++ ASSERT_PTR_EQ(a->token, b->token); ++ if (b->manuf != NULL) ++ ASSERT_STRING_EQ(a->manuf, b->manuf); ++ else /* both should be null */ ++ ASSERT_PTR_EQ(a->manuf, b->manuf); ++ if (b->lib_manuf != NULL) ++ ASSERT_STRING_EQ(a->lib_manuf, b->lib_manuf); ++ else /* both should be null */ ++ ASSERT_PTR_EQ(a->lib_manuf, b->lib_manuf); ++} ++ ++void ++check_parse_rv(char *uri, struct pkcs11_uri *expect, int expect_rv) ++{ ++ char *buf = NULL, *str; ++ struct pkcs11_uri *pkcs11uri = NULL; ++ int rv; ++ ++ if (expect_rv == 0) ++ str = "Valid"; ++ else ++ str = "Invalid"; ++ asprintf(&buf, "%s PKCS#11 URI parsing: %s", str, uri); ++ TEST_START(buf); ++ free(buf); ++ pkcs11uri = pkcs11_uri_init(); ++ rv = pkcs11_uri_parse(uri, pkcs11uri); ++ ASSERT_INT_EQ(rv, expect_rv); ++ if (rv == 0) /* in case of failure result is undefined */ ++ compare_uri(pkcs11uri, expect); ++ pkcs11_uri_cleanup(pkcs11uri); ++ free(expect); ++ TEST_DONE(); ++} ++ ++void ++check_parse(char *uri, struct pkcs11_uri *expect) ++{ ++ check_parse_rv(uri, expect, 0); ++} ++ ++struct pkcs11_uri * ++compose_uri(unsigned char *id, size_t id_len, char *token, char *lib_manuf, ++ char *manuf, char *module_path, char *object, char *pin) ++{ ++ struct pkcs11_uri *uri = pkcs11_uri_init(); ++ if (id_len > 0) { ++ uri->id_len = id_len; ++ uri->id = id; ++ } ++ uri->module_path = module_path; ++ uri->token = token; ++ uri->lib_manuf = lib_manuf; ++ uri->manuf = manuf; ++ uri->object = object; ++ uri->pin = pin; ++ return uri; ++} ++ ++static void ++test_parse_valid(void) ++{ ++ /* path arguments */ ++ check_parse("pkcs11:id=%01", ++ compose_uri("\x01", 1, NULL, NULL, NULL, NULL, NULL, NULL)); ++ check_parse("pkcs11:id=%00%01", ++ compose_uri("\x00\x01", 2, NULL, NULL, NULL, NULL, NULL, NULL)); ++ check_parse("pkcs11:token=SSH%20Keys", ++ compose_uri(NULL, 0, "SSH Keys", NULL, NULL, NULL, NULL, NULL)); ++ check_parse("pkcs11:library-manufacturer=OpenSC", ++ compose_uri(NULL, 0, NULL, "OpenSC", NULL, NULL, NULL, NULL)); ++ check_parse("pkcs11:manufacturer=piv_II", ++ compose_uri(NULL, 0, NULL, NULL, "piv_II", NULL, NULL, NULL)); ++ check_parse("pkcs11:object=SIGN%20Key", ++ compose_uri(NULL, 0, NULL, NULL, NULL, NULL, "SIGN Key", NULL)); ++ /* query arguments */ ++ check_parse("pkcs11:?module-path=/usr/lib64/p11-kit-proxy.so", ++ compose_uri(NULL, 0, NULL, NULL, NULL, "/usr/lib64/p11-kit-proxy.so", NULL, NULL)); ++ check_parse("pkcs11:?pin-value=123456", ++ compose_uri(NULL, 0, NULL, NULL, NULL, NULL, NULL, "123456")); ++ ++ /* combinations */ ++ /* ID SHOULD be percent encoded */ ++ check_parse("pkcs11:token=SSH%20Key;id=0", ++ compose_uri("0", 1, "SSH Key", NULL, NULL, NULL, NULL, NULL)); ++ check_parse( ++ "pkcs11:manufacturer=CAC?module-path=/usr/lib64/p11-kit-proxy.so", ++ compose_uri(NULL, 0, NULL, NULL, "CAC", ++ "/usr/lib64/p11-kit-proxy.so", NULL, NULL)); ++ check_parse( ++ "pkcs11:object=RSA%20Key?module-path=/usr/lib64/pkcs11/opencryptoki.so", ++ compose_uri(NULL, 0, NULL, NULL, NULL, ++ "/usr/lib64/pkcs11/opencryptoki.so", "RSA Key", NULL)); ++ check_parse("pkcs11:?module-path=/usr/lib64/p11-kit-proxy.so&pin-value=123456", ++ compose_uri(NULL, 0, NULL, NULL, NULL, "/usr/lib64/p11-kit-proxy.so", NULL, "123456")); ++ ++ /* empty path component matches everything */ ++ check_parse("pkcs11:", EMPTY_URI); ++ ++ /* empty string is a valid to match against (and different from NULL) */ ++ check_parse("pkcs11:token=", ++ compose_uri(NULL, 0, "", NULL, NULL, NULL, NULL, NULL)); ++ /* Percent character needs to be percent-encoded */ ++ check_parse("pkcs11:token=%25", ++ compose_uri(NULL, 0, "%", NULL, NULL, NULL, NULL, NULL)); ++} ++ ++static void ++test_parse_invalid(void) ++{ ++ /* Invalid percent encoding */ ++ check_parse_rv("pkcs11:id=%0", EMPTY_URI, -1); ++ /* Invalid percent encoding */ ++ check_parse_rv("pkcs11:id=%ZZ", EMPTY_URI, -1); ++ /* Space MUST be percent encoded -- XXX not enforced yet */ ++ check_parse("pkcs11:token=SSH Keys", ++ compose_uri(NULL, 0, "SSH Keys", NULL, NULL, NULL, NULL, NULL)); ++ /* MUST NOT contain duplicate attributes of the same name */ ++ check_parse_rv("pkcs11:id=%01;id=%02", EMPTY_URI, -1); ++ /* MUST NOT contain duplicate attributes of the same name */ ++ check_parse_rv("pkcs11:?pin-value=111111&pin-value=123456", EMPTY_URI, -1); ++ /* Unrecognized attribute in path are ignored with log message */ ++ check_parse("pkcs11:key_name=SSH", EMPTY_URI); ++ /* Unrecognized attribute in query SHOULD be ignored */ ++ check_parse("pkcs11:?key_name=SSH", EMPTY_URI); ++} ++ ++void ++check_gen(char *expect, struct pkcs11_uri *uri) ++{ ++ char *buf = NULL, *uri_str; ++ ++ asprintf(&buf, "Valid PKCS#11 URI generation: %s", expect); ++ TEST_START(buf); ++ free(buf); ++ uri_str = pkcs11_uri_get(uri); ++ ASSERT_PTR_NE(uri_str, NULL); ++ ASSERT_STRING_EQ(uri_str, expect); ++ free(uri_str); ++ TEST_DONE(); ++} ++ ++static void ++test_generate_valid(void) ++{ ++ /* path arguments */ ++ check_gen("pkcs11:id=%01", ++ compose_uri("\x01", 1, NULL, NULL, NULL, NULL, NULL, NULL)); ++ check_gen("pkcs11:id=%00%01", ++ compose_uri("\x00\x01", 2, NULL, NULL, NULL, NULL, NULL, NULL)); ++ check_gen("pkcs11:token=SSH%20Keys", /* space must be percent encoded */ ++ compose_uri(NULL, 0, "SSH Keys", NULL, NULL, NULL, NULL, NULL)); ++ /* library-manufacturer is not implmented now */ ++ /*check_gen("pkcs11:library-manufacturer=OpenSC", ++ compose_uri(NULL, 0, NULL, "OpenSC", NULL, NULL, NULL, NULL));*/ ++ check_gen("pkcs11:manufacturer=piv_II", ++ compose_uri(NULL, 0, NULL, NULL, "piv_II", NULL, NULL, NULL)); ++ check_gen("pkcs11:object=RSA%20Key", ++ compose_uri(NULL, 0, NULL, NULL, NULL, NULL, "RSA Key", NULL)); ++ /* query arguments */ ++ check_gen("pkcs11:?module-path=/usr/lib64/p11-kit-proxy.so", ++ compose_uri(NULL, 0, NULL, NULL, NULL, "/usr/lib64/p11-kit-proxy.so", NULL, NULL)); ++ ++ /* combinations */ ++ check_gen("pkcs11:id=%02;token=SSH%20Keys", ++ compose_uri("\x02", 1, "SSH Keys", NULL, NULL, NULL, NULL, NULL)); ++ check_gen("pkcs11:id=%EE%02?module-path=/usr/lib64/p11-kit-proxy.so", ++ compose_uri("\xEE\x02", 2, NULL, NULL, NULL, "/usr/lib64/p11-kit-proxy.so", NULL, NULL)); ++ check_gen("pkcs11:object=Encryption%20Key;manufacturer=piv_II", ++ compose_uri(NULL, 0, NULL, NULL, "piv_II", NULL, "Encryption Key", NULL)); ++ ++ /* empty path component matches everything */ ++ check_gen("pkcs11:", EMPTY_URI); ++ ++} ++ ++void ++check_encode(char *source, size_t len, char *whitelist, char *expect) ++{ ++ char *buf = NULL; ++ struct sshbuf *b; ++ ++ asprintf(&buf, "percent_encode: expected %s", expect); ++ TEST_START(buf); ++ free(buf); ++ ++ b = percent_encode(source, len, whitelist); ++ ASSERT_STRING_EQ(sshbuf_ptr(b), expect); ++ sshbuf_free(b); ++ TEST_DONE(); ++} ++ ++static void ++test_percent_encode_multibyte(void) ++{ ++ /* SHOULD be encoded as octets according to the UTF-8 character encoding */ ++ ++ /* multi-byte characters are "for free" */ ++ check_encode("$", 1, "", "%24"); ++ check_encode("¢", 2, "", "%C2%A2"); ++ check_encode("€", 3, "", "%E2%82%AC"); ++ check_encode("𐍈", 4, "", "%F0%90%8D%88"); ++ ++ /* CK_UTF8CHAR is unsigned char (1 byte) */ ++ /* labels SHOULD be normalized to NFC [UAX15] */ ++ ++} ++ ++static void ++test_percent_encode(void) ++{ ++ /* Without whitelist encodes everything (for CKA_ID) */ ++ check_encode("A*", 2, "", "%41%2A"); ++ check_encode("\x00", 1, "", "%00"); ++ check_encode("\x7F", 1, "", "%7F"); ++ check_encode("\x80", 1, "", "%80"); ++ check_encode("\xff", 1, "", "%FF"); ++ ++ /* Default whitelist encodes anything but safe letters */ ++ check_encode("test" "\x00" "0alpha", 11, PKCS11_URI_WHITELIST, ++ "test%000alpha"); ++ check_encode(" ", 1, PKCS11_URI_WHITELIST, ++ "%20"); /* Space MUST be percent encoded */ ++ check_encode("/", 1, PKCS11_URI_WHITELIST, ++ "%2F"); /* '/' delimiter MUST be percent encoded (in the path) */ ++ check_encode("?", 1, PKCS11_URI_WHITELIST, ++ "%3F"); /* delimiter '?' MUST be percent encoded (in the path) */ ++ check_encode("#", 1, PKCS11_URI_WHITELIST, ++ "%23"); /* '#' MUST be always percent encoded */ ++ check_encode("key=value;separator?query&#anch", 35, PKCS11_URI_WHITELIST, ++ "key%3Dvalue%3Bseparator%3Fquery%26amp%3B%23anch"); ++ ++ /* Components in query can have '/' unencoded (useful for paths) */ ++ check_encode("/path/to.file", 13, PKCS11_URI_WHITELIST "/", ++ "/path/to.file"); ++} ++ ++void ++check_decode(char *source, char *expect, int expect_len) ++{ ++ char *buf = NULL, *out = NULL; ++ int rv; ++ ++ asprintf(&buf, "percent_decode: %s", source); ++ TEST_START(buf); ++ free(buf); ++ ++ rv = percent_decode(source, &out); ++ ASSERT_INT_EQ(rv, expect_len); ++ if (rv >= 0) ++ ASSERT_MEM_EQ(out, expect, expect_len); ++ free(out); ++ TEST_DONE(); ++} ++ ++static void ++test_percent_decode(void) ++{ ++ /* simple valid cases */ ++ check_decode("%00", "\x00", 1); ++ check_decode("%FF", "\xFF", 1); ++ ++ /* normal strings shold be kept intact */ ++ check_decode("strings are left", "strings are left", 16); ++ check_decode("10%25 of trees", "10% of trees", 12); ++ ++ /* make sure no more than 2 bytes are parsed */ ++ check_decode("%222", "\x22" "2", 2); ++ ++ /* invalid expects failure */ ++ check_decode("%0", "", -1); ++ check_decode("%Z", "", -1); ++ check_decode("%FG", "", -1); ++} ++ ++void ++tests(void) ++{ ++ test_percent_encode(); ++ test_percent_encode_multibyte(); ++ test_percent_decode(); ++ test_parse_valid(); ++ test_parse_invalid(); ++ test_generate_valid(); ++} +diff --git a/ssh-add.c b/ssh-add.c +index ac9c808d..f039e00e 100644 +--- a/ssh-add.c ++++ b/ssh-add.c +@@ -64,6 +64,7 @@ + #include "misc.h" + #include "ssherr.h" + #include "digest.h" ++#include "ssh-pkcs11-uri.h" + + /* argv0 */ + extern char *__progname; +@@ -188,6 +189,24 @@ delete_all(int agent_fd, int qflag) + return ret; + } + ++#ifdef ENABLE_PKCS11 ++static int update_card(int, int, const char *, int); ++ ++int ++update_pkcs11_uri(int agent_fd, int adding, const char *pkcs11_uri, int qflag) ++{ ++ struct pkcs11_uri *uri; ++ ++ /* dry-run parse to make sure the URI is valid and to report errors */ ++ uri = pkcs11_uri_init(); ++ if (pkcs11_uri_parse((char *) pkcs11_uri, uri) != 0) ++ fatal("Failed to parse PKCS#11 URI"); ++ pkcs11_uri_cleanup(uri); ++ ++ return update_card(agent_fd, adding, pkcs11_uri, qflag); ++} ++#endif ++ + static int + add_file(int agent_fd, const char *filename, int key_only, int qflag) + { +@@ -529,6 +548,13 @@ lock_agent(int agent_fd, int lock) + static int + do_file(int agent_fd, int deleting, int key_only, char *file, int qflag) + { ++#ifdef ENABLE_PKCS11 ++ if (strlen(file) >= strlen(PKCS11_URI_SCHEME) && ++ strncmp(file, PKCS11_URI_SCHEME, ++ strlen(PKCS11_URI_SCHEME)) == 0) { ++ return update_pkcs11_uri(agent_fd, !deleting, file, qflag); ++ } ++#endif + if (deleting) { + if (delete_file(agent_fd, file, key_only, qflag) == -1) + return -1; +diff --git a/ssh-agent.c b/ssh-agent.c +index d06ecfd9..9c1b328f 100644 +--- a/ssh-agent.c ++++ b/ssh-agent.c +@@ -548,10 +548,72 @@ no_identities(SocketEntry *e) + } + + #ifdef ENABLE_PKCS11 ++static char * ++sanitize_pkcs11_provider(const char *provider) ++{ ++ struct pkcs11_uri *uri = NULL; ++ char *sane_uri, *module_path = NULL; /* default path */ ++ char canonical_provider[PATH_MAX]; ++ ++ if (provider == NULL) ++ return NULL; ++ ++ if (strlen(provider) >= strlen(PKCS11_URI_SCHEME) && ++ strncmp(provider, PKCS11_URI_SCHEME, ++ strlen(PKCS11_URI_SCHEME)) == 0) { ++ /* PKCS#11 URI */ ++ uri = pkcs11_uri_init(); ++ if (uri == NULL) { ++ error("Failed to init PCKS#11 URI"); ++ return NULL; ++ } ++ ++ if (pkcs11_uri_parse(provider, uri) != 0) { ++ error("Failed to parse PKCS#11 URI"); ++ return NULL; ++ } ++ /* validate also provider from URI */ ++ if (uri->module_path) ++ module_path = strdup(uri->module_path); ++ } else ++ module_path = strdup(provider); /* simple path */ ++ ++ if (module_path != NULL) { /* do not validate default NULL path in URI */ ++ if (realpath(module_path, canonical_provider) == NULL) { ++ verbose("failed PKCS#11 provider \"%.100s\": realpath: %s", ++ module_path, strerror(errno)); ++ free(module_path); ++ pkcs11_uri_cleanup(uri); ++ return NULL; ++ } ++ free(module_path); ++ if (match_pattern_list(canonical_provider, pkcs11_whitelist, 0) != 1) { ++ verbose("refusing PKCS#11 provider \"%.100s\": " ++ "not whitelisted", canonical_provider); ++ pkcs11_uri_cleanup(uri); ++ return NULL; ++ } ++ ++ /* copy verified and sanitized provider path back to the uri */ ++ if (uri) { ++ free(uri->module_path); ++ uri->module_path = xstrdup(canonical_provider); ++ } ++ } ++ ++ if (uri) { ++ sane_uri = pkcs11_uri_get(uri); ++ pkcs11_uri_cleanup(uri); ++ return sane_uri; ++ } else { ++ return xstrdup(canonical_provider); /* simple path */ ++ } ++} ++ + static void + process_add_smartcard_key(SocketEntry *e) + { +- char *provider = NULL, *pin = NULL, canonical_provider[PATH_MAX]; ++ char *provider = NULL, *pin = NULL, *sane_uri = NULL; + int r, i, count = 0, success = 0, confirm = 0; + u_int seconds; + time_t death = 0; +@@ -587,28 +649,23 @@ process_add_smartcard_key(SocketEntry *e) + goto send; + } + } +- if (realpath(provider, canonical_provider) == NULL) { +- verbose("failed PKCS#11 add of \"%.100s\": realpath: %s", +- provider, strerror(errno)); +- goto send; +- } +- if (match_pattern_list(canonical_provider, pkcs11_whitelist, 0) != 1) { +- verbose("refusing PKCS#11 add of \"%.100s\": " +- "provider not whitelisted", canonical_provider); ++ ++ sane_uri = sanitize_pkcs11_provider(provider); ++ if (sane_uri == NULL) + goto send; +- } +- debug("%s: add %.100s", __func__, canonical_provider); ++ + if (lifetime && !death) + death = monotime() + lifetime; + +- count = pkcs11_add_provider(canonical_provider, pin, &keys); ++ debug("%s: add %.100s", __func__, sane_uri); ++ count = pkcs11_add_provider(sane_uri, pin, &keys); + for (i = 0; i < count; i++) { + k = keys[i]; + if (lookup_identity(k) == NULL) { + id = xcalloc(1, sizeof(Identity)); + id->key = k; +- id->provider = xstrdup(canonical_provider); +- id->comment = xstrdup(canonical_provider); /* XXX */ ++ id->provider = xstrdup(sane_uri); ++ id->comment = xstrdup(sane_uri); + id->death = death; + id->confirm = confirm; + TAILQ_INSERT_TAIL(&idtab->idlist, id, next); +@@ -622,6 +679,7 @@ process_add_smartcard_key(SocketEntry *e) + send: + free(pin); + free(provider); ++ free(sane_uri); + free(keys); + send_status(e, success); + } +@@ -629,7 +687,7 @@ send: + static void + process_remove_smartcard_key(SocketEntry *e) + { +- char *provider = NULL, *pin = NULL, canonical_provider[PATH_MAX]; ++ char *provider = NULL, *pin = NULL, *sane_uri = NULL; + int r, success = 0; + Identity *id, *nxt; + +@@ -640,30 +698,29 @@ process_remove_smartcard_key(SocketEntry *e) + } + free(pin); + +- if (realpath(provider, canonical_provider) == NULL) { +- verbose("failed PKCS#11 add of \"%.100s\": realpath: %s", +- provider, strerror(errno)); ++ sane_uri = sanitize_pkcs11_provider(provider); ++ if (sane_uri == NULL) + goto send; +- } + +- debug("%s: remove %.100s", __func__, canonical_provider); ++ debug("%s: remove %.100s", __func__, sane_uri); + for (id = TAILQ_FIRST(&idtab->idlist); id; id = nxt) { + nxt = TAILQ_NEXT(id, next); + /* Skip file--based keys */ + if (id->provider == NULL) + continue; +- if (!strcmp(canonical_provider, id->provider)) { ++ if (!strcmp(sane_uri, id->provider)) { + TAILQ_REMOVE(&idtab->idlist, id, next); + free_identity(id); + idtab->nentries--; + } + } +- if (pkcs11_del_provider(canonical_provider) == 0) ++ if (pkcs11_del_provider(sane_uri) == 0) + success = 1; + else + error("%s: pkcs11_del_provider failed", __func__); + send: + free(provider); ++ free(sane_uri); + send_status(e, success); + } + #endif /* ENABLE_PKCS11 */ +diff --git a/ssh-keygen.c b/ssh-keygen.c +index 3898b281..91c43b14 100644 +--- a/ssh-keygen.c ++++ b/ssh-keygen.c +@@ -798,6 +798,7 @@ do_download(struct passwd *pw) + free(fp); + } else { + (void) sshkey_write(keys[i], stdout); /* XXX check */ ++ (void) pkcs11_uri_write(keys[i], stdout); + fprintf(stdout, "\n"); + } + sshkey_free(keys[i]); +diff --git a/ssh-pkcs11-client.c b/ssh-pkcs11-client.c +index e7860de8..7b2a9115 100644 +--- a/ssh-pkcs11-client.c ++++ b/ssh-pkcs11-client.c +@@ -321,6 +321,8 @@ pkcs11_add_provider(char *name, char *pin, struct sshkey ***keysp) + u_int nkeys, i; + struct sshbuf *msg; + ++ debug("%s: called, name = %s", __func__, name); ++ + if (fd < 0 && pkcs11_start_helper() < 0) + return (-1); + +@@ -338,6 +340,7 @@ pkcs11_add_provider(char *name, char *pin, struct sshkey ***keysp) + if ((r = sshbuf_get_u32(msg, &nkeys)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + *keysp = xcalloc(nkeys, sizeof(struct sshkey *)); ++ debug("%s: nkeys = %u", __func__, nkeys); + for (i = 0; i < nkeys; i++) { + /* XXX clean up properly instead of fatal() */ + if ((r = sshbuf_get_string(msg, &blob, &blen)) != 0 || +diff --git a/ssh-pkcs11-uri.c b/ssh-pkcs11-uri.c +new file mode 100644 +index 00000000..e1a7b4e0 +--- /dev/null ++++ b/ssh-pkcs11-uri.c +@@ -0,0 +1,425 @@ ++/* ++ * Copyright (c) 2017 Red Hat ++ * ++ * Authors: Jakub Jelen ++ * ++ * 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" ++ ++#ifdef ENABLE_PKCS11 ++ ++#include ++#include ++ ++#include "sshkey.h" ++#include "sshbuf.h" ++#include "log.h" ++ ++#define CRYPTOKI_COMPAT ++#include "pkcs11.h" ++ ++#include "ssh-pkcs11-uri.h" ++ ++#define PKCS11_URI_PATH_SEPARATOR ";" ++#define PKCS11_URI_QUERY_SEPARATOR "&" ++#define PKCS11_URI_VALUE_SEPARATOR "=" ++#define PKCS11_URI_ID "id" ++#define PKCS11_URI_TOKEN "token" ++#define PKCS11_URI_OBJECT "object" ++#define PKCS11_URI_LIB_MANUF "library-manufacturer" ++#define PKCS11_URI_MANUF "manufacturer" ++#define PKCS11_URI_MODULE_PATH "module-path" ++#define PKCS11_URI_PIN_VALUE "pin-value" ++ ++/* Keyword tokens. */ ++typedef enum { ++ pId, pToken, pObject, pLibraryManufacturer, pManufacturer, pModulePath, ++ pPinValue, pBadOption ++} pkcs11uriOpCodes; ++ ++/* Textual representation of the tokens. */ ++static struct { ++ const char *name; ++ pkcs11uriOpCodes opcode; ++} keywords[] = { ++ { PKCS11_URI_ID, pId }, ++ { PKCS11_URI_TOKEN, pToken }, ++ { PKCS11_URI_OBJECT, pObject }, ++ { PKCS11_URI_LIB_MANUF, pLibraryManufacturer }, ++ { PKCS11_URI_MANUF, pManufacturer }, ++ { PKCS11_URI_MODULE_PATH, pModulePath }, ++ { PKCS11_URI_PIN_VALUE, pPinValue }, ++ { NULL, pBadOption } ++}; ++ ++static pkcs11uriOpCodes ++parse_token(const char *cp) ++{ ++ u_int i; ++ ++ for (i = 0; keywords[i].name; i++) ++ if (strncasecmp(cp, keywords[i].name, ++ strlen(keywords[i].name)) == 0) ++ return keywords[i].opcode; ++ ++ return pBadOption; ++} ++ ++int ++percent_decode(char *data, char **outp) ++{ ++ char tmp[3]; ++ char *out, *tmp_end; ++ char *p = data; ++ long value; ++ size_t outlen = 0; ++ ++ out = malloc(strlen(data)+1); /* upper bound */ ++ if (out == NULL) ++ return -1; ++ while (*p != '\0') { ++ switch (*p) { ++ case '%': ++ p++; ++ if (*p == '\0') ++ goto fail; ++ tmp[0] = *p++; ++ if (*p == '\0') ++ goto fail; ++ tmp[1] = *p++; ++ tmp[2] = '\0'; ++ tmp_end = NULL; ++ value = strtol(tmp, &tmp_end, 16); ++ if (tmp_end != tmp+2) ++ goto fail; ++ else ++ out[outlen++] = (char) value; ++ break; ++ default: ++ out[outlen++] = *p++; ++ break; ++ } ++ } ++ ++ /* zero terminate */ ++ out[outlen] = '\0'; ++ *outp = out; ++ return outlen; ++fail: ++ free(out); ++ return -1; ++} ++ ++struct sshbuf * ++percent_encode(const char *data, size_t length, const char *whitelist) ++{ ++ struct sshbuf *b = NULL; ++ char tmp[4], *cp; ++ size_t i; ++ ++ if ((b = sshbuf_new()) == NULL) ++ return NULL; ++ for (i = 0; i < length; i++) { ++ cp = strchr(whitelist, data[i]); ++ /* if c is specified as '\0' pointer to terminator is returned !! */ ++ if (cp != NULL && *cp != '\0') { ++ if (sshbuf_put(b, &data[i], 1) != 0) ++ goto err; ++ } else ++ if (snprintf(tmp, 4, "%%%02X", (unsigned char) data[i]) < 3 ++ || sshbuf_put(b, tmp, 3) != 0) ++ goto err; ++ } ++ if (sshbuf_put(b, "\0", 1) == 0) ++ return b; ++err: ++ sshbuf_free(b); ++ return NULL; ++} ++ ++char * ++pkcs11_uri_append(char *part, const char *separator, const char *key, ++ struct sshbuf *value) ++{ ++ char *new_part; ++ size_t size = 0; ++ ++ if (value == NULL) ++ return NULL; ++ ++ size = asprintf(&new_part, ++ "%s%s%s" PKCS11_URI_VALUE_SEPARATOR "%s", ++ (part != NULL ? part : ""), ++ (part != NULL ? separator : ""), ++ key, sshbuf_ptr(value)); ++ sshbuf_free(value); ++ free(part); ++ ++ if (size <= 0) ++ return NULL; ++ return new_part; ++} ++ ++char * ++pkcs11_uri_get(struct pkcs11_uri *uri) ++{ ++ size_t size = 0; ++ char *p = NULL, *path = NULL, *query = NULL; ++ ++ /* compose a percent-encoded ID */ ++ if (uri->id_len > 0) { ++ struct sshbuf *key_id = percent_encode(uri->id, uri->id_len, ""); ++ path = pkcs11_uri_append(path, PKCS11_URI_PATH_SEPARATOR, ++ PKCS11_URI_ID, key_id); ++ if (path == NULL) ++ goto err; ++ } ++ ++ /* Write object label */ ++ if (uri->object) { ++ struct sshbuf *label = percent_encode(uri->object, strlen(uri->object), ++ PKCS11_URI_WHITELIST); ++ path = pkcs11_uri_append(path, PKCS11_URI_PATH_SEPARATOR, ++ PKCS11_URI_OBJECT, label); ++ if (path == NULL) ++ goto err; ++ } ++ ++ /* Write token label */ ++ if (uri->token) { ++ struct sshbuf *label = percent_encode(uri->token, strlen(uri->token), ++ PKCS11_URI_WHITELIST); ++ path = pkcs11_uri_append(path, PKCS11_URI_PATH_SEPARATOR, ++ PKCS11_URI_TOKEN, label); ++ if (path == NULL) ++ goto err; ++ } ++ ++ /* Write manufacturer */ ++ if (uri->manuf) { ++ struct sshbuf *manuf = percent_encode(uri->manuf, ++ strlen(uri->manuf), PKCS11_URI_WHITELIST); ++ path = pkcs11_uri_append(path, PKCS11_URI_PATH_SEPARATOR, ++ PKCS11_URI_MANUF, manuf); ++ if (path == NULL) ++ goto err; ++ } ++ ++ /* Write module_path */ ++ if (uri->module_path) { ++ struct sshbuf *module = percent_encode(uri->module_path, ++ strlen(uri->module_path), PKCS11_URI_WHITELIST "/"); ++ query = pkcs11_uri_append(query, PKCS11_URI_QUERY_SEPARATOR, ++ PKCS11_URI_MODULE_PATH, module); ++ if (query == NULL) ++ goto err; ++ } ++ ++ size = asprintf(&p, PKCS11_URI_SCHEME "%s%s%s", ++ path != NULL ? path : "", ++ query != NULL ? "?" : "", ++ query != NULL ? query : ""); ++err: ++ free(query); ++ free(path); ++ if (size <= 0) ++ return NULL; ++ return p; ++} ++ ++struct pkcs11_uri * ++pkcs11_uri_init() ++{ ++ struct pkcs11_uri *d = calloc(1, sizeof(struct pkcs11_uri)); ++ return d; ++} ++ ++void ++pkcs11_uri_cleanup(struct pkcs11_uri *pkcs11) ++{ ++ if (pkcs11 == NULL) { ++ return; ++ } ++ ++ free(pkcs11->id); ++ free(pkcs11->module_path); ++ free(pkcs11->token); ++ free(pkcs11->object); ++ free(pkcs11->lib_manuf); ++ free(pkcs11->manuf); ++ if (pkcs11->pin) ++ freezero(pkcs11->pin, strlen(pkcs11->pin)); ++ free(pkcs11); ++} ++ ++int ++pkcs11_uri_parse(const char *uri, struct pkcs11_uri *pkcs11) ++{ ++ char *saveptr1, *saveptr2, *str1, *str2, *tok; ++ int rv = 0, len; ++ char *p = NULL; ++ ++ size_t scheme_len = strlen(PKCS11_URI_SCHEME); ++ if (strlen(uri) < scheme_len || /* empty URI matches everything */ ++ strncmp(uri, PKCS11_URI_SCHEME, scheme_len) != 0) { ++ error("%s: The '%s' does not look like PKCS#11 URI", ++ __func__, uri); ++ return -1; ++ } ++ ++ if (pkcs11 == NULL) { ++ error("%s: Bad arguments. The pkcs11 can't be null", __func__); ++ return -1; ++ } ++ ++ /* skip URI schema name */ ++ p = strdup(uri); ++ str1 = p; ++ ++ /* everything before ? */ ++ tok = strtok_r(str1, "?", &saveptr1); ++ if (tok == NULL) { ++ error("%s: pk11-path expected, got EOF", __func__); ++ rv = -1; ++ goto out; ++ } ++ ++ /* skip URI schema name: ++ * the scheme ensures that there is at least something before "?" ++ * allowing empty pk11-path. Resulting token at worst pointing to ++ * \0 byte */ ++ tok = tok + scheme_len; ++ ++ /* parse pk11-path */ ++ for (str2 = tok; ; str2 = NULL) { ++ char **charptr, *arg = NULL; ++ pkcs11uriOpCodes opcode; ++ tok = strtok_r(str2, PKCS11_URI_PATH_SEPARATOR, &saveptr2); ++ if (tok == NULL) ++ break; ++ opcode = parse_token(tok); ++ if (opcode != pBadOption) ++ arg = tok + strlen(keywords[opcode].name) + 1; /* separator "=" */ ++ ++ switch (opcode) { ++ case pId: ++ /* CKA_ID */ ++ if (pkcs11->id != NULL) { ++ verbose("%s: The id already set in the PKCS#11 URI", ++ __func__); ++ rv = -1; ++ goto out; ++ } ++ len = percent_decode(arg, &pkcs11->id); ++ if (len <= 0) { ++ verbose("%s: Failed to percent-decode CKA_ID: %s", ++ __func__, arg); ++ rv = -1; ++ goto out; ++ } else ++ pkcs11->id_len = len; ++ debug3("%s: Setting CKA_ID = %s from PKCS#11 URI", ++ __func__, arg); ++ break; ++ case pToken: ++ /* CK_TOKEN_INFO -> label */ ++ charptr = &pkcs11->token; ++ parse_string: ++ if (*charptr != NULL) { ++ verbose("%s: The %s already set in the PKCS#11 URI", ++ keywords[opcode].name, __func__); ++ rv = -1; ++ goto out; ++ } ++ percent_decode(arg, charptr); ++ debug3("%s: Setting %s = %s from PKCS#11 URI", ++ __func__, keywords[opcode].name, *charptr); ++ break; ++ ++ case pObject: ++ /* CK_TOKEN_INFO -> manufacturerID */ ++ charptr = &pkcs11->object; ++ goto parse_string; ++ ++ case pManufacturer: ++ /* CK_TOKEN_INFO -> manufacturerID */ ++ charptr = &pkcs11->manuf; ++ goto parse_string; ++ ++ case pLibraryManufacturer: ++ /* CK_INFO -> manufacturerID */ ++ charptr = &pkcs11->lib_manuf; ++ goto parse_string; ++ ++ default: ++ /* Unrecognized attribute in the URI path SHOULD be error */ ++ verbose("%s: Unknown part of path in PKCS#11 URI: %s", ++ __func__, tok); ++ } ++ } ++ ++ tok = strtok_r(NULL, "?", &saveptr1); ++ if (tok == NULL) { ++ goto out; ++ } ++ /* parse pk11-query (optional) */ ++ for (str2 = tok; ; str2 = NULL) { ++ char *arg; ++ pkcs11uriOpCodes opcode; ++ tok = strtok_r(str2, PKCS11_URI_QUERY_SEPARATOR, &saveptr2); ++ if (tok == NULL) ++ break; ++ opcode = parse_token(tok); ++ if (opcode != pBadOption) ++ arg = tok + strlen(keywords[opcode].name) + 1; /* separator "=" */ ++ ++ switch (opcode) { ++ case pModulePath: ++ /* module-path is PKCS11Provider */ ++ if (pkcs11->module_path != NULL) { ++ verbose("%s: Multiple module-path attributes are" ++ "not supported the PKCS#11 URI", __func__); ++ rv = -1; ++ goto out; ++ } ++ percent_decode(arg, &pkcs11->module_path); ++ debug3("%s: Setting PKCS11Provider = %s from PKCS#11 URI", ++ __func__, pkcs11->module_path); ++ break; ++ ++ case pPinValue: ++ /* pin-value */ ++ if (pkcs11->pin != NULL) { ++ verbose("%s: Multiple pin-value attributes are" ++ "not supported the PKCS#11 URI", __func__); ++ rv = -1; ++ goto out; ++ } ++ percent_decode(arg, &pkcs11->pin); ++ debug3("%s: Setting PIN from PKCS#11 URI", __func__); ++ break; ++ ++ default: ++ /* Unrecognized attribute in the URI query SHOULD be ignored */ ++ verbose("%s: Unknown part of query in PKCS#11 URI: %s", ++ __func__, tok); ++ } ++ } ++out: ++ free(p); ++ return rv; ++} ++ ++#endif /* ENABLE_PKCS11 */ +diff --git a/ssh-pkcs11-uri.h b/ssh-pkcs11-uri.h +new file mode 100644 +index 00000000..942a5a5a +--- /dev/null ++++ b/ssh-pkcs11-uri.h +@@ -0,0 +1,42 @@ ++/* ++ * Copyright (c) 2017 Red Hat ++ * ++ * Authors: Jakub Jelen ++ * ++ * 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. ++ */ ++ ++#define PKCS11_URI_SCHEME "pkcs11:" ++#define PKCS11_URI_WHITELIST "abcdefghijklmnopqrstuvwxyz" \ ++ "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \ ++ "0123456789_-.()" ++ ++struct pkcs11_uri { ++ /* path */ ++ char *id; ++ size_t id_len; ++ char *token; ++ char *object; ++ char *lib_manuf; ++ char *manuf; ++ /* query */ ++ char *module_path; ++ char *pin; /* Only parsed, but not printed */ ++}; ++ ++struct pkcs11_uri *pkcs11_uri_init(); ++void pkcs11_uri_cleanup(struct pkcs11_uri *); ++int pkcs11_uri_parse(const char *, struct pkcs11_uri *); ++struct pkcs11_uri *pkcs11_uri_init(); ++char *pkcs11_uri_get(struct pkcs11_uri *uri); ++ +diff --git a/ssh-pkcs11.c b/ssh-pkcs11.c +index 70f06bff..59332945 100644 +--- a/ssh-pkcs11.c ++++ b/ssh-pkcs11.c +@@ -54,8 +54,8 @@ struct pkcs11_slotinfo { + int logged_in; + }; + +-struct pkcs11_provider { +- char *name; ++struct pkcs11_module { ++ char *module_path; + void *handle; + CK_FUNCTION_LIST *function_list; + CK_INFO info; +@@ -64,6 +64,13 @@ struct pkcs11_provider { + struct pkcs11_slotinfo *slotinfo; + int valid; + int refcount; ++}; ++ ++struct pkcs11_provider { ++ char *name; ++ struct pkcs11_module *module; /* can be shared between various providers */ ++ int refcount; ++ int valid; + TAILQ_ENTRY(pkcs11_provider) next; + }; + +@@ -74,6 +81,7 @@ struct pkcs11_key { + CK_ULONG slotidx; + char *keyid; + int keyid_len; ++ char *label; + }; + + int pkcs11_interactive = 0; +@@ -106,26 +114,63 @@ pkcs11_init(int interactive) + * this is called when a provider gets unregistered. + */ + static void +-pkcs11_provider_finalize(struct pkcs11_provider *p) ++pkcs11_module_finalize(struct pkcs11_module *m) + { + CK_RV rv; + CK_ULONG i; + +- debug("pkcs11_provider_finalize: %p refcount %d valid %d", +- p, p->refcount, p->valid); +- if (!p->valid) ++ debug("%s: %p refcount %d valid %d", __func__, ++ m, m->refcount, m->valid); ++ if (!m->valid) + return; +- for (i = 0; i < p->nslots; i++) { +- if (p->slotinfo[i].session && +- (rv = p->function_list->C_CloseSession( +- p->slotinfo[i].session)) != CKR_OK) ++ for (i = 0; i < m->nslots; i++) { ++ if (m->slotinfo[i].session && ++ (rv = m->function_list->C_CloseSession( ++ m->slotinfo[i].session)) != CKR_OK) + error("C_CloseSession failed: %lu", rv); + } +- if ((rv = p->function_list->C_Finalize(NULL)) != CKR_OK) ++ if ((rv = m->function_list->C_Finalize(NULL)) != CKR_OK) + error("C_Finalize failed: %lu", rv); ++ m->valid = 0; ++ m->function_list = NULL; ++ dlclose(m->handle); ++} ++ ++/* ++ * remove a reference to the pkcs11 module. ++ * called when a provider is unregistered. ++ */ ++static void ++pkcs11_module_unref(struct pkcs11_module *m) ++{ ++ debug("%s: %p refcount %d", __func__, m, m->refcount); ++ if (--m->refcount <= 0) { ++ pkcs11_module_finalize(m); ++ if (m->valid) ++ error("%s: %p still valid", __func__, m); ++ free(m->slotlist); ++ free(m->slotinfo); ++ free(m->module_path); ++ free(m); ++ } ++} ++ ++/* ++ * finalize a provider shared libarary, it's no longer usable. ++ * however, there might still be keys referencing this provider, ++ * so the actuall freeing of memory is handled by pkcs11_provider_unref(). ++ * this is called when a provider gets unregistered. ++ */ ++static void ++pkcs11_provider_finalize(struct pkcs11_provider *p) ++{ ++ debug("%s: %p refcount %d valid %d", __func__, ++ p, p->refcount, p->valid); ++ if (!p->valid) ++ return; ++ pkcs11_module_unref(p->module); ++ p->module = NULL; + p->valid = 0; +- p->function_list = NULL; +- dlclose(p->handle); + } + + /* +@@ -135,13 +180,11 @@ pkcs11_provider_finalize(struct pkcs11_provider *p) + static void + pkcs11_provider_unref(struct pkcs11_provider *p) + { +- debug("pkcs11_provider_unref: %p refcount %d", p, p->refcount); ++ debug("%s: %p refcount %d", __func__, p, p->refcount); + if (--p->refcount <= 0) { +- if (p->valid) +- error("pkcs11_provider_unref: %p still valid", p); + free(p->name); +- free(p->slotlist); +- free(p->slotinfo); ++ if (p->module) ++ pkcs11_module_unref(p->module); + free(p); + } + } +@@ -159,6 +202,20 @@ pkcs11_terminate(void) + } + } + ++/* lookup provider by module path */ ++static struct pkcs11_module * ++pkcs11_provider_lookup_module(char *module_path) ++{ ++ struct pkcs11_provider *p; ++ ++ TAILQ_FOREACH(p, &pkcs11_providers, next) { ++ debug("check %p %s (%s)", p, p->name, p->module->module_path); ++ if (!strcmp(module_path, p->module->module_path)) ++ return (p->module); ++ } ++ return (NULL); ++} ++ + /* lookup provider by name */ + static struct pkcs11_provider * + pkcs11_provider_lookup(char *provider_id) +@@ -173,19 +230,52 @@ pkcs11_provider_lookup(char *provider_id) + return (NULL); + } + ++int pkcs11_del_provider_by_uri(struct pkcs11_uri *); ++ + /* unregister provider by name */ + int + pkcs11_del_provider(char *provider_id) ++{ ++ int rv; ++ struct pkcs11_uri *uri; ++ ++ debug("%s: called, provider_id = %s", __func__, provider_id); ++ ++ uri = pkcs11_uri_init(); ++ if (uri == NULL) ++ fatal("Failed to init PCKS#11 URI"); ++ ++ if (strlen(provider_id) >= strlen(PKCS11_URI_SCHEME) && ++ strncmp(provider_id, PKCS11_URI_SCHEME, strlen(PKCS11_URI_SCHEME)) == 0) { ++ if (pkcs11_uri_parse(provider_id, uri) != 0) ++ fatal("Failed to parse PKCS#11 URI"); ++ } else { ++ uri->module_path = strdup(provider_id); ++ } ++ ++ rv = pkcs11_del_provider_by_uri(uri); ++ pkcs11_uri_cleanup(uri); ++ return rv; ++} ++ ++/* unregister provider by PKCS#11 URI */ ++int ++pkcs11_del_provider_by_uri(struct pkcs11_uri *uri) + { + struct pkcs11_provider *p; ++ int rv = -1; ++ char *provider_uri = pkcs11_uri_get(uri); + +- if ((p = pkcs11_provider_lookup(provider_id)) != NULL) { ++ debug3("%s(%s): called", __func__, provider_uri); ++ ++ if ((p = pkcs11_provider_lookup(provider_uri)) != NULL) { + TAILQ_REMOVE(&pkcs11_providers, p, next); + pkcs11_provider_finalize(p); + pkcs11_provider_unref(p); +- return (0); ++ rv = 0; + } +- return (-1); ++ free(provider_uri); ++ return rv; + } + + static RSA_METHOD *rsa_method; +@@ -195,6 +285,55 @@ static EC_KEY_METHOD *ec_key_method; + static int ec_key_idx = 0; + #endif + ++/* ++ * This can't be in the ssh-pkcs11-uri, becase we can not depend on ++ * PKCS#11 structures in ssh-agent (using client-helper communication) ++ */ ++int ++pkcs11_uri_write(const struct sshkey *key, FILE *f) ++{ ++ char *p = NULL; ++ struct pkcs11_uri uri; ++ struct pkcs11_key *k11; ++ ++ /* sanity - is it a RSA key with associated app_data? */ ++ switch (key->type) { ++ case KEY_RSA: ++ k11 = RSA_get_ex_data(key->rsa, rsa_idx); ++ break; ++#ifdef HAVE_EC_KEY_METHOD_NEW ++ case KEY_ECDSA: ++ k11 = EC_KEY_get_ex_data(key->ecdsa, ec_key_idx); ++ break; ++#endif ++ default: ++ error("Unknown key type %d", key->type); ++ return -1; ++ } ++ if (k11 == NULL) { ++ error("Failed to get ex_data for key type %d", key->type); ++ return (-1); ++ } ++ ++ /* omit type -- we are looking for private-public or private-certificate pairs */ ++ uri.id = k11->keyid; ++ uri.id_len = k11->keyid_len; ++ uri.token = k11->provider->module->slotinfo[k11->slotidx].token.label; ++ uri.object = k11->label; ++ uri.module_path = k11->provider->module->module_path; ++ uri.lib_manuf = k11->provider->module->info.manufacturerID; ++ uri.manuf = k11->provider->module->slotinfo[k11->slotidx].token.manufacturerID; ++ ++ p = pkcs11_uri_get(&uri); ++ /* do not cleanup -- we do not allocate here, only reference */ ++ if (p == NULL) ++ return -1; ++ ++ fprintf(f, " %s", p); ++ free(p); ++ return 0; ++} ++ + /* release a wrapped object */ + static void + pkcs11_k11_free(void *parent, void *ptr, CRYPTO_EX_DATA *ad, int idx, +@@ -208,6 +347,7 @@ pkcs11_k11_free(void *parent, void *ptr, CRYPTO_EX_DATA *ad, int idx, + if (k11->provider) + pkcs11_provider_unref(k11->provider); + free(k11->keyid); ++ free(k11->label); + free(k11); + } + +@@ -222,8 +362,8 @@ pkcs11_find(struct pkcs11_provider *p, CK_ULONG slotidx, CK_ATTRIBUTE *attr, + CK_RV rv; + int ret = -1; + +- f = p->function_list; +- session = p->slotinfo[slotidx].session; ++ f = p->module->function_list; ++ session = p->module->slotinfo[slotidx].session; + if ((rv = f->C_FindObjectsInit(session, attr, nattr)) != CKR_OK) { + error("C_FindObjectsInit failed (nattr %lu): %lu", nattr, rv); + return (-1); +@@ -252,8 +392,8 @@ pkcs11_login(struct pkcs11_key *k11, CK_USER_TYPE type) + return (-1); + } + +- f = k11->provider->function_list; +- si = &k11->provider->slotinfo[k11->slotidx]; ++ f = k11->provider->module->function_list; ++ si = &k11->provider->module->slotinfo[k11->slotidx]; + + if (!pkcs11_interactive) { + error("need pin entry%s", +@@ -300,8 +440,8 @@ pkcs11_check_obj_bool_attrib(struct pkcs11_key *k11, CK_OBJECT_HANDLE obj, + return (-1); + } + +- f = k11->provider->function_list; +- si = &k11->provider->slotinfo[k11->slotidx]; ++ f = k11->provider->module->function_list; ++ si = &k11->provider->module->slotinfo[k11->slotidx]; + + attr.type = type; + attr.pValue = &flag; +@@ -332,13 +472,14 @@ pkcs11_get_key(struct pkcs11_key *k11, CK_MECHANISM_TYPE mech_type) + int always_auth = 0; + int did_login = 0; + +- if (!k11->provider || !k11->provider->valid) { ++ if (!k11->provider || !k11->provider->valid || !k11->provider->module ++ || !k11->provider->module->valid) { + error("no pkcs11 (valid) provider found"); + return (-1); + } + +- f = k11->provider->function_list; +- si = &k11->provider->slotinfo[k11->slotidx]; ++ f = k11->provider->module->function_list; ++ si = &k11->provider->module->slotinfo[k11->slotidx]; + + if ((si->token.flags & CKF_LOGIN_REQUIRED) && !si->logged_in) { + if (pkcs11_login(k11, CKU_USER) < 0) { +@@ -415,8 +556,8 @@ pkcs11_rsa_private_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa, + return (-1); + } + +- f = k11->provider->function_list; +- si = &k11->provider->slotinfo[k11->slotidx]; ++ f = k11->provider->module->function_list; ++ si = &k11->provider->module->slotinfo[k11->slotidx]; + tlen = RSA_size(rsa); + + /* XXX handle CKR_BUFFER_TOO_SMALL */ +@@ -460,7 +601,7 @@ pkcs11_rsa_start_wrapper(void) + /* redirect private key operations for rsa key to pkcs11 token */ + static int + pkcs11_rsa_wrap(struct pkcs11_provider *provider, CK_ULONG slotidx, +- CK_ATTRIBUTE *keyid_attrib, RSA *rsa) ++ CK_ATTRIBUTE *keyid_attrib, CK_ATTRIBUTE *label_attrib, RSA *rsa) + { + struct pkcs11_key *k11; + +@@ -478,6 +619,12 @@ pkcs11_rsa_wrap(struct pkcs11_provider *provider, CK_ULONG slotidx, + memcpy(k11->keyid, keyid_attrib->pValue, k11->keyid_len); + } + ++ if (label_attrib->ulValueLen > 0 ) { ++ k11->label = xmalloc(label_attrib->ulValueLen+1); ++ memcpy(k11->label, label_attrib->pValue, label_attrib->ulValueLen); ++ k11->label[label_attrib->ulValueLen] = 0; ++ } ++ + RSA_set_method(rsa, rsa_method); + RSA_set_ex_data(rsa, rsa_idx, k11); + return (0); +@@ -508,8 +655,8 @@ ecdsa_do_sign(const unsigned char *dgst, int dgst_len, const BIGNUM *inv, + return (NULL); + } + +- f = k11->provider->function_list; +- si = &k11->provider->slotinfo[k11->slotidx]; ++ f = k11->provider->module->function_list; ++ si = &k11->provider->module->slotinfo[k11->slotidx]; + + siglen = ECDSA_size(ec); + sig = xmalloc(siglen); +@@ -574,7 +721,7 @@ pkcs11_ecdsa_start_wrapper(void) + + static int + pkcs11_ecdsa_wrap(struct pkcs11_provider *provider, CK_ULONG slotidx, +- CK_ATTRIBUTE *keyid_attrib, EC_KEY *ec) ++ CK_ATTRIBUTE *keyid_attrib, CK_ATTRIBUTE *label_attrib, EC_KEY *ec) + { + struct pkcs11_key *k11; + +@@ -590,6 +737,12 @@ pkcs11_ecdsa_wrap(struct pkcs11_provider *provider, CK_ULONG slotidx, + k11->keyid = xmalloc(k11->keyid_len); + memcpy(k11->keyid, keyid_attrib->pValue, k11->keyid_len); + ++ if (label_attrib->ulValueLen > 0 ) { ++ k11->label = xmalloc(label_attrib->ulValueLen+1); ++ memcpy(k11->label, label_attrib->pValue, label_attrib->ulValueLen); ++ k11->label[label_attrib->ulValueLen] = 0; ++ } ++ + EC_KEY_set_method(ec, ec_key_method); + EC_KEY_set_ex_data(ec, ec_key_idx, k11); + +@@ -624,47 +777,26 @@ pkcs11_open_session(struct pkcs11_provider *p, CK_ULONG slotidx, char *pin, + CK_FUNCTION_LIST *f; + CK_RV rv; + CK_SESSION_HANDLE session; +- int login_required, have_pinpad, ret; +- char prompt[1024], *xpin = NULL; ++ int login_required, ret; + +- f = p->function_list; +- si = &p->slotinfo[slotidx]; ++ f = p->module->function_list; ++ si = &p->module->slotinfo[slotidx]; + +- have_pinpad = si->token.flags & CKF_PROTECTED_AUTHENTICATION_PATH; + login_required = si->token.flags & CKF_LOGIN_REQUIRED; + + /* fail early before opening session */ +- if (login_required && !have_pinpad && !pkcs11_interactive && ++ if (login_required && !pkcs11_interactive && + (pin == NULL || strlen(pin) == 0)) { + error("pin required"); + return (-SSH_PKCS11_ERR_PIN_REQUIRED); + } +- if ((rv = f->C_OpenSession(p->slotlist[slotidx], CKF_RW_SESSION| ++ if ((rv = f->C_OpenSession(p->module->slotlist[slotidx], CKF_RW_SESSION| + CKF_SERIAL_SESSION, NULL, NULL, &session)) != CKR_OK) { +- error("C_OpenSession failed: %lu", rv); ++ error("C_OpenSession failed for slot %lu: %lu", slotidx, rv); + return (-1); + } +- if (login_required) { +- if (have_pinpad && (pin == NULL || strlen(pin) == 0)) { +- /* defer PIN entry to the reader keypad */ +- rv = f->C_Login(session, CKU_USER, NULL_PTR, 0); +- } else { +- if (pkcs11_interactive) { +- snprintf(prompt, sizeof(prompt), +- "Enter PIN for '%s': ", si->token.label); +- if ((xpin = read_passphrase(prompt, +- RP_ALLOW_EOF)) == NULL) { +- debug("%s: no pin specified", +- __func__); +- return (-SSH_PKCS11_ERR_PIN_REQUIRED); +- } +- pin = xpin; +- } +- rv = f->C_Login(session, CKU_USER, +- (u_char *)pin, strlen(pin)); +- if (xpin != NULL) +- freezero(xpin, strlen(xpin)); +- } ++ if (login_required && pin != NULL && strlen(pin) != 0) { ++ rv = f->C_Login(session, user, (u_char *)pin, strlen(pin)); + if (rv != CKR_OK && rv != CKR_USER_ALREADY_LOGGED_IN) { + error("C_Login failed: %lu", rv); + ret = (rv == CKR_PIN_LOCKED) ? +@@ -696,7 +828,8 @@ static struct sshkey * + pkcs11_fetch_ecdsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx, + CK_OBJECT_HANDLE *obj) + { +- CK_ATTRIBUTE key_attr[3]; ++ CK_ATTRIBUTE key_attr[4]; ++ int nattr = 4; + CK_SESSION_HANDLE session; + CK_FUNCTION_LIST *f = NULL; + CK_RV rv; +@@ -710,14 +843,15 @@ pkcs11_fetch_ecdsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx, + + memset(&key_attr, 0, sizeof(key_attr)); + key_attr[0].type = CKA_ID; +- key_attr[1].type = CKA_EC_POINT; +- key_attr[2].type = CKA_EC_PARAMS; ++ key_attr[1].type = CKA_LABEL; ++ key_attr[2].type = CKA_EC_POINT; ++ key_attr[3].type = CKA_EC_PARAMS; + +- session = p->slotinfo[slotidx].session; +- f = p->function_list; ++ session = p->module->slotinfo[slotidx].session; ++ f = p->module->function_list; + + /* figure out size of the attributes */ +- rv = f->C_GetAttributeValue(session, *obj, key_attr, 3); ++ rv = f->C_GetAttributeValue(session, *obj, key_attr, nattr); + if (rv != CKR_OK) { + error("C_GetAttributeValue failed: %lu", rv); + return (NULL); +@@ -730,19 +863,19 @@ pkcs11_fetch_ecdsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx, + * ensure that none of the others are zero length. + * XXX assumes CKA_ID is always first. + */ +- if (key_attr[1].ulValueLen == 0 || +- key_attr[2].ulValueLen == 0) { ++ if (key_attr[2].ulValueLen == 0 || ++ key_attr[3].ulValueLen == 0) { + error("invalid attribute length"); + return (NULL); + } + + /* allocate buffers for attributes */ +- for (i = 0; i < 3; i++) ++ for (i = 0; i < nattr; i++) + if (key_attr[i].ulValueLen > 0) + key_attr[i].pValue = xcalloc(1, key_attr[i].ulValueLen); + + /* retrieve ID, public point and curve parameters of EC key */ +- rv = f->C_GetAttributeValue(session, *obj, key_attr, 3); ++ rv = f->C_GetAttributeValue(session, *obj, key_attr, nattr); + if (rv != CKR_OK) { + error("C_GetAttributeValue failed: %lu", rv); + goto fail; +@@ -752,8 +887,8 @@ pkcs11_fetch_ecdsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx, + goto fail; + } + +- attrp = key_attr[2].pValue; +- group = d2i_ECPKParameters(NULL, &attrp, key_attr[2].ulValueLen); ++ attrp = key_attr[3].pValue; ++ group = d2i_ECPKParameters(NULL, &attrp, key_attr[3].ulValueLen); + if (group == NULL) { + ossl_error("d2i_ECPKParameters failed"); + goto fail; +@@ -764,13 +899,13 @@ pkcs11_fetch_ecdsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx, + goto fail; + } + +- if (key_attr[1].ulValueLen <= 2) { ++ if (key_attr[2].ulValueLen <= 2) { + error("CKA_EC_POINT too small"); + goto fail; + } + +- attrp = key_attr[1].pValue; +- octet = d2i_ASN1_OCTET_STRING(NULL, &attrp, key_attr[1].ulValueLen); ++ attrp = key_attr[2].pValue; ++ octet = d2i_ASN1_OCTET_STRING(NULL, &attrp, key_attr[2].ulValueLen); + if (octet == NULL) { + ossl_error("d2i_ASN1_OCTET_STRING failed"); + goto fail; +@@ -787,7 +922,7 @@ pkcs11_fetch_ecdsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx, + goto fail; + } + +- if (pkcs11_ecdsa_wrap(p, slotidx, &key_attr[0], ec)) ++ if (pkcs11_ecdsa_wrap(p, slotidx, &key_attr[0], &key_attr[1], ec)) + goto fail; + + key = sshkey_new(KEY_UNSPEC); +@@ -803,7 +938,7 @@ pkcs11_fetch_ecdsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx, + ec = NULL; /* now owned by key */ + + fail: +- for (i = 0; i < 3; i++) ++ for (i = 0; i < nattr; i++) + free(key_attr[i].pValue); + if (ec) + EC_KEY_free(ec); +@@ -820,7 +955,8 @@ static struct sshkey * + pkcs11_fetch_rsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx, + CK_OBJECT_HANDLE *obj) + { +- CK_ATTRIBUTE key_attr[3]; ++ CK_ATTRIBUTE key_attr[4]; ++ int nattr = 4; + CK_SESSION_HANDLE session; + CK_FUNCTION_LIST *f = NULL; + CK_RV rv; +@@ -831,14 +967,15 @@ pkcs11_fetch_rsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx, + + memset(&key_attr, 0, sizeof(key_attr)); + key_attr[0].type = CKA_ID; +- key_attr[1].type = CKA_MODULUS; +- key_attr[2].type = CKA_PUBLIC_EXPONENT; ++ key_attr[1].type = CKA_LABEL; ++ key_attr[2].type = CKA_MODULUS; ++ key_attr[3].type = CKA_PUBLIC_EXPONENT; + +- session = p->slotinfo[slotidx].session; +- f = p->function_list; ++ session = p->module->slotinfo[slotidx].session; ++ f = p->module->function_list; + + /* figure out size of the attributes */ +- rv = f->C_GetAttributeValue(session, *obj, key_attr, 3); ++ rv = f->C_GetAttributeValue(session, *obj, key_attr, nattr); + if (rv != CKR_OK) { + error("C_GetAttributeValue failed: %lu", rv); + return (NULL); +@@ -850,19 +987,19 @@ pkcs11_fetch_rsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx, + * ensure that none of the others are zero length. + * XXX assumes CKA_ID is always first. + */ +- if (key_attr[1].ulValueLen == 0 || +- key_attr[2].ulValueLen == 0) { ++ if (key_attr[2].ulValueLen == 0 || ++ key_attr[3].ulValueLen == 0) { + error("invalid attribute length"); + return (NULL); + } + + /* allocate buffers for attributes */ +- for (i = 0; i < 3; i++) ++ for (i = 0; i < nattr; i++) + if (key_attr[i].ulValueLen > 0) + key_attr[i].pValue = xcalloc(1, key_attr[i].ulValueLen); + + /* retrieve ID, modulus and public exponent of RSA key */ +- rv = f->C_GetAttributeValue(session, *obj, key_attr, 3); ++ rv = f->C_GetAttributeValue(session, *obj, key_attr, nattr); + if (rv != CKR_OK) { + error("C_GetAttributeValue failed: %lu", rv); + goto fail; +@@ -873,8 +1011,8 @@ pkcs11_fetch_rsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx, + goto fail; + } + +- rsa_n = BN_bin2bn(key_attr[1].pValue, key_attr[1].ulValueLen, NULL); +- rsa_e = BN_bin2bn(key_attr[2].pValue, key_attr[2].ulValueLen, NULL); ++ rsa_n = BN_bin2bn(key_attr[2].pValue, key_attr[2].ulValueLen, NULL); ++ rsa_e = BN_bin2bn(key_attr[3].pValue, key_attr[3].ulValueLen, NULL); + if (rsa_n == NULL || rsa_e == NULL) { + error("BN_bin2bn failed"); + goto fail; +@@ -883,7 +1021,7 @@ pkcs11_fetch_rsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx, + fatal("%s: set key", __func__); + rsa_n = rsa_e = NULL; /* transferred */ + +- if (pkcs11_rsa_wrap(p, slotidx, &key_attr[0], rsa)) ++ if (pkcs11_rsa_wrap(p, slotidx, &key_attr[0], &key_attr[1], rsa)) + goto fail; + + key = sshkey_new(KEY_UNSPEC); +@@ -898,7 +1036,7 @@ pkcs11_fetch_rsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx, + rsa = NULL; /* now owned by key */ + + fail: +- for (i = 0; i < 3; i++) ++ for (i = 0; i < nattr; i++) + free(key_attr[i].pValue); + RSA_free(rsa); + +@@ -909,7 +1047,8 @@ static struct sshkey * + pkcs11_fetch_x509_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx, + CK_OBJECT_HANDLE *obj) + { +- CK_ATTRIBUTE cert_attr[3]; ++ CK_ATTRIBUTE cert_attr[4]; ++ int nattr = 4; + CK_SESSION_HANDLE session; + CK_FUNCTION_LIST *f = NULL; + CK_RV rv; +@@ -926,14 +1065,15 @@ pkcs11_fetch_x509_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx, + + memset(&cert_attr, 0, sizeof(cert_attr)); + cert_attr[0].type = CKA_ID; +- cert_attr[1].type = CKA_SUBJECT; +- cert_attr[2].type = CKA_VALUE; ++ cert_attr[1].type = CKA_LABEL; ++ cert_attr[2].type = CKA_SUBJECT; ++ cert_attr[3].type = CKA_VALUE; + +- session = p->slotinfo[slotidx].session; +- f = p->function_list; ++ session = p->module->slotinfo[slotidx].session; ++ f = p->module->function_list; + + /* figure out size of the attributes */ +- rv = f->C_GetAttributeValue(session, *obj, cert_attr, 3); ++ rv = f->C_GetAttributeValue(session, *obj, cert_attr, nattr); + if (rv != CKR_OK) { + error("C_GetAttributeValue failed: %lu", rv); + return (NULL); +@@ -945,18 +1085,19 @@ pkcs11_fetch_x509_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx, + * XXX assumes CKA_ID is always first. + */ + if (cert_attr[1].ulValueLen == 0 || +- cert_attr[2].ulValueLen == 0) { ++ cert_attr[2].ulValueLen == 0 || ++ cert_attr[3].ulValueLen == 0) { + error("invalid attribute length"); + return (NULL); + } + + /* allocate buffers for attributes */ +- for (i = 0; i < 3; i++) ++ for (i = 0; i < nattr; i++) + if (cert_attr[i].ulValueLen > 0) + cert_attr[i].pValue = xcalloc(1, cert_attr[i].ulValueLen); + + /* retrieve ID, subject and value of certificate */ +- rv = f->C_GetAttributeValue(session, *obj, cert_attr, 3); ++ rv = f->C_GetAttributeValue(session, *obj, cert_attr, nattr); + if (rv != CKR_OK) { + error("C_GetAttributeValue failed: %lu", rv); + goto fail; +@@ -968,8 +1109,8 @@ pkcs11_fetch_x509_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx, + goto fail; + } + +- cp = cert_attr[2].pValue; +- if (d2i_X509(&x509, &cp, cert_attr[2].ulValueLen) == NULL) { ++ cp = cert_attr[3].pValue; ++ if (d2i_X509(&x509, &cp, cert_attr[3].ulValueLen) == NULL) { + error("d2i_x509 failed"); + goto fail; + } +@@ -990,7 +1131,7 @@ pkcs11_fetch_x509_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx, + goto fail; + } + +- if (pkcs11_rsa_wrap(p, slotidx, &cert_attr[0], rsa)) ++ if (pkcs11_rsa_wrap(p, slotidx, &cert_attr[0], &cert_attr[1], rsa)) + goto fail; + + key = sshkey_new(KEY_UNSPEC); +@@ -1020,7 +1161,7 @@ pkcs11_fetch_x509_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx, + goto fail; + } + +- if (pkcs11_ecdsa_wrap(p, slotidx, &cert_attr[0], ec)) ++ if (pkcs11_ecdsa_wrap(p, slotidx, &cert_attr[0], &cert_attr[1], ec)) + goto fail; + + key = sshkey_new(KEY_UNSPEC); +@@ -1039,7 +1180,7 @@ pkcs11_fetch_x509_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx, + error("unknown certificate key type"); + + fail: +- for (i = 0; i < 3; i++) ++ for (i = 0; i < nattr; i++) + free(cert_attr[i].pValue); + X509_free(x509); + RSA_free(rsa); +@@ -1066,11 +1207,12 @@ have_rsa_key(const RSA *rsa) + */ + static int + pkcs11_fetch_certs(struct pkcs11_provider *p, CK_ULONG slotidx, +- struct sshkey ***keysp, int *nkeys) ++ struct sshkey ***keysp, int *nkeys, struct pkcs11_uri *uri) + { + struct sshkey *key = NULL; + CK_OBJECT_CLASS key_class; +- CK_ATTRIBUTE key_attr[1]; ++ CK_ATTRIBUTE key_attr[3]; ++ int nattr = 1; + CK_SESSION_HANDLE session; + CK_FUNCTION_LIST *f = NULL; + CK_RV rv; +@@ -1086,10 +1228,23 @@ pkcs11_fetch_certs(struct pkcs11_provider *p, CK_ULONG slotidx, + key_attr[0].pValue = &key_class; + key_attr[0].ulValueLen = sizeof(key_class); + +- session = p->slotinfo[slotidx].session; +- f = p->function_list; ++ if (uri->id != NULL) { ++ key_attr[nattr].type = CKA_ID; ++ key_attr[nattr].pValue = uri->id; ++ key_attr[nattr].ulValueLen = uri->id_len; ++ nattr++; ++ } ++ if (uri->object != NULL) { ++ key_attr[nattr].type = CKA_LABEL; ++ key_attr[nattr].pValue = uri->object; ++ key_attr[nattr].ulValueLen = strlen(uri->object); ++ nattr++; ++ } + +- rv = f->C_FindObjectsInit(session, key_attr, 1); ++ session = p->module->slotinfo[slotidx].session; ++ f = p->module->function_list; ++ ++ rv = f->C_FindObjectsInit(session, key_attr, nattr); + if (rv != CKR_OK) { + error("C_FindObjectsInit failed: %lu", rv); + goto fail; +@@ -1163,11 +1318,12 @@ fail: + */ + static int + pkcs11_fetch_keys(struct pkcs11_provider *p, CK_ULONG slotidx, +- struct sshkey ***keysp, int *nkeys) ++ struct sshkey ***keysp, int *nkeys, struct pkcs11_uri *uri) + { + struct sshkey *key = NULL; + CK_OBJECT_CLASS key_class; +- CK_ATTRIBUTE key_attr[1]; ++ CK_ATTRIBUTE key_attr[3]; ++ int nattr = 1; + CK_SESSION_HANDLE session; + CK_FUNCTION_LIST *f = NULL; + CK_RV rv; +@@ -1183,10 +1339,23 @@ pkcs11_fetch_keys(struct pkcs11_provider *p, CK_ULONG slotidx, + key_attr[0].pValue = &key_class; + key_attr[0].ulValueLen = sizeof(key_class); + +- session = p->slotinfo[slotidx].session; +- f = p->function_list; ++ if (uri->id != NULL) { ++ key_attr[nattr].type = CKA_ID; ++ key_attr[nattr].pValue = uri->id; ++ key_attr[nattr].ulValueLen = uri->id_len; ++ nattr++; ++ } ++ if (uri->object != NULL) { ++ key_attr[nattr].type = CKA_LABEL; ++ key_attr[nattr].pValue = uri->object; ++ key_attr[nattr].ulValueLen = strlen(uri->object); ++ nattr++; ++ } + +- rv = f->C_FindObjectsInit(session, key_attr, 1); ++ session = p->module->slotinfo[slotidx].session; ++ f = p->module->function_list; ++ ++ rv = f->C_FindObjectsInit(session, key_attr, nattr); + if (rv != CKR_OK) { + error("C_FindObjectsInit failed: %lu", rv); + goto fail; +@@ -1443,15 +1612,10 @@ pkcs11_ecdsa_generate_private_key(struct pkcs11_provider *p, CK_ULONG slotidx, + } + #endif /* WITH_PKCS11_KEYGEN */ + +-/* +- * register a new provider, fails if provider already exists. if +- * keyp is provided, fetch keys. +- */ + static int +-pkcs11_register_provider(char *provider_id, char *pin, struct sshkey ***keyp, +- struct pkcs11_provider **providerp, CK_ULONG user) ++pkcs11_initialize_provider(struct pkcs11_uri *uri, struct pkcs11_provider **providerp) + { +- int nkeys, need_finalize = 0; ++ int need_finalize = 0; + int ret = -1; + struct pkcs11_provider *p = NULL; + void *handle = NULL; +@@ -1460,148 +1624,285 @@ pkcs11_register_provider(char *provider_id, char *pin, struct sshkey ***keyp, + CK_FUNCTION_LIST *f = NULL; + CK_TOKEN_INFO *token; + CK_ULONG i; +- +- if (providerp == NULL) ++ char *provider_module = NULL; ++ struct pkcs11_module *m = NULL; ++ ++ /* if no provider specified, fallback to p11-kit */ ++ if (uri->module_path == NULL) { ++#ifdef PKCS11_DEFAULT_PROVIDER ++ provider_module = strdup(PKCS11_DEFAULT_PROVIDER); ++#else ++ error("%s: No module path provided", __func__); + goto fail; +- *providerp = NULL; +- +- if (keyp != NULL) +- *keyp = NULL; ++#endif ++ } else { ++ provider_module = strdup(uri->module_path); ++ } + +- if (pkcs11_provider_lookup(provider_id) != NULL) { +- debug("%s: provider already registered: %s", +- __func__, provider_id); +- goto fail; ++ p = xcalloc(1, sizeof(*p)); ++ p->name = pkcs11_uri_get(uri); ++ ++ if ((m = pkcs11_provider_lookup_module(provider_module)) != NULL ++ && m->valid) { ++ debug("%s: provider module already initialized: %s", ++ __func__, provider_module); ++ free(provider_module); ++ /* Skip the initialization of PKCS#11 module */ ++ m->refcount++; ++ p->module = m; ++ p->valid = 1; ++ TAILQ_INSERT_TAIL(&pkcs11_providers, p, next); ++ p->refcount++; /* add to provider list */ ++ *providerp = p; ++ return 0; ++ } else { ++ m = xcalloc(1, sizeof(*m)); ++ p->module = m; ++ m->refcount++; + } ++ + /* open shared pkcs11-library */ +- if ((handle = dlopen(provider_id, RTLD_NOW)) == NULL) { +- error("dlopen %s failed: %s", provider_id, dlerror()); ++ if ((handle = dlopen(provider_module, RTLD_NOW)) == NULL) { ++ error("dlopen %s failed: %s", provider_module, dlerror()); + goto fail; + } + if ((getfunctionlist = dlsym(handle, "C_GetFunctionList")) == NULL) { + error("dlsym(C_GetFunctionList) failed: %s", dlerror()); + goto fail; + } +- p = xcalloc(1, sizeof(*p)); +- p->name = xstrdup(provider_id); +- p->handle = handle; ++ ++ p->module->handle = handle; + /* setup the pkcs11 callbacks */ + if ((rv = (*getfunctionlist)(&f)) != CKR_OK) { + error("C_GetFunctionList for provider %s failed: %lu", +- provider_id, rv); ++ provider_module, rv); + goto fail; + } +- p->function_list = f; ++ m->function_list = f; + if ((rv = f->C_Initialize(NULL)) != CKR_OK) { + error("C_Initialize for provider %s failed: %lu", +- provider_id, rv); ++ provider_module, rv); + goto fail; + } + need_finalize = 1; +- if ((rv = f->C_GetInfo(&p->info)) != CKR_OK) { ++ if ((rv = f->C_GetInfo(&m->info)) != CKR_OK) { + error("C_GetInfo for provider %s failed: %lu", +- provider_id, rv); ++ provider_module, rv); + goto fail; + } +- rmspace(p->info.manufacturerID, sizeof(p->info.manufacturerID)); +- rmspace(p->info.libraryDescription, sizeof(p->info.libraryDescription)); ++ rmspace(m->info.manufacturerID, sizeof(m->info.manufacturerID)); ++ if (uri->lib_manuf != NULL && ++ strcmp(uri->lib_manuf, m->info.manufacturerID)) { ++ debug("%s: Skipping provider %s not matching library_manufacturer", ++ __func__, m->info.manufacturerID); ++ goto fail; ++ } ++ rmspace(m->info.libraryDescription, sizeof(m->info.libraryDescription)); + debug("provider %s: manufacturerID <%s> cryptokiVersion %d.%d" + " libraryDescription <%s> libraryVersion %d.%d", +- provider_id, +- p->info.manufacturerID, +- p->info.cryptokiVersion.major, +- p->info.cryptokiVersion.minor, +- p->info.libraryDescription, +- p->info.libraryVersion.major, +- p->info.libraryVersion.minor); +- if ((rv = f->C_GetSlotList(CK_TRUE, NULL, &p->nslots)) != CKR_OK) { ++ provider_module, ++ m->info.manufacturerID, ++ m->info.cryptokiVersion.major, ++ m->info.cryptokiVersion.minor, ++ m->info.libraryDescription, ++ m->info.libraryVersion.major, ++ m->info.libraryVersion.minor); ++ ++ if ((rv = f->C_GetSlotList(CK_TRUE, NULL, &m->nslots)) != CKR_OK) { + error("C_GetSlotList failed: %lu", rv); + goto fail; + } +- if (p->nslots == 0) { ++ if (m->nslots == 0) { +- error("%s: provider %s returned no slots", __func__, ++ debug("%s: provider %s returned no slots", __func__, +- provider_id); ++ provider_module); + ret = -SSH_PKCS11_ERR_NO_SLOTS; + goto fail; + } +- p->slotlist = xcalloc(p->nslots, sizeof(CK_SLOT_ID)); +- if ((rv = f->C_GetSlotList(CK_TRUE, p->slotlist, &p->nslots)) ++ m->slotlist = xcalloc(m->nslots, sizeof(CK_SLOT_ID)); ++ if ((rv = f->C_GetSlotList(CK_TRUE, m->slotlist, &m->nslots)) + != CKR_OK) { + error("C_GetSlotList for provider %s failed: %lu", +- provider_id, rv); ++ provider_module, rv); + goto fail; + } +- p->slotinfo = xcalloc(p->nslots, sizeof(struct pkcs11_slotinfo)); + p->valid = 1; +- nkeys = 0; +- for (i = 0; i < p->nslots; i++) { +- token = &p->slotinfo[i].token; +- if ((rv = f->C_GetTokenInfo(p->slotlist[i], token)) ++ m->slotinfo = xcalloc(m->nslots, sizeof(struct pkcs11_slotinfo)); ++ m->valid = 1; ++ for (i = 0; i < m->nslots; i++) { ++ token = &m->slotinfo[i].token; ++ if ((rv = f->C_GetTokenInfo(m->slotlist[i], token)) + != CKR_OK) { + error("C_GetTokenInfo for provider %s slot %lu " +- "failed: %lu", provider_id, (unsigned long)i, rv); ++ "failed: %lu", provider_module, (unsigned long)i, rv); ++ token->flags = 0; + continue; + } ++ rmspace(token->label, sizeof(token->label)); ++ rmspace(token->manufacturerID, sizeof(token->manufacturerID)); ++ rmspace(token->model, sizeof(token->model)); ++ rmspace(token->serialNumber, sizeof(token->serialNumber)); ++ } ++ m->module_path = provider_module; ++ provider_module = NULL; ++ ++ /* insert unconditionally -- remove if there will be no keys later */ ++ TAILQ_INSERT_TAIL(&pkcs11_providers, p, next); ++ p->refcount++; /* add to provider list */ ++ *providerp = p; ++ return 0; ++ ++fail: ++ if (need_finalize && (rv = f->C_Finalize(NULL)) != CKR_OK) ++ error("C_Finalize for provider %s failed: %lu", ++ provider_module, rv); ++ free(provider_module); ++ if (m) { ++ free(m->slotlist); ++ free(m); ++ } ++ if (p) { ++ free(p->name); ++ free(p); ++ } ++ if (handle) ++ dlclose(handle); ++ return ret; ++} ++ ++/* ++ * register a new provider, fails if provider already exists. if ++ * keyp is provided, fetch keys. ++ */ ++static int ++pkcs11_register_provider_by_uri(struct pkcs11_uri *uri, char *pin, ++ struct sshkey ***keyp, struct pkcs11_provider **providerp, CK_ULONG user) ++{ ++ int nkeys; ++ int ret = -1; ++ struct pkcs11_provider *p = NULL; ++ CK_ULONG i; ++ CK_TOKEN_INFO *token; ++ char *provider_uri = NULL; ++ ++ if (providerp == NULL) ++ goto fail; ++ *providerp = NULL; ++ ++ if (keyp != NULL) ++ *keyp = NULL; ++ ++ if ((ret = pkcs11_initialize_provider(uri, &p)) != 0) { ++ goto fail; ++ } ++ ++ provider_uri = pkcs11_uri_get(uri); ++ if (pin == NULL && uri->pin != NULL) { ++ pin = uri->pin; ++ } ++ nkeys = 0; ++ for (i = 0; i < p->module->nslots; i++) { ++ token = &p->module->slotinfo[i].token; + if ((token->flags & CKF_TOKEN_INITIALIZED) == 0) { + debug2("%s: ignoring uninitialised token in " + "provider %s slot %lu", __func__, +- provider_id, (unsigned long)i); ++ provider_uri, (unsigned long)i); ++ continue; ++ } ++ if (uri->token != NULL && ++ strcmp(token->label, uri->token) != 0) { ++ debug2("%s: ignoring token not matching label (%s) " ++ "specified by PKCS#11 URI in slot %lu", __func__, ++ token->label, (unsigned long)i); ++ continue; ++ } ++ if (uri->manuf != NULL && ++ strcmp(token->manufacturerID, uri->manuf) != 0) { ++ debug2("%s: ignoring token not matching requrested " ++ "manufacturerID (%s) specified by PKCS#11 URI in " ++ "slot %lu", __func__, ++ token->manufacturerID, (unsigned long)i); + continue; + } +- rmspace(token->label, sizeof(token->label)); +- rmspace(token->manufacturerID, sizeof(token->manufacturerID)); +- rmspace(token->model, sizeof(token->model)); +- rmspace(token->serialNumber, sizeof(token->serialNumber)); + debug("provider %s slot %lu: label <%s> manufacturerID <%s> " + "model <%s> serial <%s> flags 0x%lx", +- provider_id, (unsigned long)i, ++ provider_uri, (unsigned long)i, + token->label, token->manufacturerID, token->model, + token->serialNumber, token->flags); + /* +- * open session, login with pin and retrieve public +- * keys (if keyp is provided) ++ * open session if not yet openend, login with pin and ++ * retrieve public keys (if keyp is provided) + */ +- if ((ret = pkcs11_open_session(p, i, pin, user)) == 0) { ++ if (p->module->slotinfo[i].session != 0 || ++ (ret = pkcs11_open_session(p, i, pin, user)) == 0) { + if (keyp == NULL) + continue; +- pkcs11_fetch_keys(p, i, keyp, &nkeys); +- pkcs11_fetch_certs(p, i, keyp, &nkeys); ++ pkcs11_fetch_keys(p, i, keyp, &nkeys, uri); ++ pkcs11_fetch_certs(p, i, keyp, &nkeys, uri); ++ if (nkeys == 0 && uri->object != NULL) { ++ debug3("%s: No keys found. Retrying without label (%s) ", ++ __func__, token->label); ++ /* Try once more without the label filter */ ++ char *label = uri->object; ++ uri->object = NULL; /* XXX clone uri? */ ++ pkcs11_fetch_keys(p, i, keyp, &nkeys, uri); ++ pkcs11_fetch_certs(p, i, keyp, &nkeys, uri); ++ uri->object = label; ++ } + } + } ++ pin = NULL; /* Will be cleaned up with URI */ + + /* now owned by caller */ + *providerp = p; + +- TAILQ_INSERT_TAIL(&pkcs11_providers, p, next); +- p->refcount++; /* add to provider list */ +- ++ free(provider_uri); + return (nkeys); + fail: +- if (need_finalize && (rv = f->C_Finalize(NULL)) != CKR_OK) +- error("C_Finalize for provider %s failed: %lu", +- provider_id, rv); + if (p) { +- free(p->name); +- free(p->slotlist); +- free(p->slotinfo); +- free(p); ++ TAILQ_REMOVE(&pkcs11_providers, p, next); ++ pkcs11_provider_unref(p); + } +- if (handle) +- dlclose(handle); + return (ret); + } + +-/* +- * register a new provider and get number of keys hold by the token, +- * fails if provider already exists +- */ ++static int ++pkcs11_register_provider(char *provider_id, char *pin, struct sshkey ***keyp, ++ struct pkcs11_provider **providerp, CK_ULONG user) ++{ ++ struct pkcs11_uri *uri = NULL; ++ int r; ++ ++ debug("%s: called, provider_id = %s", __func__, provider_id); ++ ++ uri = pkcs11_uri_init(); ++ if (uri == NULL) ++ fatal("failed to init PKCS#11 URI"); ++ ++ if (strlen(provider_id) >= strlen(PKCS11_URI_SCHEME) && ++ strncmp(provider_id, PKCS11_URI_SCHEME, strlen(PKCS11_URI_SCHEME)) == 0) { ++ if (pkcs11_uri_parse(provider_id, uri) != 0) ++ fatal("Failed to parse PKCS#11 URI"); ++ } else { ++ uri->module_path = strdup(provider_id); ++ } ++ ++ r = pkcs11_register_provider_by_uri(uri, pin, keyp, providerp, user); ++ pkcs11_uri_cleanup(uri); ++ ++ return r; ++} ++ + int +-pkcs11_add_provider(char *provider_id, char *pin, struct sshkey ***keyp) ++pkcs11_add_provider_by_uri(struct pkcs11_uri *uri, char *pin, ++ struct sshkey ***keyp) + { +- struct pkcs11_provider *p = NULL; + int nkeys; ++ struct pkcs11_provider *p = NULL; ++ char *provider_uri = pkcs11_uri_get(uri); + +- nkeys = pkcs11_register_provider(provider_id, pin, keyp, &p, CKU_USER); ++ debug("%s: called, provider_uri = %s", __func__, provider_uri); ++ ++ nkeys = pkcs11_register_provider_by_uri(uri, pin, keyp, &p, CKU_USER); + + /* no keys found or some other error, de-register provider */ + if (nkeys <= 0 && p != NULL) { +@@ -1611,7 +1912,36 @@ pkcs11_add_provider(char *provider_id, char *pin, struct sshkey ***keyp) + } + if (nkeys == 0) + debug("%s: provider %s returned no keys", __func__, +- provider_id); ++ provider_uri); ++ ++ free(provider_uri); ++ return nkeys; ++} ++ ++/* ++ * register a new provider and get number of keys hold by the token, ++ * fails if provider already exists ++ */ ++int ++pkcs11_add_provider(char *provider_id, char *pin, struct sshkey ***keyp) ++{ ++ struct pkcs11_uri *uri; ++ int nkeys; ++ ++ uri = pkcs11_uri_init(); ++ if (uri == NULL) ++ fatal("Failed to init PCKS#11 URI"); ++ ++ if (strlen(provider_id) >= strlen(PKCS11_URI_SCHEME) && ++ strncmp(provider_id, PKCS11_URI_SCHEME, strlen(PKCS11_URI_SCHEME)) == 0) { ++ if (pkcs11_uri_parse(provider_id, uri) != 0) ++ fatal("Failed to parse PKCS#11 URI"); ++ } else { ++ uri->module_path = strdup(provider_id); ++ } ++ ++ nkeys = pkcs11_add_provider_by_uri(uri, pin, keyp); ++ pkcs11_uri_cleanup(uri); + + return (nkeys); + } +@@ -1633,8 +1963,7 @@ pkcs11_gakp(char *provider_id, char *pin, unsigned int slotidx, char *label, + + if ((p = pkcs11_provider_lookup(provider_id)) != NULL) + debug("%s: provider \"%s\" available", __func__, provider_id); +- else if ((ret = pkcs11_register_provider(provider_id, pin, NULL, &p, +- CKU_SO)) < 0) { ++ else if ((rv = pkcs11_register_provider(provider_id, pin, NULL, &p, CKU_SO)) != 0) { + debug("%s: could not register provider %s", __func__, + provider_id); + goto out; +@@ -1705,8 +2034,7 @@ pkcs11_destroy_keypair(char *provider_id, char *pin, unsigned long slotidx, + + if ((p = pkcs11_provider_lookup(provider_id)) != NULL) { + debug("%s: using provider \"%s\"", __func__, provider_id); +- } else if (pkcs11_register_provider(provider_id, pin, NULL, &p, +- CKU_SO) < 0) { ++ } else if ((rv = pkcs11_register_provider(provider_id, pin, NULL, &p, CKU_SO)) != 0) { + debug("%s: could not register provider %s", __func__, + provider_id); + goto out; +diff --git a/ssh-pkcs11.h b/ssh-pkcs11.h +index b9038450..5a855338 100644 +--- a/ssh-pkcs11.h ++++ b/ssh-pkcs11.h +@@ -22,10 +22,14 @@ + #define SSH_PKCS11_ERR_PIN_REQUIRED 4 + #define SSH_PKCS11_ERR_PIN_LOCKED 5 + ++#include "ssh-pkcs11-uri.h" ++ + int pkcs11_init(int); + void pkcs11_terminate(void); + int pkcs11_add_provider(char *, char *, struct sshkey ***); ++int pkcs11_add_provider_by_uri(struct pkcs11_uri *, char *, struct sshkey ***); + int pkcs11_del_provider(char *); ++int pkcs11_uri_write(const struct sshkey *, FILE *); + #ifdef WITH_PKCS11_KEYGEN + struct sshkey * + pkcs11_gakp(char *, char *, unsigned int, char *, unsigned int, +diff --git a/ssh.c b/ssh.c +index 91e7c351..47f4f299 100644 +--- a/ssh.c ++++ b/ssh.c +@@ -772,6 +772,14 @@ main(int ac, char **av) + options.gss_deleg_creds = 1; + break; + case 'i': ++#ifdef ENABLE_PKCS11 ++ if (strlen(optarg) >= strlen(PKCS11_URI_SCHEME) && ++ strncmp(optarg, PKCS11_URI_SCHEME, ++ strlen(PKCS11_URI_SCHEME)) == 0) { ++ add_identity_file(&options, NULL, optarg, 1); ++ break; ++ } ++#endif + p = tilde_expand_filename(optarg, getuid()); + if (stat(p, &st) < 0) + fprintf(stderr, "Warning: Identity file %s " +@@ -1521,6 +1529,7 @@ main(int ac, char **av) + free(options.certificate_files[i]); + options.certificate_files[i] = NULL; + } ++ pkcs11_terminate(); + + skip_connect: + exit_status = ssh_session2(ssh, pw); +@@ -1994,6 +2003,45 @@ ssh_session2(struct ssh *ssh, struct passwd *pw) + options.escape_char : SSH_ESCAPECHAR_NONE, id); + } + ++#ifdef ENABLE_PKCS11 ++static void ++load_pkcs11_identity(char *pkcs11_uri, char *identity_files[], ++ struct sshkey *identity_keys[], int *n_ids) ++{ ++ int nkeys, i; ++ struct sshkey **keys; ++ struct pkcs11_uri *uri; ++ ++ debug("identity file '%s' from pkcs#11", pkcs11_uri); ++ uri = pkcs11_uri_init(); ++ if (uri == NULL) ++ fatal("Failed to init PCKS#11 URI"); ++ ++ if (pkcs11_uri_parse(pkcs11_uri, uri) != 0) ++ fatal("Failed to parse PKCS#11 URI %s", pkcs11_uri); ++ ++ /* we need to merge URI and provider together */ ++ if (options.pkcs11_provider != NULL && uri->module_path == NULL) ++ uri->module_path = strdup(options.pkcs11_provider); ++ ++ if (options.num_identity_files < SSH_MAX_IDENTITY_FILES && ++ (nkeys = pkcs11_add_provider_by_uri(uri, NULL, &keys)) > 0) { ++ for (i = 0; i < nkeys; i++) { ++ if (*n_ids >= SSH_MAX_IDENTITY_FILES) { ++ sshkey_free(keys[i]); ++ continue; ++ } ++ identity_keys[*n_ids] = keys[i]; ++ identity_files[*n_ids] = pkcs11_uri_get(uri); ++ (*n_ids)++; ++ } ++ free(keys); ++ } ++ ++ pkcs11_uri_cleanup(uri); ++} ++#endif /* ENABLE_PKCS11 */ ++ + /* Loads all IdentityFile and CertificateFile keys */ + static void + load_public_identity_files(struct passwd *pw) +@@ -2008,10 +2056,6 @@ load_public_identity_files(struct passwd *pw) + 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; +- int nkeys; +-#endif /* PKCS11 */ + + n_ids = n_certs = 0; + memset(identity_files, 0, sizeof(identity_files)); +@@ -2024,32 +2068,46 @@ load_public_identity_files(struct passwd *pw) + 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)) > 0) { +- for (i = 0; i < nkeys; i++) { +- if (n_ids >= SSH_MAX_IDENTITY_FILES) { +- sshkey_free(keys[i]); +- continue; +- } +- identity_keys[n_ids] = keys[i]; +- identity_files[n_ids] = +- xstrdup(options.pkcs11_provider); /* XXX */ +- n_ids++; +- } +- free(keys); ++ /* handle fallback from PKCS11Provider option */ ++ pkcs11_init(!options.batch_mode); ++ ++ if (options.pkcs11_provider != NULL) { ++ struct pkcs11_uri *uri; ++ ++ uri = pkcs11_uri_init(); ++ if (uri == NULL) ++ fatal("Failed to init PCKS#11 URI"); ++ ++ /* Construct simple PKCS#11 URI to simplify access */ ++ uri->module_path = strdup(options.pkcs11_provider); ++ ++ /* Add it as any other IdentityFile */ ++ cp = pkcs11_uri_get(uri); ++ add_identity_file(&options, NULL, cp, 1); ++ free(cp); ++ ++ pkcs11_uri_cleanup(uri); + } + #endif /* ENABLE_PKCS11 */ + for (i = 0; i < options.num_identity_files; i++) { ++ char *name = options.identity_files[i]; + if (n_ids >= SSH_MAX_IDENTITY_FILES || +- strcasecmp(options.identity_files[i], "none") == 0) { ++ strcasecmp(name, "none") == 0) { + free(options.identity_files[i]); + options.identity_files[i] = NULL; + continue; + } +- cp = tilde_expand_filename(options.identity_files[i], getuid()); ++#ifdef ENABLE_PKCS11 ++ if (strlen(name) >= strlen(PKCS11_URI_SCHEME) && ++ strncmp(name, PKCS11_URI_SCHEME, ++ strlen(PKCS11_URI_SCHEME)) == 0) { ++ load_pkcs11_identity(name, identity_files, ++ identity_keys, &n_ids); ++ free(options.identity_files[i]); ++ continue; ++ } ++#endif /* ENABLE_PKCS11 */ ++ cp = tilde_expand_filename(name, getuid()); + filename = percent_expand(cp, "d", pw->pw_dir, + "u", pw->pw_name, "l", thishost, "h", host, + "r", options.user, (char *)NULL); +diff --git a/ssh_config.5 b/ssh_config.5 +index 41262963..a211034e 100644 +--- a/ssh_config.5 ++++ b/ssh_config.5 +@@ -952,6 +952,21 @@ may also be used in conjunction with + .Cm CertificateFile + in order to provide any certificate also needed for authentication with + the identity. ++.Pp ++The authentication identity can be also specified in a form of PKCS#11 URI ++starting with a string ++.Cm pkcs11: . ++There is supported a subset of the PKCS#11 URI as defined ++in RFC 7512 (implemented path arguments ++.Cm id , ++.Cm manufacturer , ++.Cm object , ++.Cm token ++and query arguments ++.Cm module-path ++and ++.Cm pin-value ++). The URI can not be in quotes. + .It Cm IgnoreUnknown + Specifies a pattern-list of unknown options to be ignored if they are + encountered in configuration parsing. +commit 1efe98998408593861fdcd4da392dd10820f0fde +Author: Jakub Jelen +Date: Wed Jun 12 14:30:30 2019 +0200 + + Allow to specify the pin also for the ssh-add + +diff --git a/ssh-add.c b/ssh-add.c +index f039e00e..adc4e5c9 100644 +--- a/ssh-add.c ++++ b/ssh-add.c +@@ -190,20 +190,28 @@ delete_all(int agent_fd, int qflag) + } + + #ifdef ENABLE_PKCS11 +-static int update_card(int, int, const char *, int); ++static int update_card(int, int, const char *, int, char *); + + int + update_pkcs11_uri(int agent_fd, int adding, const char *pkcs11_uri, int qflag) + { ++ char *pin = NULL; + struct pkcs11_uri *uri; + + /* dry-run parse to make sure the URI is valid and to report errors */ + uri = pkcs11_uri_init(); + if (pkcs11_uri_parse((char *) pkcs11_uri, uri) != 0) + fatal("Failed to parse PKCS#11 URI"); ++ if (uri->pin != NULL) { ++ pin = strdup(uri->pin); ++ if (pin == NULL) { ++ fatal("Failed to dupplicate string"); ++ } ++ /* pin is freed in the update_card() */ ++ } + pkcs11_uri_cleanup(uri); + +- return update_card(agent_fd, adding, pkcs11_uri, qflag); ++ return update_card(agent_fd, adding, pkcs11_uri, qflag, pin); + } + #endif + +@@ -409,12 +417,11 @@ add_file(int agent_fd, const char *filename, int key_only, int qflag) + } + + static int +-update_card(int agent_fd, int add, const char *id, int qflag) ++update_card(int agent_fd, int add, const char *id, int qflag, char *pin) + { +- char *pin = NULL; + int r, ret = -1; + +- if (add) { ++ if (add && pin == NULL) { + if ((pin = read_passphrase("Enter passphrase for PKCS#11: ", + RP_ALLOW_STDIN)) == NULL) + return -1; +@@ -734,7 +741,7 @@ main(int argc, char **argv) + } + if (pkcs11provider != NULL) { + if (update_card(agent_fd, !deleting, pkcs11provider, +- qflag) == -1) ++ qflag, NULL) == -1) + ret = 1; + goto done; + } diff --git a/openssh-8.0p1-preserve-pam-errors.patch b/openssh-8.0p1-preserve-pam-errors.patch new file mode 100644 index 0000000..dbdbe93 --- /dev/null +++ b/openssh-8.0p1-preserve-pam-errors.patch @@ -0,0 +1,44 @@ +diff -up openssh-8.0p1/auth-pam.c.preserve-pam-errors openssh-8.0p1/auth-pam.c +--- openssh-8.0p1/auth-pam.c.preserve-pam-errors 2021-03-31 17:03:15.618592347 +0200 ++++ openssh-8.0p1/auth-pam.c 2021-03-31 17:06:58.115220014 +0200 +@@ -511,7 +511,11 @@ sshpam_thread(void *ctxtp) + goto auth_fail; + + if (!do_pam_account()) { +- sshpam_err = PAM_ACCT_EXPIRED; ++ /* Preserve PAM_PERM_DENIED and PAM_USER_UNKNOWN. ++ * Backward compatibility for other errors. */ ++ if (sshpam_err != PAM_PERM_DENIED ++ && sshpam_err != PAM_USER_UNKNOWN) ++ sshpam_err = PAM_ACCT_EXPIRED; + goto auth_fail; + } + if (sshpam_authctxt->force_pwchange) { +@@ -568,8 +572,10 @@ sshpam_thread(void *ctxtp) + pam_strerror(sshpam_handle, sshpam_err))) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + /* XXX - can't do much about an error here */ +- if (sshpam_err == PAM_ACCT_EXPIRED) +- ssh_msg_send(ctxt->pam_csock, PAM_ACCT_EXPIRED, buffer); ++ if (sshpam_err == PAM_PERM_DENIED ++ || sshpam_err == PAM_USER_UNKNOWN ++ || sshpam_err == PAM_ACCT_EXPIRED) ++ ssh_msg_send(ctxt->pam_csock, sshpam_err, buffer); + else if (sshpam_maxtries_reached) + ssh_msg_send(ctxt->pam_csock, PAM_MAXTRIES, buffer); + else +@@ -856,10 +862,12 @@ sshpam_query(void *ctx, char **name, cha + plen++; + free(msg); + break; ++ case PAM_USER_UNKNOWN: ++ case PAM_PERM_DENIED: + case PAM_ACCT_EXPIRED: ++ sshpam_account_status = 0; ++ /* FALLTHROUGH */ + case PAM_MAXTRIES: +- if (type == PAM_ACCT_EXPIRED) +- sshpam_account_status = 0; + if (type == PAM_MAXTRIES) + sshpam_set_maxtries_reached(1); + /* FALLTHROUGH */ diff --git a/openssh-8.0p1-proxyjump-loops.patch b/openssh-8.0p1-proxyjump-loops.patch new file mode 100644 index 0000000..578eff3 --- /dev/null +++ b/openssh-8.0p1-proxyjump-loops.patch @@ -0,0 +1,33 @@ +From de1f3564cd85915b3002859873a37cb8d31ac9ce Mon Sep 17 00:00:00 2001 +From: "dtucker@openbsd.org" +Date: Tue, 18 Feb 2020 08:49:49 +0000 +Subject: [PATCH] upstream: Detect and prevent simple configuration loops when + using + +ProxyJump. bz#3057, ok djm@ + +OpenBSD-Commit-ID: 077d21c564c886c98309d871ed6f8ef267b9f037 +--- + ssh.c | 10 +++++++++- + 1 file changed, 9 insertions(+), 1 deletion(-) + +diff --git a/ssh.c b/ssh.c +index 15aee569e..a983a108b 100644 +--- a/ssh.c ++++ b/ssh.c +@@ -1208,6 +1208,14 @@ main(int ac, char **av) + if (options.jump_host != NULL) { + char port_s[8]; + const char *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 (strcmp(options.jump_host, host) == 0 && port == jumpport) ++ fatal("jumphost loop via %s", options.jump_host); + + /* + * Try to use SSH indicated by argv[0], but fall back to + diff --git a/openssh-8.0p1-rdomain.patch b/openssh-8.0p1-rdomain.patch new file mode 100644 index 0000000..610c8b3 --- /dev/null +++ b/openssh-8.0p1-rdomain.patch @@ -0,0 +1,44 @@ +commit 5481d0b4036b33b92c372ee36258ed11bff57d5d +Author: Jakub Jelen +Date: Thu Feb 27 10:07:33 2020 +0100 + + Mark the RDomain configuration option unsupported on non-openbsd builds + +diff --git a/servconf.c b/servconf.c +index db80e943..153d2525 100644 +--- a/servconf.c ++++ b/servconf.c +@@ -698,7 +698,11 @@ static struct { + { "fingerprinthash", sFingerprintHash, SSHCFG_GLOBAL }, + { "disableforwarding", sDisableForwarding, SSHCFG_ALL }, + { "exposeauthinfo", sExposeAuthInfo, SSHCFG_ALL }, ++#if defined(__OpenBSD__) + { "rdomain", sRDomain, SSHCFG_ALL }, ++#else ++ { "rdomain", sUnsupported, SSHCFG_ALL }, ++#endif + { "casignaturealgorithms", sCASignatureAlgorithms, SSHCFG_ALL }, + { NULL, sBadOption, 0 } + }; +@@ -2841,7 +2845,9 @@ dump_config(ServerOptions *o) + o->hostkeyalgorithms : KEX_DEFAULT_PK_ALG); + dump_cfg_string(sPubkeyAcceptedKeyTypes, o->pubkey_key_types ? + o->pubkey_key_types : KEX_DEFAULT_PK_ALG); ++#if defined(__OpenBSD__) + dump_cfg_string(sRDomain, o->routing_domain); ++#endif + + /* string arguments requiring a lookup */ + dump_cfg_string(sLogLevel, log_level_name(o->log_level)); +diff --git a/sshd_config.5 b/sshd_config.5 +index 5dca8981..766e9b90 100644 +--- a/sshd_config.5 ++++ b/sshd_config.5 +@@ -1542,6 +1542,7 @@ will be bound to this + If the routing domain is set to + .Cm \&%D , + then the domain in which the incoming connection was received will be applied. ++This feature is available on OpenBSD only. + .It Cm SetEnv + Specifies one or more environment variables to set in child sessions started + by diff --git a/openssh-8.0p1-restore-nonblock.patch b/openssh-8.0p1-restore-nonblock.patch new file mode 100644 index 0000000..b16e17e --- /dev/null +++ b/openssh-8.0p1-restore-nonblock.patch @@ -0,0 +1,311 @@ +diff -up openssh-8.0p1/channels.c.restore-nonblock openssh-8.0p1/channels.c +--- openssh-8.0p1/channels.c.restore-nonblock 2021-06-21 10:44:26.380559612 +0200 ++++ openssh-8.0p1/channels.c 2021-06-21 10:48:47.754579151 +0200 +@@ -333,7 +333,27 @@ channel_register_fds(struct ssh *ssh, Ch + #endif + + /* enable nonblocking mode */ +- if (nonblock) { ++ 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) && fcntl(rfd, F_GETFL) == 0) { ++ c->restore_block |= CHANNEL_RESTORE_RFD; ++ set_nonblock(rfd); ++ } ++ if (wfd != -1 && !isatty(wfd) && fcntl(wfd, F_GETFL) == 0) { ++ c->restore_block |= CHANNEL_RESTORE_WFD; ++ set_nonblock(wfd); ++ } ++ if (efd != -1 && !isatty(efd) && fcntl(efd, F_GETFL) == 0) { ++ c->restore_block |= CHANNEL_RESTORE_EFD; ++ set_nonblock(efd); ++ } ++ } else if (nonblock) { + if (rfd != -1) + set_nonblock(rfd); + if (wfd != -1) +@@ -422,17 +442,23 @@ channel_find_maxfd(struct ssh_channels * + } + + int +-channel_close_fd(struct ssh *ssh, int *fdp) ++channel_close_fd(struct ssh *ssh, Channel *c, int *fdp) + { + struct ssh_channels *sc = ssh->chanctxt; +- int ret = 0, fd = *fdp; ++ int ret, fd = *fdp; + +- if (fd != -1) { +- ret = close(fd); +- *fdp = -1; +- if (fd == sc->channel_max_fd) +- channel_find_maxfd(sc); +- } ++ if (fd == -1) ++ return 0; ++ ++ if ((*fdp == c->rfd && (c->restore_block & CHANNEL_RESTORE_RFD) != 0) || ++ (*fdp == c->wfd && (c->restore_block & CHANNEL_RESTORE_WFD) != 0) || ++ (*fdp == c->efd && (c->restore_block & CHANNEL_RESTORE_EFD) != 0)) ++ (void)fcntl(*fdp, F_SETFL, 0); /* restore blocking */ ++ ++ ret = close(fd); ++ *fdp = -1; ++ if (fd == sc->channel_max_fd) ++ channel_find_maxfd(sc); + return ret; + } + +@@ -442,13 +468,13 @@ channel_close_fds(struct ssh *ssh, Chann + { + int sock = c->sock, rfd = c->rfd, wfd = c->wfd, efd = c->efd; + +- channel_close_fd(ssh, &c->sock); ++ channel_close_fd(ssh, c, &c->sock); + if (rfd != sock) +- channel_close_fd(ssh, &c->rfd); ++ channel_close_fd(ssh, c, &c->rfd); + if (wfd != sock && wfd != rfd) +- channel_close_fd(ssh, &c->wfd); ++ channel_close_fd(ssh, c, &c->wfd); + if (efd != sock && efd != rfd && efd != wfd) +- channel_close_fd(ssh, &c->efd); ++ channel_close_fd(ssh, c, &c->efd); + } + + static void +@@ -681,7 +707,7 @@ channel_stop_listening(struct ssh *ssh) + case SSH_CHANNEL_X11_LISTENER: + case SSH_CHANNEL_UNIX_LISTENER: + case SSH_CHANNEL_RUNIX_LISTENER: +- channel_close_fd(ssh, &c->sock); ++ channel_close_fd(ssh, c, &c->sock); + channel_free(ssh, c); + break; + } +@@ -1487,7 +1513,8 @@ channel_decode_socks5(Channel *c, struct + + Channel * + channel_connect_stdio_fwd(struct ssh *ssh, +- const char *host_to_connect, u_short port_to_connect, int in, int out) ++ const char *host_to_connect, u_short port_to_connect, ++ int in, int out, int nonblock) + { + Channel *c; + +@@ -1495,7 +1522,7 @@ channel_connect_stdio_fwd(struct ssh *ss + + c = channel_new(ssh, "stdio-forward", SSH_CHANNEL_OPENING, in, out, + -1, CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, +- 0, "stdio-forward", /*nonblock*/0); ++ 0, "stdio-forward", nonblock); + + c->path = xstrdup(host_to_connect); + c->host_port = port_to_connect; +@@ -1650,7 +1677,7 @@ channel_post_x11_listener(struct ssh *ss + if (c->single_connection) { + oerrno = errno; + debug2("single_connection: closing X11 listener."); +- channel_close_fd(ssh, &c->sock); ++ channel_close_fd(ssh, c, &c->sock); + chan_mark_dead(ssh, c); + errno = oerrno; + } +@@ -2087,7 +2114,7 @@ channel_handle_efd_write(struct ssh *ssh + return 1; + if (len <= 0) { + debug2("channel %d: closing write-efd %d", c->self, c->efd); +- channel_close_fd(ssh, &c->efd); ++ channel_close_fd(ssh, c, &c->efd); + } else { + if ((r = sshbuf_consume(c->extended, len)) != 0) { + fatal("%s: channel %d: consume: %s", +@@ -2119,7 +2146,7 @@ channel_handle_efd_read(struct ssh *ssh, + if (len <= 0) { + debug2("channel %d: closing read-efd %d", + c->self, c->efd); +- channel_close_fd(ssh, &c->efd); ++ channel_close_fd(ssh, c, &c->efd); + } else { + if (c->extended_usage == CHAN_EXTENDED_IGNORE) { + debug3("channel %d: discard efd", +diff -up openssh-8.0p1/channels.h.restore-nonblock openssh-8.0p1/channels.h +--- openssh-8.0p1/channels.h.restore-nonblock 2021-06-21 10:44:26.380559612 +0200 ++++ openssh-8.0p1/channels.h 2021-06-21 10:44:26.387559665 +0200 +@@ -63,6 +63,16 @@ + + #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) +@@ -131,6 +141,7 @@ struct Channel { + * to a matching pre-select handler. + * this way post-select handlers are not + * accidentally called if a FD gets reused */ ++ int restore_block; /* fd mask to restore blocking status */ + struct sshbuf *input; /* data read from socket, to be sent over + * encrypted connection */ + struct sshbuf *output; /* data received over encrypted connection for +@@ -258,7 +269,7 @@ void channel_register_filter(struct ssh + 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 *, int *); ++int channel_close_fd(struct ssh *, Channel *, int *); + void channel_send_window_changes(struct ssh *); + + /* mux proxy support */ +@@ -305,7 +316,7 @@ Channel *channel_connect_to_port(struct + 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*, +- u_short, int, int); ++ u_short, 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 *, +diff -up openssh-8.0p1/clientloop.c.restore-nonblock openssh-8.0p1/clientloop.c +--- openssh-8.0p1/clientloop.c.restore-nonblock 2021-06-21 10:44:26.290558923 +0200 ++++ openssh-8.0p1/clientloop.c 2021-06-21 10:44:26.387559665 +0200 +@@ -1436,14 +1436,6 @@ client_loop(struct ssh *ssh, int have_pt + if (have_pty) + leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); + +- /* restore blocking io */ +- if (!isatty(fileno(stdin))) +- unset_nonblock(fileno(stdin)); +- if (!isatty(fileno(stdout))) +- unset_nonblock(fileno(stdout)); +- if (!isatty(fileno(stderr))) +- unset_nonblock(fileno(stderr)); +- + /* + * 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 +diff -up openssh-8.0p1/mux.c.restore-nonblock openssh-8.0p1/mux.c +--- openssh-8.0p1/mux.c.restore-nonblock 2019-04-18 00:52:57.000000000 +0200 ++++ openssh-8.0p1/mux.c 2021-06-21 10:50:51.007537336 +0200 +@@ -454,14 +454,6 @@ mux_master_process_new_session(struct ss + if (cctx->want_tty && tcgetattr(new_fd[0], &cctx->tio) == -1) + error("%s: tcgetattr: %s", __func__, strerror(errno)); + +- /* enable nonblocking unless tty */ +- if (!isatty(new_fd[0])) +- set_nonblock(new_fd[0]); +- if (!isatty(new_fd[1])) +- set_nonblock(new_fd[1]); +- if (!isatty(new_fd[2])) +- set_nonblock(new_fd[2]); +- + window = CHAN_SES_WINDOW_DEFAULT; + packetmax = CHAN_SES_PACKET_DEFAULT; + if (cctx->want_tty) { +@@ -471,7 +463,7 @@ mux_master_process_new_session(struct ss + + nc = channel_new(ssh, "session", SSH_CHANNEL_OPENING, + new_fd[0], new_fd[1], new_fd[2], window, packetmax, +- CHAN_EXTENDED_WRITE, "client-session", /*nonblock*/0); ++ 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 */ +@@ -1033,13 +1025,8 @@ mux_master_process_stdio_fwd(struct ssh + } + } + +- /* enable nonblocking unless tty */ +- if (!isatty(new_fd[0])) +- set_nonblock(new_fd[0]); +- if (!isatty(new_fd[1])) +- set_nonblock(new_fd[1]); +- +- nc = channel_connect_stdio_fwd(ssh, chost, cport, new_fd[0], new_fd[1]); ++ 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 */ +diff -up openssh-8.0p1/nchan.c.restore-nonblock openssh-8.0p1/nchan.c +--- openssh-8.0p1/nchan.c.restore-nonblock 2021-06-21 10:44:26.388559673 +0200 ++++ openssh-8.0p1/nchan.c 2021-06-21 10:52:42.685405537 +0200 +@@ -387,7 +387,7 @@ chan_shutdown_write(struct ssh *ssh, Cha + strerror(errno)); + } + } else { +- if (channel_close_fd(ssh, &c->wfd) < 0) { ++ if (channel_close_fd(ssh, c, &c->wfd) < 0) { + logit("channel %d: %s: close() failed for " + "fd %d [i%d o%d]: %.100s", + c->self, __func__, c->wfd, c->istate, c->ostate, +@@ -417,7 +417,7 @@ chan_shutdown_read(struct ssh *ssh, Chan + strerror(errno)); + } + } else { +- if (channel_close_fd(ssh, &c->rfd) < 0) { ++ if (channel_close_fd(ssh, c, &c->rfd) < 0) { + logit("channel %d: %s: close() failed for " + "fd %d [i%d o%d]: %.100s", + c->self, __func__, c->rfd, c->istate, c->ostate, +@@ -437,7 +437,7 @@ chan_shutdown_extended_read(struct ssh * + debug2("channel %d: %s (i%d o%d sock %d wfd %d efd %d [%s])", + c->self, __func__, c->istate, c->ostate, c->sock, c->rfd, c->efd, + channel_format_extended_usage(c)); +- if (channel_close_fd(ssh, &c->efd) < 0) { ++ if (channel_close_fd(ssh, c, &c->efd) < 0) { + logit("channel %d: %s: close() failed for " + "extended fd %d [i%d o%d]: %.100s", + c->self, __func__, c->efd, c->istate, c->ostate, +diff -up openssh-8.0p1/ssh.c.restore-nonblock openssh-8.0p1/ssh.c +--- openssh-8.0p1/ssh.c.restore-nonblock 2021-06-21 10:44:26.389559681 +0200 ++++ openssh-8.0p1/ssh.c 2021-06-21 10:54:47.651377045 +0200 +@@ -1709,7 +1709,8 @@ ssh_init_stdio_forwarding(struct ssh *ss + (out = dup(STDOUT_FILENO)) < 0) + fatal("channel_connect_stdio_fwd: dup() in/out failed"); + if ((c = channel_connect_stdio_fwd(ssh, options.stdio_forward_host, +- options.stdio_forward_port, in, out)) == NULL) ++ options.stdio_forward_port, in, out, ++ CHANNEL_NONBLOCK_STDIO)) == NULL) + fatal("%s: channel_connect_stdio_fwd failed", __func__); + channel_register_cleanup(ssh, c->self, client_cleanup_stdio_fwd, 0); + channel_register_open_confirm(ssh, c->self, ssh_stdio_confirm, NULL); +@@ -1862,14 +1863,6 @@ ssh_session2_open(struct ssh *ssh) + if (in < 0 || out < 0 || err < 0) + fatal("dup() in/out/err failed"); + +- /* enable nonblocking unless tty */ +- if (!isatty(in)) +- set_nonblock(in); +- if (!isatty(out)) +- set_nonblock(out); +- if (!isatty(err)) +- set_nonblock(err); +- + window = CHAN_SES_WINDOW_DEFAULT; + packetmax = CHAN_SES_PACKET_DEFAULT; + if (tty_flag) { +@@ -1879,7 +1872,7 @@ ssh_session2_open(struct ssh *ssh) + c = channel_new(ssh, + "session", SSH_CHANNEL_OPENING, in, out, err, + window, packetmax, CHAN_EXTENDED_WRITE, +- "client-session", /*nonblock*/0); ++ "client-session", CHANNEL_NONBLOCK_STDIO); + + debug3("%s: channel_new: %d", __func__, c->self); + diff --git a/openssh-8.0p1-scp-tests.patch b/openssh-8.0p1-scp-tests.patch new file mode 100644 index 0000000..e0a63c4 --- /dev/null +++ b/openssh-8.0p1-scp-tests.patch @@ -0,0 +1,61 @@ +diff --git a/regress/scp-ssh-wrapper.sh b/regress/scp-ssh-wrapper.sh +index 59f1ff63..dd48a482 100644 +--- a/regress/scp-ssh-wrapper.sh ++++ b/regress/scp-ssh-wrapper.sh +@@ -51,6 +51,18 @@ badserver_4) + echo "C755 2 file" + echo "X" + ;; ++badserver_5) ++ echo "D0555 0 " ++ echo "X" ++ ;; ++badserver_6) ++ echo "D0555 0 ." ++ echo "X" ++ ;; ++badserver_7) ++ echo "C0755 2 extrafile" ++ echo "X" ++ ;; + *) + set -- $arg + shift +diff --git a/regress/scp.sh b/regress/scp.sh +index 57cc7706..104c89e1 100644 +--- a/regress/scp.sh ++++ b/regress/scp.sh +@@ -25,6 +25,7 @@ export SCP # used in scp-ssh-wrapper.scp + scpclean() { + rm -rf ${COPY} ${COPY2} ${DIR} ${DIR2} + mkdir ${DIR} ${DIR2} ++ chmod 755 ${DIR} ${DIR2} + } + + verbose "$tid: simple copy local file to local file" +@@ -101,7 +102,7 @@ if [ ! -z "$SUDO" ]; then + $SUDO rm ${DIR2}/copy + fi + +-for i in 0 1 2 3 4; do ++for i in 0 1 2 3 4 5 6 7; do + verbose "$tid: disallow bad server #$i" + SCPTESTMODE=badserver_$i + export DIR SCPTESTMODE +@@ -113,6 +114,15 @@ for i in 0 1 2 3 4; do + 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 extranous object creation" ++ rm -f ${DIR2}/extrafile + done + + verbose "$tid: detect non-directory target" + diff --git a/openssh-8.0p1-sftp-realpath.patch b/openssh-8.0p1-sftp-realpath.patch new file mode 100644 index 0000000..37e32ca --- /dev/null +++ b/openssh-8.0p1-sftp-realpath.patch @@ -0,0 +1,273 @@ +diff --color -ruN a/Makefile.in b/Makefile.in +--- a/Makefile.in 2022-06-23 11:31:10.168186838 +0200 ++++ b/Makefile.in 2022-06-23 11:32:19.146513347 +0200 +@@ -125,7 +125,7 @@ + monitor.o monitor_wrap.o auth-krb5.o \ + auth2-gss.o gss-serv.o gss-serv-krb5.o kexgsss.o \ + loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \ +- sftp-server.o sftp-common.o \ ++ sftp-server.o sftp-common.o sftp-realpath.o \ + sandbox-null.o sandbox-rlimit.o sandbox-systrace.o sandbox-darwin.o \ + sandbox-seccomp-filter.o sandbox-capsicum.o sandbox-pledge.o \ + sandbox-solaris.o uidswap.o +@@ -217,8 +217,8 @@ + ssh-keyscan$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-keyscan.o + $(LD) -o $@ ssh-keyscan.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh $(LIBS) + +-sftp-server$(EXEEXT): $(LIBCOMPAT) libssh.a sftp.o sftp-common.o sftp-server.o sftp-server-main.o +- $(LD) -o $@ sftp-server.o sftp-common.o sftp-server-main.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) ++sftp-server$(EXEEXT): $(LIBCOMPAT) libssh.a sftp.o sftp-common.o sftp-realpath.o sftp-server.o sftp-server-main.o ++ $(LD) -o $@ sftp-server.o sftp-common.o sftp-realpath.o sftp-server-main.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) + + sftp$(EXEEXT): $(LIBCOMPAT) libssh.a sftp.o sftp-client.o sftp-common.o sftp-glob.o progressmeter.o + $(LD) -o $@ progressmeter.o sftp.o sftp-client.o sftp-common.o sftp-glob.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) $(LIBEDIT) +diff --color -ruN a/sftp-realpath.c b/sftp-realpath.c +--- a/sftp-realpath.c 1970-01-01 01:00:00.000000000 +0100 ++++ b/sftp-realpath.c 2022-06-23 11:35:33.193244873 +0200 +@@ -0,0 +1,225 @@ ++/* $OpenBSD: sftp-realpath.c,v 1.2 2021/09/02 21:03:54 deraadt Exp $ */ ++/* ++ * Copyright (c) 2003 Constantin S. Svintsoff ++ * ++ * 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. The names of the authors may not be used to endorse or promote ++ * products derived from this software without specific prior written ++ * permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#ifndef SYMLOOP_MAX ++# define SYMLOOP_MAX 32 ++#endif ++ ++/* XXX rewrite sftp-server to use POSIX realpath and remove this hack */ ++ ++char *sftp_realpath(const char *path, char *resolved); ++ ++/* ++ * char *realpath(const char *path, char resolved[PATH_MAX]); ++ * ++ * Find the real name of path, by removing all ".", ".." and symlink ++ * components. Returns (resolved) on success, or (NULL) on failure, ++ * in which case the path which caused trouble is left in (resolved). ++ */ ++char * ++sftp_realpath(const char *path, char *resolved) ++{ ++ struct stat sb; ++ char *p, *q, *s; ++ size_t left_len, resolved_len; ++ unsigned symlinks; ++ int serrno, slen, mem_allocated; ++ char left[PATH_MAX], next_token[PATH_MAX], symlink[PATH_MAX]; ++ ++ if (path[0] == '\0') { ++ errno = ENOENT; ++ return (NULL); ++ } ++ ++ serrno = errno; ++ ++ if (resolved == NULL) { ++ resolved = malloc(PATH_MAX); ++ if (resolved == NULL) ++ return (NULL); ++ mem_allocated = 1; ++ } else ++ mem_allocated = 0; ++ ++ symlinks = 0; ++ if (path[0] == '/') { ++ resolved[0] = '/'; ++ resolved[1] = '\0'; ++ if (path[1] == '\0') ++ return (resolved); ++ resolved_len = 1; ++ left_len = strlcpy(left, path + 1, sizeof(left)); ++ } else { ++ if (getcwd(resolved, PATH_MAX) == NULL) { ++ if (mem_allocated) ++ free(resolved); ++ else ++ strlcpy(resolved, ".", PATH_MAX); ++ return (NULL); ++ } ++ resolved_len = strlen(resolved); ++ left_len = strlcpy(left, path, sizeof(left)); ++ } ++ if (left_len >= sizeof(left) || resolved_len >= PATH_MAX) { ++ errno = ENAMETOOLONG; ++ goto err; ++ } ++ ++ /* ++ * Iterate over path components in `left'. ++ */ ++ while (left_len != 0) { ++ /* ++ * Extract the next path component and adjust `left' ++ * and its length. ++ */ ++ p = strchr(left, '/'); ++ s = p ? p : left + left_len; ++ if (s - left >= (ptrdiff_t)sizeof(next_token)) { ++ errno = ENAMETOOLONG; ++ goto err; ++ } ++ memcpy(next_token, left, s - left); ++ next_token[s - left] = '\0'; ++ left_len -= s - left; ++ if (p != NULL) ++ memmove(left, s + 1, left_len + 1); ++ if (resolved[resolved_len - 1] != '/') { ++ if (resolved_len + 1 >= PATH_MAX) { ++ errno = ENAMETOOLONG; ++ goto err; ++ } ++ resolved[resolved_len++] = '/'; ++ resolved[resolved_len] = '\0'; ++ } ++ if (next_token[0] == '\0') ++ continue; ++ else if (strcmp(next_token, ".") == 0) ++ continue; ++ else if (strcmp(next_token, "..") == 0) { ++ /* ++ * Strip the last path component except when we have ++ * single "/" ++ */ ++ if (resolved_len > 1) { ++ resolved[resolved_len - 1] = '\0'; ++ q = strrchr(resolved, '/') + 1; ++ *q = '\0'; ++ resolved_len = q - resolved; ++ } ++ continue; ++ } ++ ++ /* ++ * Append the next path component and lstat() it. If ++ * lstat() fails we still can return successfully if ++ * there are no more path components left. ++ */ ++ resolved_len = strlcat(resolved, next_token, PATH_MAX); ++ if (resolved_len >= PATH_MAX) { ++ errno = ENAMETOOLONG; ++ goto err; ++ } ++ if (lstat(resolved, &sb) != 0) { ++ if (errno == ENOENT && p == NULL) { ++ errno = serrno; ++ return (resolved); ++ } ++ goto err; ++ } ++ if (S_ISLNK(sb.st_mode)) { ++ if (symlinks++ > SYMLOOP_MAX) { ++ errno = ELOOP; ++ goto err; ++ } ++ slen = readlink(resolved, symlink, sizeof(symlink) - 1); ++ if (slen < 0) ++ goto err; ++ symlink[slen] = '\0'; ++ if (symlink[0] == '/') { ++ resolved[1] = 0; ++ resolved_len = 1; ++ } else if (resolved_len > 1) { ++ /* Strip the last path component. */ ++ resolved[resolved_len - 1] = '\0'; ++ q = strrchr(resolved, '/') + 1; ++ *q = '\0'; ++ resolved_len = q - resolved; ++ } ++ ++ /* ++ * If there are any path components left, then ++ * append them to symlink. The result is placed ++ * in `left'. ++ */ ++ if (p != NULL) { ++ if (symlink[slen - 1] != '/') { ++ if (slen + 1 >= ++ (ptrdiff_t)sizeof(symlink)) { ++ errno = ENAMETOOLONG; ++ goto err; ++ } ++ symlink[slen] = '/'; ++ symlink[slen + 1] = 0; ++ } ++ left_len = strlcat(symlink, left, sizeof(symlink)); ++ if (left_len >= sizeof(symlink)) { ++ errno = ENAMETOOLONG; ++ goto err; ++ } ++ } ++ left_len = strlcpy(left, symlink, sizeof(left)); ++ } ++ } ++ ++ /* ++ * Remove trailing slash except when the resolved pathname ++ * is a single "/". ++ */ ++ if (resolved_len > 1 && resolved[resolved_len - 1] == '/') ++ resolved[resolved_len - 1] = '\0'; ++ return (resolved); ++ ++err: ++ if (mem_allocated) ++ free(resolved); ++ return (NULL); ++} +diff --color -ruN a/sftp-server.c b/sftp-server.c +--- a/sftp-server.c 2022-06-23 11:31:10.147186434 +0200 ++++ b/sftp-server.c 2022-06-23 11:32:19.147513366 +0200 +@@ -51,6 +51,8 @@ + #include "sftp.h" + #include "sftp-common.h" + ++char *sftp_realpath(const char *, char *); /* sftp-realpath.c */ ++ + /* Our verbosity */ + static LogLevel log_level = SYSLOG_LEVEL_ERROR; + +@@ -1185,7 +1187,7 @@ + } + debug3("request %u: realpath", id); + verbose("realpath \"%s\"", path); +- if (realpath(path, resolvedname) == NULL) { ++ if (sftp_realpath(path, resolvedname) == NULL) { + send_status(id, errno_to_portable(errno)); + } else { + Stat s; diff --git a/openssh-8.0p1-sftp-timespeccmp.patch b/openssh-8.0p1-sftp-timespeccmp.patch new file mode 100644 index 0000000..7254b4a --- /dev/null +++ b/openssh-8.0p1-sftp-timespeccmp.patch @@ -0,0 +1,16 @@ +diff -up openssh-8.0p1/sftp.c.original openssh-8.0p1/sftp.c +--- openssh-8.0p1/sftp.c.original 2020-12-22 17:05:02.105698989 +0900 ++++ openssh-8.0p1/sftp.c 2020-12-22 17:05:42.922035780 +0900 +@@ -937,7 +937,11 @@ sglob_comp(const void *aa, const void *b + return (rmul * strcmp(ap, bp)); + else if (sort_flag & LS_TIME_SORT) { + #if defined(HAVE_STRUCT_STAT_ST_MTIM) +- return (rmul * timespeccmp(&as->st_mtim, &bs->st_mtim, <)); ++ if (timespeccmp(&as->st_mtim, &bs->st_mtim, <)){ ++ return rmul; ++ } else { ++ return -rmul; ++ } + #elif defined(HAVE_STRUCT_STAT_ST_MTIME) + return (rmul * NCMP(as->st_mtime, bs->st_mtime)); + #else diff --git a/openssh-8.0p1-sshd_config.patch b/openssh-8.0p1-sshd_config.patch new file mode 100644 index 0000000..e4b7912 --- /dev/null +++ b/openssh-8.0p1-sshd_config.patch @@ -0,0 +1,97 @@ +diff --git a/servconf.c b/servconf.c +index ffac5d2c..340045b2 100644 +--- a/servconf.c ++++ b/servconf.c +@@ -1042,7 +1042,7 @@ match_cfg_line(char **condition, int line, struct connection_info *ci) + return -1; + } + if (strcasecmp(attrib, "user") == 0) { +- if (ci == NULL) { ++ if (ci == NULL || (ci->test && ci->user == NULL)) { + result = 0; + continue; + } +@@ -1054,7 +1054,7 @@ match_cfg_line(char **condition, int line, struct connection_info *ci) + debug("user %.100s matched 'User %.100s' at " + "line %d", ci->user, arg, line); + } else if (strcasecmp(attrib, "group") == 0) { +- if (ci == NULL) { ++ if (ci == NULL || (ci->test && ci->user == NULL)) { + result = 0; + continue; + } +@@ -1067,7 +1067,7 @@ match_cfg_line(char **condition, int line, struct connection_info *ci) + result = 0; + } + } else if (strcasecmp(attrib, "host") == 0) { +- if (ci == NULL) { ++ if (ci == NULL || (ci->test && ci->host == NULL)) { + result = 0; + continue; + } +@@ -1079,7 +1079,7 @@ match_cfg_line(char **condition, int line, struct connection_info *ci) + debug("connection from %.100s matched 'Host " + "%.100s' at line %d", ci->host, arg, line); + } else if (strcasecmp(attrib, "address") == 0) { +- if (ci == NULL) { ++ if (ci == NULL || (ci->test && ci->address == NULL)) { + result = 0; + continue; + } +@@ -1098,7 +1098,7 @@ match_cfg_line(char **condition, int line, struct connection_info *ci) + return -1; + } + } else if (strcasecmp(attrib, "localaddress") == 0){ +- if (ci == NULL) { ++ if (ci == NULL || (ci->test && ci->laddress == NULL)) { + result = 0; + continue; + } +@@ -1124,7 +1124,7 @@ match_cfg_line(char **condition, int line, struct connection_info *ci) + arg); + return -1; + } +- if (ci == NULL) { ++ if (ci == NULL || (ci->test && ci->lport == -1)) { + result = 0; + continue; + } +@@ -1138,10 +1138,12 @@ match_cfg_line(char **condition, int line, struct connection_info *ci) + else + result = 0; + } else if (strcasecmp(attrib, "rdomain") == 0) { +- if (ci == NULL || ci->rdomain == NULL) { ++ 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 +diff --git a/servconf.h b/servconf.h +index 54e0a8d8..5483da05 100644 +--- a/servconf.h ++++ b/servconf.h +@@ -221,6 +221,8 @@ struct connection_info { + 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 */ + }; + + +diff --git a/sshd.c b/sshd.c +index cbd3bce9..1fcde502 100644 +--- a/sshd.c ++++ b/sshd.c +@@ -1843,6 +1843,7 @@ main(int ac, char **av) + */ + if (connection_info == NULL) + connection_info = get_connection_info(ssh, 0, 0); ++ connection_info->test = 1; + parse_server_match_config(&options, connection_info); + dump_config(&options); + } diff --git a/openssh-8.0p1-sshd_include.patch b/openssh-8.0p1-sshd_include.patch new file mode 100644 index 0000000..ff51340 --- /dev/null +++ b/openssh-8.0p1-sshd_include.patch @@ -0,0 +1,805 @@ +diff -up openssh-8.0p1/auth.c.sshdinclude openssh-8.0p1/auth.c +--- openssh-8.0p1/auth.c.sshdinclude 2021-10-20 15:18:49.740331098 +0200 ++++ openssh-8.0p1/auth.c 2021-10-20 15:19:41.324781344 +0200 +@@ -80,6 +80,7 @@ + + /* import */ + extern ServerOptions options; ++extern struct include_list includes; + extern int use_privsep; + extern struct sshbuf *loginmsg; + extern struct passwd *privsep_pw; +@@ -573,7 +574,7 @@ getpwnamallow(struct ssh *ssh, const cha + + ci = get_connection_info(ssh, 1, options.use_dns); + ci->user = user; +- parse_server_match_config(&options, ci); ++ parse_server_match_config(&options, &includes, ci); + log_change_level(options.log_level); + process_permitopen(ssh, &options); + +diff -up openssh-8.0p1/readconf.c.sshdinclude openssh-8.0p1/readconf.c +--- openssh-8.0p1/readconf.c.sshdinclude 2021-10-20 15:21:43.541848103 +0200 ++++ openssh-8.0p1/readconf.c 2021-10-20 15:22:06.302046768 +0200 +@@ -711,7 +711,7 @@ match_cfg_line(Options *options, char ** + static void + rm_env(Options *options, const char *arg, const char *filename, int linenum) + { +- int i, j; ++ int i, j, onum_send_env = options->num_send_env; + char *cp; + + /* Remove an environment variable */ +@@ -734,6 +734,11 @@ rm_env(Options *options, const char *arg + 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)); ++ } + } + + /* +diff -up openssh-8.0p1/regress/Makefile.sshdinclude openssh-8.0p1/regress/Makefile +--- openssh-8.0p1/regress/Makefile.sshdinclude 2021-10-20 15:18:49.742331115 +0200 ++++ openssh-8.0p1/regress/Makefile 2021-10-20 15:19:41.324781344 +0200 +@@ -82,6 +82,7 @@ LTESTS= connect \ + principals-command \ + cert-file \ + cfginclude \ ++ servcfginclude \ + allow-deny-users \ + authinfo + +@@ -118,7 +119,7 @@ CLEANFILES= *.core actual agent-key.* au + sftp-server.sh sftp.log ssh-log-wrapper.sh ssh.log \ + ssh_config ssh_config.* ssh_proxy ssh_proxy_bak \ + ssh_proxy_envpass sshd.log sshd_config sshd_config_minimal \ +- sshd_config.orig sshd_proxy sshd_proxy.* sshd_proxy_bak \ ++ 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 testdata \ +diff -up openssh-8.0p1/regress/servcfginclude.sh.sshdinclude openssh-8.0p1/regress/servcfginclude.sh +--- openssh-8.0p1/regress/servcfginclude.sh.sshdinclude 2021-10-20 15:18:49.744331132 +0200 ++++ openssh-8.0p1/regress/servcfginclude.sh 2021-10-20 15:22:06.303046777 +0200 +@@ -0,0 +1,188 @@ ++# Placed in the Public Domain. ++ ++tid="server config include" ++ ++cat > $OBJ/sshd_config.i << _EOF ++HostKey $OBJ/host.ssh-ed25519 ++Match host a ++ Banner /aa ++ ++Match host b ++ Banner /bb ++ Include $OBJ/sshd_config.i.* ++ ++Match host c ++ Include $OBJ/sshd_config.i.* ++ Banner /cc ++ ++Match host m ++ Include $OBJ/sshd_config.i.* ++ ++Match Host d ++ Banner /dd ++ ++Match Host e ++ Banner /ee ++ Include $OBJ/sshd_config.i.* ++ ++Match Host f ++ Include $OBJ/sshd_config.i.* ++ Banner /ff ++ ++Match Host n ++ Include $OBJ/sshd_config.i.* ++_EOF ++ ++cat > $OBJ/sshd_config.i.0 << _EOF ++Match host xxxxxx ++_EOF ++ ++cat > $OBJ/sshd_config.i.1 << _EOF ++Match host a ++ Banner /aaa ++ ++Match host b ++ Banner /bbb ++ ++Match host c ++ Banner /ccc ++ ++Match Host d ++ Banner /ddd ++ ++Match Host e ++ Banner /eee ++ ++Match Host f ++ Banner /fff ++_EOF ++ ++cat > $OBJ/sshd_config.i.2 << _EOF ++Match host a ++ Banner /aaaa ++ ++Match host b ++ Banner /bbbb ++ ++Match host c ++ Banner /cccc ++ ++Match Host d ++ Banner /dddd ++ ++Match Host e ++ Banner /eeee ++ ++Match Host f ++ Banner /ffff ++ ++Match all ++ Banner /xxxx ++_EOF ++ ++trial() { ++ _host="$1" ++ _exp="$2" ++ _desc="$3" ++ test -z "$_desc" && _desc="test match" ++ trace "$_desc host=$_host expect=$_exp" ++ ${SUDO} ${REAL_SSHD} -f $OBJ/sshd_config.i -T \ ++ -C "host=$_host,user=test,addr=127.0.0.1" > $OBJ/sshd_config.out || ++ fatal "ssh config parse failed: $_desc host=$_host expect=$_exp" ++ _got=`grep -i '^banner ' $OBJ/sshd_config.out | awk '{print $2}'` ++ if test "x$_exp" != "x$_got" ; then ++ fail "$desc_ host $_host include fail: expected $_exp got $_got" ++ fi ++} ++ ++trial a /aa ++trial b /bb ++trial c /ccc ++trial d /dd ++trial e /ee ++trial f /fff ++trial m /xxxx ++trial n /xxxx ++trial x none ++ ++# Prepare an included config with an error. ++ ++cat > $OBJ/sshd_config.i.3 << _EOF ++Banner xxxx ++ Junk ++_EOF ++ ++trace "disallow invalid config host=a" ++${SUDO} ${REAL_SSHD} -f $OBJ/sshd_config.i \ ++ -C "host=a,user=test,addr=127.0.0.1" 2>/dev/null && \ ++ fail "sshd include allowed invalid config" ++ ++trace "disallow invalid config host=x" ++${SUDO} ${REAL_SSHD} -f $OBJ/sshd_config.i \ ++ -C "host=x,user=test,addr=127.0.0.1" 2>/dev/null && \ ++ fail "sshd include allowed invalid config" ++ ++rm -f $OBJ/sshd_config.i.* ++ ++# Ensure that a missing include is not fatal. ++cat > $OBJ/sshd_config.i << _EOF ++HostKey $OBJ/host.ssh-ed25519 ++Include $OBJ/sshd_config.i.* ++Banner /aa ++_EOF ++ ++trial a /aa "missing include non-fatal" ++ ++# Ensure that Match/Host in an included config does not affect parent. ++cat > $OBJ/sshd_config.i.x << _EOF ++Match host x ++_EOF ++ ++trial a /aa "included file does not affect match state" ++ ++# Ensure the empty include directive is not accepted ++cat > $OBJ/sshd_config.i.x << _EOF ++Include ++_EOF ++ ++trace "disallow invalid with no argument" ++${SUDO} ${REAL_SSHD} -f $OBJ/sshd_config.i.x -T \ ++ -C "host=x,user=test,addr=127.0.0.1" 2>/dev/null && \ ++ fail "sshd allowed Include with no argument" ++ ++# Ensure the Include before any Match block works as expected (bug #3122) ++cat > $OBJ/sshd_config.i << _EOF ++Banner /xx ++HostKey $OBJ/host.ssh-ed25519 ++Include $OBJ/sshd_config.i.2 ++Match host a ++ Banner /aaaa ++_EOF ++cat > $OBJ/sshd_config.i.2 << _EOF ++Match host a ++ Banner /aa ++_EOF ++ ++trace "Include before match blocks" ++trial a /aa "included file before match blocks is properly evaluated" ++ ++# Port in included file is correctly interpretted (bug #3169) ++cat > $OBJ/sshd_config.i << _EOF ++Include $OBJ/sshd_config.i.2 ++Port 7722 ++_EOF ++cat > $OBJ/sshd_config.i.2 << _EOF ++HostKey $OBJ/host.ssh-ed25519 ++_EOF ++ ++trace "Port after included files" ++${SUDO} ${REAL_SSHD} -f $OBJ/sshd_config.i -T \ ++ -C "host=x,user=test,addr=127.0.0.1" > $OBJ/sshd_config.out || \ ++ fail "failed to parse Port after included files" ++_port=`grep -i '^port ' $OBJ/sshd_config.out | awk '{print $2}'` ++if test "x7722" != "x$_port" ; then ++ fail "The Port in included file was intertepretted wrongly. Expected 7722, got $_port" ++fi ++ ++# cleanup ++rm -f $OBJ/sshd_config.i $OBJ/sshd_config.i.* $OBJ/sshd_config.out +diff -up openssh-8.0p1/regress/test-exec.sh.sshdinclude openssh-8.0p1/regress/test-exec.sh +--- openssh-8.0p1/regress/test-exec.sh.sshdinclude 2021-10-20 15:18:49.746331150 +0200 ++++ openssh-8.0p1/regress/test-exec.sh 2021-10-20 15:19:41.324781344 +0200 +@@ -220,6 +220,7 @@ echo "exec ${SSH} -E${TEST_SSH_LOGFILE} + + chmod a+rx $OBJ/ssh-log-wrapper.sh + REAL_SSH="$SSH" ++REAL_SSHD="$SSHD" + SSH="$SSHLOGWRAP" + + # Some test data. We make a copy because some tests will overwrite it. +diff -up openssh-8.0p1/servconf.c.sshdinclude openssh-8.0p1/servconf.c +--- openssh-8.0p1/servconf.c.sshdinclude 2021-10-20 15:18:49.748331167 +0200 ++++ openssh-8.0p1/servconf.c 2021-10-20 15:22:06.303046777 +0200 +@@ -40,6 +40,11 @@ + #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" +@@ -70,6 +75,9 @@ static void add_listen_addr(ServerOption + 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; +@@ -528,7 +536,7 @@ typedef enum { + sAcceptEnv, sSetEnv, sPermitTunnel, + sMatch, sPermitOpen, sPermitListen, sForceCommand, sChrootDirectory, + sUsePrivilegeSeparation, sAllowAgentForwarding, +- sHostCertificate, ++ sHostCertificate, sInclude, + sRevokedKeys, sTrustedUserCAKeys, sAuthorizedPrincipalsFile, + sAuthorizedPrincipalsCommand, sAuthorizedPrincipalsCommandUser, + sKexAlgorithms, sCASignatureAlgorithms, sIPQoS, sVersionAddendum, +@@ -540,9 +548,11 @@ typedef enum { + sDeprecated, sIgnore, sUnsupported + } ServerOpCodes; + +-#define SSHCFG_GLOBAL 0x01 /* allowed in main section of sshd_config */ +-#define SSHCFG_MATCH 0x02 /* allowed inside a Match section */ +-#define SSHCFG_ALL (SSHCFG_GLOBAL|SSHCFG_MATCH) ++#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 { +@@ -687,6 +697,7 @@ static struct { + { "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 }, +@@ -1259,13 +1270,14 @@ static const struct multistate multistat + { NULL, -1 } + }; + +-int +-process_server_config_line(ServerOptions *options, char *line, ++static int ++process_server_config_line_depth(ServerOptions *options, char *line, + const char *filename, int linenum, int *activep, +- struct connection_info *connectinfo) ++ struct connection_info *connectinfo, int *inc_flags, int depth, ++ struct include_list *includes) + { + char ch, *cp, ***chararrayptr, **charptr, *arg, *arg2, *p; +- int cmdline = 0, *intptr, value, value2, n, port; ++ int cmdline = 0, *intptr, value, value2, n, port, oactive, r, found; + SyslogFacility *log_facility_ptr; + LogLevel *log_level_ptr; + ServerOpCodes opcode; +@@ -1274,6 +1286,8 @@ process_server_config_line(ServerOptions + long long val64; + const struct multistate *multistate_ptr; + const char *errstr; ++ struct include_item *item; ++ glob_t gbuf; + + /* Strip trailing whitespace. Allow \f (form feed) at EOL only */ + if ((len = strlen(line)) == 0) +@@ -1300,7 +1314,7 @@ process_server_config_line(ServerOptions + cmdline = 1; + activep = &cmdline; + } +- if (*activep && opcode != sMatch) ++ if (*activep && opcode != sMatch && opcode != sInclude) + debug3("%s:%d setting %s %s", filename, linenum, arg, cp); + if (*activep == 0 && !(flags & SSHCFG_MATCH)) { + if (connectinfo == NULL) { +@@ -1980,15 +1994,112 @@ process_server_config_line(ServerOptions + *intptr = value; + break; + ++ case sInclude: ++ if (cmdline) { ++ fatal("Include directive not supported as a " ++ "command-line option"); ++ } ++ value = 0; ++ while ((arg2 = strdelim(&cp)) != NULL && *arg2 != '\0') { ++ 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("%s: too many glob results", __func__); ++ 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("%s: sshbuf_new failed", ++ __func__); ++ } ++ 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: Include missing filename argument", ++ filename, linenum); ++ } ++ break; ++ + case sMatch: + if (cmdline) + fatal("Match directive not supported as a command-line " + "option"); +- value = match_cfg_line(&cp, linenum, connectinfo); ++ value = match_cfg_line(&cp, linenum, ++ (*inc_flags & SSHCFG_NEVERMATCH ? NULL : connectinfo)); + if (value < 0) + fatal("%s line %d: Bad Match condition", filename, + linenum); +- *activep = value; ++ *activep = (*inc_flags & SSHCFG_NEVERMATCH) ? 0 : value; ++ /* The MATCH_ONLY is applicable only until the first match block */ ++ *inc_flags &= ~SSHCFG_MATCH_ONLY; + break; + + case sKerberosUseKuserok: +@@ -2275,6 +2386,18 @@ process_server_config_line(ServerOptions + return 0; + } + ++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 +@@ -2313,12 +2436,13 @@ load_server_config(const char *filename, + + void + parse_server_match_config(ServerOptions *options, +- struct connection_info *connectinfo) ++ struct include_list *includes, struct connection_info *connectinfo) + { + ServerOptions mo; + + initialize_server_options(&mo); +- parse_server_config(&mo, "reprocess config", cfg, connectinfo); ++ parse_server_config(&mo, "reprocess config", cfg, includes, ++ connectinfo); + copy_set_server_options(options, &mo, 0); + } + +@@ -2464,28 +2588,44 @@ copy_set_server_options(ServerOptions *d + #undef M_CP_STROPT + #undef M_CP_STRARRAYOPT + +-void +-parse_server_config(ServerOptions *options, const char *filename, +- struct sshbuf *conf, struct connection_info *connectinfo) ++#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 active, linenum, bad_options = 0; ++ int linenum, bad_options = 0; + char *cp, *obuf, *cbuf; + +- debug2("%s: config %s len %zu", __func__, filename, sshbuf_len(conf)); ++ if (depth < 0 || depth > SERVCONF_MAX_DEPTH) ++ fatal("Too many recursive configuration includes"); ++ ++ debug2("%s: config %s len %zu%s", __func__, filename, sshbuf_len(conf), ++ (flags & SSHCFG_NEVERMATCH ? " [checking syntax only]" : "")); + + if ((obuf = cbuf = sshbuf_dup_string(conf)) == NULL) + fatal("%s: sshbuf_dup_string failed", __func__); +- active = connectinfo ? 0 : 1; + linenum = 1; + while ((cp = strsep(&cbuf, "\n")) != NULL) { +- if (process_server_config_line(options, cp, filename, +- linenum++, &active, connectinfo) != 0) ++ 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 active = connectinfo ? 0 : 1; ++ parse_server_config_depth(options, filename, conf, includes, ++ connectinfo, (connectinfo ? SSHCFG_MATCH_ONLY : 0), &active, 0); + process_queued_listen_addrs(options); + } + +diff -up openssh-8.0p1/servconf.h.sshdinclude openssh-8.0p1/servconf.h +--- openssh-8.0p1/servconf.h.sshdinclude 2021-10-20 15:18:49.750331185 +0200 ++++ openssh-8.0p1/servconf.h 2021-10-20 15:19:41.325781353 +0200 +@@ -16,6 +16,8 @@ + #ifndef SERVCONF_H + #define SERVCONF_H + ++#include ++ + #define MAX_PORTS 256 /* Max # ports. */ + + #define MAX_SUBSYSTEMS 256 /* Max # subsystems. */ +@@ -234,6 +236,15 @@ struct connection_info { + * 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 +@@ -273,12 +284,13 @@ struct connection_info *get_connection_i + 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 *); ++ int *, struct connection_info *, struct include_list *includes); + void process_permitopen(struct ssh *ssh, ServerOptions *options); + void load_server_config(const char *, struct sshbuf *); + void parse_server_config(ServerOptions *, const char *, struct sshbuf *, +- struct connection_info *); +-void parse_server_match_config(ServerOptions *, struct connection_info *); ++ struct include_list *includes, struct connection_info *); ++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 copy_set_server_options(ServerOptions *, ServerOptions *, int); +diff -up openssh-8.0p1/sshd_config.5.sshdinclude openssh-8.0p1/sshd_config.5 +--- openssh-8.0p1/sshd_config.5.sshdinclude 2021-10-20 15:18:49.754331220 +0200 ++++ openssh-8.0p1/sshd_config.5 2021-10-20 15:19:41.325781353 +0200 +@@ -825,7 +825,20 @@ during + and use only the system-wide known hosts file + .Pa /etc/ssh/known_hosts . + The default is +-.Cm no . ++.Dq no . ++.It Cm Include ++Include the specified configuration file(s). ++Multiple path names may be specified and each pathname may contain ++.Xr glob 7 ++wildcards. ++Files without absolute paths are assumed to be in ++.Pa /etc/ssh . ++A ++.Cm Include ++directive may appear inside a ++.Cm Match ++block ++to perform conditional inclusion. + .It Cm IPQoS + Specifies the IPv4 type-of-service or DSCP class for the connection. + Accepted values are +diff -up openssh-8.0p1/sshd.c.sshdinclude openssh-8.0p1/sshd.c +--- openssh-8.0p1/sshd.c.sshdinclude 2021-10-20 15:18:49.752331202 +0200 ++++ openssh-8.0p1/sshd.c 2021-10-20 15:19:41.325781353 +0200 +@@ -257,6 +257,9 @@ 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; + +@@ -927,30 +930,45 @@ usage(void) + static void + send_rexec_state(int fd, struct sshbuf *conf) + { +- struct sshbuf *m; ++ struct sshbuf *m = NULL, *inc = NULL; ++ struct include_item *item = NULL; + int r; + + debug3("%s: entering fd = %d config len %zu", __func__, fd, + sshbuf_len(conf)); + ++ if ((m = sshbuf_new()) == NULL || (inc = sshbuf_new()) == NULL) ++ fatal("%s: sshbuf_new failed", __func__); ++ ++ /* 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("%s: buffer error: %s", __func__, ssh_err(r)); ++ } ++ + /* + * Protocol from reexec master to child: + * string configuration +- * string rngseed (only if OpenSSL is not self-seeded) ++ * string included_files[] { ++ * string selector ++ * string filename ++ * string contents ++ * } ++ * string rng_seed (if required) + */ +- if ((m = sshbuf_new()) == NULL) +- fatal("%s: sshbuf_new failed", __func__); +- if ((r = sshbuf_put_stringb(m, conf)) != 0) ++ if ((r = sshbuf_put_stringb(m, conf)) != 0 || ++ (r = sshbuf_put_stringb(m, inc)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); +- + #if defined(WITH_OPENSSL) && !defined(OPENSSL_PRNG_ONLY) + rexec_send_rng_seed(m); + #endif +- + if (ssh_msg_send(fd, 0, m) == -1) + fatal("%s: ssh_msg_send failed", __func__); + + sshbuf_free(m); ++ sshbuf_free(inc); + + debug3("%s: done", __func__); + } +@@ -958,14 +976,15 @@ send_rexec_state(int fd, struct sshbuf * + static void + recv_rexec_state(int fd, struct sshbuf *conf) + { +- struct sshbuf *m; ++ struct sshbuf *m, *inc; + u_char *cp, ver; + size_t len; + int r; ++ struct include_item *item; + + debug3("%s: entering fd = %d", __func__, fd); + +- if ((m = sshbuf_new()) == NULL) ++ if ((m = sshbuf_new()) == NULL || (inc = sshbuf_new()) == NULL) + fatal("%s: sshbuf_new failed", __func__); + if (ssh_msg_recv(fd, m) == -1) + fatal("%s: ssh_msg_recv failed", __func__); +@@ -973,14 +992,28 @@ recv_rexec_state(int fd, struct sshbuf * + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + if (ver != 0) + fatal("%s: rexec version mismatch", __func__); +- if ((r = sshbuf_get_string(m, &cp, &len)) != 0) +- fatal("%s: buffer error: %s", __func__, ssh_err(r)); +- if (conf != NULL && (r = sshbuf_put(conf, cp, len))) ++ if ((r = sshbuf_get_string(m, &cp, &len)) != 0 || ++ (r = sshbuf_get_stringb(m, inc)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ + #if defined(WITH_OPENSSL) && !defined(OPENSSL_PRNG_ONLY) + rexec_recv_rng_seed(m); + #endif + ++ if (conf != NULL && (r = sshbuf_put(conf, cp, len))) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ ++ while (sshbuf_len(inc) != 0) { ++ item = xcalloc(1, sizeof(*item)); ++ if ((item->contents = sshbuf_new()) == NULL) ++ fatal("%s: sshbuf_new failed", __func__); ++ 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("%s: buffer error: %s", __func__, ssh_err(r)); ++ TAILQ_INSERT_TAIL(&includes, item, entry); ++ } ++ + free(cp); + sshbuf_free(m); + +@@ -1661,7 +1694,7 @@ main(int ac, char **av) + case 'o': + line = xstrdup(optarg); + if (process_server_config_line(&options, line, +- "command-line", 0, NULL, NULL) != 0) ++ "command-line", 0, NULL, NULL, &includes) != 0) + exit(1); + free(line); + break; +@@ -1692,7 +1725,7 @@ main(int ac, char **av) + SYSLOG_LEVEL_INFO : options.log_level, + options.log_facility == SYSLOG_FACILITY_NOT_SET ? + SYSLOG_FACILITY_AUTH : options.log_facility, +- log_stderr || !inetd_flag); ++ log_stderr || !inetd_flag || debug_flag); + + /* + * Unset KRB5CCNAME, otherwise the user's session may inherit it from +@@ -1725,12 +1758,11 @@ main(int ac, char **av) + */ + (void)atomicio(vwrite, startup_pipe, "\0", 1); + } +- } +- else if (strcasecmp(config_file_name, "none") != 0) ++ } 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, NULL); ++ cfg, &includes, NULL); + + /* 'UsePAM no' is not supported in RHEL */ + if (! options.use_pam) +@@ -1946,7 +1978,7 @@ main(int ac, char **av) + if (connection_info == NULL) + connection_info = get_connection_info(ssh, 0, 0); + connection_info->test = 1; +- parse_server_match_config(&options, connection_info); ++ parse_server_match_config(&options, &includes, connection_info); + dump_config(&options); + } + +diff -up openssh-8.0p1/sshbuf-getput-basic.c.stringb openssh-8.0p1/sshbuf-getput-basic.c +--- openssh-8.0p1/sshbuf-getput-basic.c.stringb 2022-12-21 12:18:43.274799163 +0100 ++++ openssh-8.0p1/sshbuf-getput-basic.c 2022-12-21 12:19:19.758081516 +0100 +@@ -371,6 +371,9 @@ sshbuf_put_cstring(struct sshbuf *buf, c + int + sshbuf_put_stringb(struct sshbuf *buf, const struct sshbuf *v) + { ++ if (v == NULL) ++ return sshbuf_put_string(buf, NULL, 0); ++ + return sshbuf_put_string(buf, sshbuf_ptr(v), sshbuf_len(v)); + } + diff --git a/openssh-8.0p1-upstream-ignore-SIGPIPE.patch b/openssh-8.0p1-upstream-ignore-SIGPIPE.patch new file mode 100644 index 0000000..0e85815 --- /dev/null +++ b/openssh-8.0p1-upstream-ignore-SIGPIPE.patch @@ -0,0 +1,38 @@ +From d33ff14309e33aa79fdf95e1bc4facafa80b90a9 Mon Sep 17 00:00:00 2001 +From: Stepan Broz +Date: Tue, 25 Jun 2024 17:38:22 +0200 +Subject: [PATCH] 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 +--- + ssh.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/ssh.c b/ssh.c +index 786e26d..e037c66 100644 +--- a/ssh.c ++++ b/ssh.c +@@ -1115,6 +1115,8 @@ main(int ac, char **av) + } + } + ++ 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. +@@ -1545,7 +1547,6 @@ main(int ac, char **av) + options.num_system_hostfiles); + tilde_expand_paths(options.user_hostfiles, options.num_user_hostfiles); + +- signal(SIGPIPE, SIG_IGN); /* ignore SIGPIPE early */ + signal(SIGCHLD, main_sigchld_handler); + + /* Log into the remote system. Never returns if the login fails. */ +-- +2.45.2 + diff --git a/openssh-8.0p1-x11-without-ipv6.patch b/openssh-8.0p1-x11-without-ipv6.patch new file mode 100644 index 0000000..0623b47 --- /dev/null +++ b/openssh-8.0p1-x11-without-ipv6.patch @@ -0,0 +1,30 @@ +diff --git a/channels.c b/channels.c +--- a/channels.c ++++ b/channels.c +@@ -3933,16 +3933,26 @@ x11_create_display_inet(int x11_display_ + 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) < 0) { + debug2("%s: bind port %d: %.100s", __func__, + port, strerror(errno)); + close(sock); ++ ++ /* do not remove successfully opened ++ * sockets if the request failed because ++ * the protocol IPv4/6 is not available ++ * (e.g. IPv6 may be disabled while being ++ * supported) ++ */ ++ if (EADDRNOTAVAIL == errno) ++ continue; ++ + for (n = 0; n < num_socks; n++) + close(socks[n]); + num_socks = 0; + break; + } + socks[num_socks++] = sock; + if (num_socks == NUM_SOCKS) + break; diff --git a/openssh-8.7p1-minimize-sha1-use.patch b/openssh-8.7p1-minimize-sha1-use.patch new file mode 100644 index 0000000..e5a8dee --- /dev/null +++ b/openssh-8.7p1-minimize-sha1-use.patch @@ -0,0 +1,166 @@ +diff --color -ru a/kex.c b/kex.c +--- a/kex.c 2022-06-23 10:25:29.529922670 +0200 ++++ b/kex.c 2022-06-23 10:26:12.911762100 +0200 +@@ -906,6 +906,18 @@ + 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) + { +@@ -941,6 +953,16 @@ + 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) { +diff --color -ru a/kex.h b/kex.h +--- a/kex.h 2022-06-23 10:25:29.511922322 +0200 ++++ b/kex.h 2022-06-23 10:26:12.902761926 +0200 +@@ -117,6 +117,8 @@ + + #define KEX_INIT_SENT 0x0001 + #define KEX_INITIAL 0x0002 ++#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 */ + + struct sshenc { + char *name; +diff --color -ru a/serverloop.c b/serverloop.c +--- a/serverloop.c 2022-06-23 10:25:29.537922825 +0200 ++++ b/serverloop.c 2022-06-23 10:26:12.918762235 +0200 +@@ -736,16 +736,17 @@ + struct sshbuf *resp = NULL; + struct sshbuf *sigbuf = NULL; + struct sshkey *key = NULL, *key_pub = NULL, *key_prv = NULL; +- int r, ndx, kexsigtype, use_kexsigtype, success = 0; ++ 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("%s: sshbuf_new", __func__); +- +- kexsigtype = sshkey_type_plain( +- sshkey_type_from_name(ssh->kex->hostkey_alg)); ++ 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; +@@ -780,16 +781,24 @@ + * For RSA keys, prefer to use the signature type negotiated + * during KEX to the default (SHA1). + */ +- use_kexsigtype = kexsigtype == KEY_RSA && +- sshkey_type_plain(key->type) == KEY_RSA; ++ 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("%s: sign %s key (index %d) using sigalg %s", __func__, ++ sshkey_type(key), ndx, sigalg == NULL ? "default" : sigalg); + if ((r = sshbuf_put_cstring(sigbuf, + "hostkeys-prove-00@openssh.com")) != 0 || + (r = sshbuf_put_string(sigbuf, + ssh->kex->session_id, ssh->kex->session_id_len)) != 0 || + (r = sshkey_puts(key, sigbuf)) != 0 || + (r = ssh->kex->sign(ssh, key_prv, key_pub, &sig, &slen, +- sshbuf_ptr(sigbuf), sshbuf_len(sigbuf), +- use_kexsigtype ? ssh->kex->hostkey_alg : NULL)) != 0 || ++ sshbuf_ptr(sigbuf), sshbuf_len(sigbuf), sigalg)) != 0 || + (r = sshbuf_put_string(resp, sig, slen)) != 0) { + error("%s: couldn't prepare signature: %s", + __func__, ssh_err(r)); +diff --color -ru a/sshkey.c b/sshkey.c +--- a/sshkey.c 2022-06-23 10:25:29.532922728 +0200 ++++ b/sshkey.c 2022-06-23 10:26:12.914762158 +0200 +@@ -82,7 +82,6 @@ + struct sshbuf *buf, enum sshkey_serialize_rep); + static int sshkey_from_blob_internal(struct sshbuf *buf, + struct sshkey **keyp, int allow_cert); +-static int get_sigtype(const u_char *sig, size_t siglen, char **sigtypep); + + /* Supported key types */ + struct keytype { +@@ -2092,7 +2091,8 @@ + if ((ret = sshkey_verify(key->cert->signature_key, sig, slen, + sshbuf_ptr(key->cert->certblob), signed_len, NULL, 0)) != 0) + goto out; +- if ((ret = get_sigtype(sig, slen, &key->cert->signature_type)) != 0) ++ if ((ret = sshkey_get_sigtype(sig, slen, ++ &key->cert->signature_type)) != 0) + goto out; + + /* Success */ +@@ -2394,8 +2394,8 @@ + return r; + } + +-static int +-get_sigtype(const u_char *sig, size_t siglen, char **sigtypep) ++int ++sshkey_get_sigtype(const u_char *sig, size_t siglen, char **sigtypep) + { + int r; + struct sshbuf *b = NULL; +@@ -2477,7 +2477,7 @@ + return 0; + if ((expected_alg = sshkey_sigalg_by_name(requested_alg)) == NULL) + return SSH_ERR_INVALID_ARGUMENT; +- if ((r = get_sigtype(sig, siglen, &sigtype)) != 0) ++ if ((r = sshkey_get_sigtype(sig, siglen, &sigtype)) != 0) + return r; + r = strcmp(expected_alg, sigtype) == 0; + free(sigtype); +@@ -2739,7 +2739,7 @@ + sshbuf_len(cert), alg, 0, signer_ctx)) != 0) + goto out; + /* Check and update signature_type against what was actually used */ +- if ((ret = get_sigtype(sig_blob, sig_len, &sigtype)) != 0) ++ 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; +diff --color -ru a/sshkey.h b/sshkey.h +--- a/sshkey.h 2022-06-23 10:25:29.521922515 +0200 ++++ b/sshkey.h 2022-06-23 10:26:12.907762022 +0200 +@@ -211,6 +211,7 @@ + const u_char *, size_t, const char *, u_int); + int sshkey_check_sigtype(const u_char *, size_t, const char *); + const char *sshkey_sigalg_by_name(const char *); ++int sshkey_get_sigtype(const u_char *, size_t, char **); + + /* for debug */ + void sshkey_dump_ec_point(const EC_GROUP *, const EC_POINT *); diff --git a/openssh-8.7p1-scp-kill-switch.patch b/openssh-8.7p1-scp-kill-switch.patch new file mode 100644 index 0000000..cbfbf56 --- /dev/null +++ b/openssh-8.7p1-scp-kill-switch.patch @@ -0,0 +1,46 @@ +diff -up openssh-8.7p1/pathnames.h.kill-scp openssh-8.7p1/pathnames.h +--- openssh-8.7p1/pathnames.h.kill-scp 2021-09-16 11:37:57.240171687 +0200 ++++ openssh-8.7p1/pathnames.h 2021-09-16 11:42:29.183427917 +0200 +@@ -42,6 +42,7 @@ + #define _PATH_HOST_XMSS_KEY_FILE SSHDIR "/ssh_host_xmss_key" + #define _PATH_HOST_RSA_KEY_FILE SSHDIR "/ssh_host_rsa_key" + #define _PATH_DH_MODULI SSHDIR "/moduli" ++#define _PATH_SCP_KILL_SWITCH SSHDIR "/disable_scp" + + #ifndef _PATH_SSH_PROGRAM + #define _PATH_SSH_PROGRAM "/usr/bin/ssh" +diff -up openssh-8.7p1/scp.1.kill-scp openssh-8.7p1/scp.1 +--- openssh-8.7p1/scp.1.kill-scp 2021-09-16 12:09:02.646714578 +0200 ++++ openssh-8.7p1/scp.1 2021-09-16 12:26:49.978628226 +0200 +@@ -278,6 +278,13 @@ to print debugging messages about their + This is helpful in + debugging connection, authentication, and configuration problems. + .El ++.Pp ++Usage of SCP protocol can be blocked by creating a world-readable ++.Ar /etc/ssh/disable_scp ++file. If this file exists, when SCP protocol is in use (either remotely or ++via the ++.Fl O ++option), the program will exit. + .Sh EXIT STATUS + .Ex -std scp + .Sh SEE ALSO +diff -up openssh-8.7p1/scp.c.kill-scp openssh-8.7p1/scp.c +--- openssh-8.7p1/scp.c.kill-scp 2021-09-16 11:42:56.013650519 +0200 ++++ openssh-8.7p1/scp.c 2021-09-16 11:53:03.249713836 +0200 +@@ -596,6 +596,14 @@ main(int argc, char **argv) + argc -= optind; + argv += optind; + ++ { ++ FILE *f = fopen(_PATH_SCP_KILL_SWITCH, "r"); ++ if (f != NULL) { ++ fclose(f); ++ fatal("SCP protocol is forbidden via %s", _PATH_SCP_KILL_SWITCH); ++ } ++ } ++ + if ((pwd = getpwuid(userid = getuid())) == NULL) + fatal("unknown user %u", (u_int) userid); + diff --git a/openssh-8.7p1-upstream-cve-2021-41617.patch b/openssh-8.7p1-upstream-cve-2021-41617.patch new file mode 100644 index 0000000..a68aebd --- /dev/null +++ b/openssh-8.7p1-upstream-cve-2021-41617.patch @@ -0,0 +1,25 @@ +diff --git a/auth.c b/auth.c +index b8d1040d..0134d694 100644 +--- a/auth.c ++++ b/auth.c +@@ -56,6 +56,7 @@ + # include + #endif + #include ++#include + #ifdef HAVE_LOGIN_H + #include + #endif +@@ -2695,6 +2696,12 @@ subprocess(const char *tag, const char *command, + } + 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); ++ } + /* Don't use permanently_set_uid() here to avoid fatal() */ + if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0) { + error("%s: setresgid %u: %s", tag, (u_int)pw->pw_gid, diff --git a/openssh-9.1p1-sshbanner.patch b/openssh-9.1p1-sshbanner.patch new file mode 100644 index 0000000..0e40770 --- /dev/null +++ b/openssh-9.1p1-sshbanner.patch @@ -0,0 +1,32 @@ +diff --git a/ssh-keyscan.c b/ssh-keyscan.c +index d29a03b4..d7283136 100644 +--- a/ssh-keyscan.c ++++ b/ssh-keyscan.c +@@ -490,6 +490,15 @@ congreet(int s) + return; + } + ++ /* ++ * Read the server banner as per RFC4253 section 4.2. The "SSH-" ++ * protocol identification string may be preceeded by an arbitarily ++ * large banner which we must read and ignore. Loop while reading ++ * newline-terminated lines until we have one starting with "SSH-". ++ * The ID string cannot be longer than 255 characters although the ++ * preceeding banner lines may (in which case they'll be discarded ++ * in multiple iterations of the outer loop). ++ */ + for (;;) { + memset(buf, '\0', sizeof(buf)); + bufsiz = sizeof(buf); +@@ -517,6 +526,11 @@ congreet(int s) + conrecycle(s); + return; + } ++ if (cp >= buf + sizeof(buf)) { ++ error("%s: greeting exceeds allowable length", c->c_name); ++ confree(s); ++ return; ++ } + if (*cp != '\n' && *cp != '\r') { + error("%s: bad greeting", c->c_name); + confree(s); diff --git a/openssh-9.3p1-upstream-cve-2023-38408.patch b/openssh-9.3p1-upstream-cve-2023-38408.patch new file mode 100644 index 0000000..5632ba1 --- /dev/null +++ b/openssh-9.3p1-upstream-cve-2023-38408.patch @@ -0,0 +1,17 @@ +diff --git a/ssh-pkcs11.c b/ssh-pkcs11.c +index 6be647ec..ebddf6c3 100644 +--- a/ssh-pkcs11.c ++++ b/ssh-pkcs11.c +@@ -1537,10 +1537,8 @@ pkcs11_register_provider(char *provider_id, char *pin, + error("dlopen %s failed: %s", provider_module, dlerror()); + goto fail; + } +- if ((getfunctionlist = dlsym(handle, "C_GetFunctionList")) == NULL) { +- error("dlsym(C_GetFunctionList) failed: %s", dlerror()); +- goto fail; +- } ++ if ((getfunctionlist = dlsym(handle, "C_GetFunctionList")) == NULL) ++ fatal("dlsym(C_GetFunctionList) failed: %s", dlerror()); + + p->module->handle = handle; + /* setup the pkcs11 callbacks */ diff --git a/openssh-9.4p2-limit-delay.patch b/openssh-9.4p2-limit-delay.patch new file mode 100644 index 0000000..009fcc7 --- /dev/null +++ b/openssh-9.4p2-limit-delay.patch @@ -0,0 +1,33 @@ +diff -u -p -r1.166 auth2.c +--- a/auth2.c 8 Mar 2023 04:43:12 -0000 1.166 ++++ b/auth2.c 28 Aug 2023 08:32:44 -0000 +@@ -208,6 +208,7 @@ input_service_request(int type, u_int32_ + } + + #define MIN_FAIL_DELAY_SECONDS 0.005 ++#define MAX_FAIL_DELAY_SECONDS 5.0 + static double + user_specific_delay(const char *user) + { +@@ -233,6 +234,12 @@ ensure_minimum_time_since(double start, + struct timespec ts; + double elapsed = monotime_double() - start, req = seconds, remain; + ++ if (elapsed > MAX_FAIL_DELAY_SECONDS) { ++ debug3("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; +@@ -317,7 +324,7 @@ input_userauth_request(int type, u_int32 + debug2("input_userauth_request: try method %s", method); + authenticated = m->userauth(ssh); + } +- 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); diff --git a/openssh-9.6p1-CVE-2023-48795.patch b/openssh-9.6p1-CVE-2023-48795.patch new file mode 100644 index 0000000..16ff4c4 --- /dev/null +++ b/openssh-9.6p1-CVE-2023-48795.patch @@ -0,0 +1,447 @@ +diff --git a/PROTOCOL b/PROTOCOL +index d453c779..ded935eb 100644 +--- a/PROTOCOL ++++ b/PROTOCOL +@@ -137,6 +137,32 @@ than as a named global or channel request to allow pings with very + described at: + http://git.libssh.org/users/aris/libssh.git/plain/doc/curve25519-sha256@libssh.org.txt?h=curve25519 + ++1.9 transport: strict key exchange extension ++ ++OpenSSH supports a number of transport-layer hardening measures under ++a "strict KEX" feature. This feature is signalled similarly to the ++RFC8308 ext-info feature: by including a additional algorithm in the ++initiial SSH2_MSG_KEXINIT kex_algorithms field. The client may append ++"kex-strict-c-v00@openssh.com" to its kex_algorithms and the server ++may append "kex-strict-s-v00@openssh.com". These pseudo-algorithms ++are only valid in the initial SSH2_MSG_KEXINIT and MUST be ignored ++if they are present in subsequent SSH2_MSG_KEXINIT packets. ++ ++When an endpoint that supports this extension observes this algorithm ++name in a peer's KEXINIT packet, it MUST make the following changes to ++the the protocol: ++ ++a) During initial KEX, terminate the connection if any unexpected or ++ out-of-sequence packet is received. This includes terminating the ++ connection if the first packet received is not SSH2_MSG_KEXINIT. ++ Unexpected packets for the purpose of strict KEX include messages ++ that are otherwise valid at any time during the connection such as ++ SSH2_MSG_DEBUG and SSH2_MSG_IGNORE. ++b) After sending or receiving a SSH2_MSG_NEWKEYS message, reset the ++ packet sequence number to zero. This behaviour persists for the ++ duration of the connection (i.e. not just the first ++ SSH2_MSG_NEWKEYS). ++ + 2. Connection protocol changes + + 2.1. connection: Channel write close extension "eow@openssh.com" +diff --git a/kex.c b/kex.c +index aa5e792d..d478ff6e 100644 +--- a/kex.c ++++ b/kex.c +@@ -65,7 +65,7 @@ + #endif + + /* prototype */ +-static int kex_choose_conf(struct ssh *); ++static int kex_choose_conf(struct ssh *, uint32_t seq); + static int kex_input_newkeys(int, u_int32_t, struct ssh *); + + static const char *proposal_names[PROPOSAL_MAX] = { +@@ -177,6 +177,18 @@ kex_names_valid(const char *names) + 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; ++} ++ + /* + * Concatenate algorithm names, avoiding duplicates in the process. + * Caller must free returned string. +@@ -184,7 +196,7 @@ kex_names_valid(const char *names) + char * + kex_names_cat(const char *a, const char *b) + { +- char *ret = NULL, *tmp = NULL, *cp, *p, *m; ++ char *ret = NULL, *tmp = NULL, *cp, *p; + size_t len; + + if (a == NULL || *a == '\0') +@@ -201,10 +213,8 @@ kex_names_cat(const char *a, const char *b) + } + strlcpy(ret, a, len); + for ((p = strsep(&cp, ",")); p && *p != '\0'; (p = strsep(&cp, ","))) { +- if ((m = match_list(ret, p, NULL)) != NULL) { +- free(m); ++ if (has_any_alg(ret, p)) + continue; /* Algorithm already present */ +- } + if (strlcat(ret, ",", len) >= len || + strlcat(ret, p, len) >= len) { + free(tmp); +@@ -466,7 +485,12 @@ 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 in strict mode, any unexpected message is an error */ ++ if ((ssh->kex->flags & KEX_INITIAL) && ssh->kex->kex_strict) { ++ ssh_packet_disconnect(ssh, "strict KEX violation: " ++ "unexpected packet type %u (seqnr %u)", type, seq); ++ } ++ error("type %u 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) +@@ -548,6 +572,11 @@ kex_input_ext_info(int type, u_int32_t seq, struct ssh *ssh) + 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 dispatch_protocol_error(type, seq, ssh); ++ } + for (i = 0; i < ninfo; i++) { + if ((r = sshpkt_get_cstring(ssh, &name, NULL)) != 0) + return r; +@@ -681,7 +705,7 @@ kex_input_kexinit(int type, u_int32_t seq, struct ssh *ssh) + if (kex == NULL) + return SSH_ERR_INVALID_ARGUMENT; + +- ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, NULL); ++ ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_protocol_error); + ptr = sshpkt_ptr(ssh, &dlen); + if ((r = sshbuf_put(kex->peer, ptr, dlen)) != 0) + return r; +@@ -717,7 +741,7 @@ kex_input_kexinit(int type, u_int32_t seq, struct ssh *ssh) + if (!(kex->flags & KEX_INIT_SENT)) + if ((r = kex_send_kexinit(ssh)) != 0) + return r; +- if ((r = kex_choose_conf(ssh)) != 0) ++ if ((r = kex_choose_conf(ssh, seq)) != 0) + return r; + + if (kex->kex_type < KEX_MAX && kex->kex[kex->kex_type] != NULL) +@@ -981,20 +1005,14 @@ proposals_match(char *my[PROPOSAL_MAX], char *peer[PROPOSAL_MAX]) + return (1); + } + +-/* returns non-zero if proposal contains any algorithm from algs */ + static int +-has_any_alg(const char *proposal, const char *algs) ++kexalgs_contains(char **peer, const char *ext) + { +- char *cp; +- +- if ((cp = match_list(proposal, algs, NULL)) == NULL) +- return 0; +- free(cp); +- return 1; ++ return has_any_alg(peer[PROPOSAL_KEX_ALGS], ext); + } + + static int +-kex_choose_conf(struct ssh *ssh) ++kex_choose_conf(struct ssh *ssh, uint32_t seq) + { + struct kex *kex = ssh->kex; + struct newkeys *newkeys; +@@ -1019,13 +1037,23 @@ kex_choose_conf(struct ssh *ssh) + 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 peer supports ext_info/kex_strict */ ++ if ((kex->flags & KEX_INITIAL) != 0) { ++ if (kex->server) { ++ kex->ext_info_c = kexalgs_contains(peer, "ext-info-c"); ++ kex->kex_strict = kexalgs_contains(peer, ++ "kex-strict-c-v00@openssh.com"); ++ } else { ++ kex->kex_strict = kexalgs_contains(peer, ++ "kex-strict-s-v00@openssh.com"); ++ } ++ if (kex->kex_strict) { ++ debug3("will use strict KEX ordering"); ++ if (seq != 0) ++ ssh_packet_disconnect(ssh, ++ "strict KEX violation: " ++ "KEXINIT was not the first packet"); ++ } + } + + /* Check whether client supports rsa-sha2 algorithms */ +diff --git a/kex.h b/kex.h +index 5f7ef784..272ebb43 100644 +--- a/kex.h ++++ b/kex.h +@@ -149,6 +149,7 @@ struct kex { + u_int kex_type; + char *server_sig_algs; + int ext_info_c; ++ int kex_strict; + struct sshbuf *my; + struct sshbuf *peer; + struct sshbuf *client_version; +diff --git a/packet.c b/packet.c +index 52017def..beb214f9 100644 +--- a/packet.c ++++ b/packet.c +@@ -1207,8 +1207,13 @@ ssh_packet_send2_wrapped(struct ssh *ssh) + sshbuf_dump(state->output, stderr); + #endif + /* increment sequence number for outgoing packets */ +- if (++state->p_send.seqnr == 0) ++ if (++state->p_send.seqnr == 0) { ++ if ((ssh->kex->flags & KEX_INITIAL) != 0) { ++ ssh_packet_disconnect(ssh, "outgoing sequence number " ++ "wrapped during initial key exchange"); ++ } + logit("outgoing seqnr wraps around"); ++ } + if (++state->p_send.packets == 0) + if (!(ssh->compat & SSH_BUG_NOREKEY)) + return SSH_ERR_NEED_REKEY; +@@ -1216,6 +1221,11 @@ ssh_packet_send2_wrapped(struct ssh *ssh) + state->p_send.bytes += len; + sshbuf_reset(state->outgoing_packet); + ++ if (type == SSH2_MSG_NEWKEYS && ssh->kex->kex_strict) { ++ debug("resetting send seqnr %u", state->p_send.seqnr); ++ state->p_send.seqnr = 0; ++ } ++ + if (type == SSH2_MSG_NEWKEYS) + r = ssh_set_newkeys(ssh, MODE_OUT); + else if (type == SSH2_MSG_USERAUTH_SUCCESS && state->server_side) +@@ -1344,8 +1354,7 @@ ssh_packet_read_seqnr(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p) + /* 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) ++ if ((r = ssh_packet_read_poll_seqnr(ssh, typep, seqnr_p)) != 0) + break; + /* If we got a packet, return it. */ + if (*typep != SSH_MSG_NONE) +@@ -1629,10 +1615,16 @@ ssh_packet_read_poll2(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p) + 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) ++ if (++state->p_read.seqnr == 0) { ++ if ((ssh->kex->flags & KEX_INITIAL) != 0) { ++ ssh_packet_disconnect(ssh, "incoming sequence number " ++ "wrapped during initial key exchange"); ++ } + logit("incoming seqnr wraps around"); ++ } + if (++state->p_read.packets == 0) + if (!(ssh->compat & SSH_BUG_NOREKEY)) + return SSH_ERR_NEED_REKEY; +@@ -1698,6 +1690,10 @@ ssh_packet_read_poll2(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p) + #endif + /* reset for next packet */ + state->packlen = 0; ++ if (*typep == SSH2_MSG_NEWKEYS && ssh->kex->kex_strict) { ++ debug("resetting read seqnr %u", state->p_read.seqnr); ++ state->p_read.seqnr = 0; ++ } + + /* do we need to rekey? */ + if (ssh_packet_need_rekeying(ssh, 0)) { +@@ -1720,10 +1716,39 @@ ssh_packet_read_poll_seqnr(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p) + 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)); ++ if (*typep == 0) { ++ /* no message ready */ ++ return 0; + } ++ state->keep_alive_timeouts = 0; ++ DBG(debug("received packet type %d", *typep)); ++ ++ /* Always process disconnect messages */ ++ if (*typep == 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; ++ } ++ ++ /* ++ * Do not implicitly handle any messages here during initial ++ * KEX when in strict mode. They will be need to be allowed ++ * explicitly by the KEX dispatch table or they will generate ++ * protocol errors. ++ */ ++ if (ssh->kex != NULL && ++ (ssh->kex->flags & KEX_INITIAL) && ssh->kex->kex_strict) ++ return 0; ++ /* Implicitly handle transport-level messages */ + switch (*typep) { + case SSH2_MSG_IGNORE: + debug3("Received SSH2_MSG_IGNORE"); +@@ -1738,19 +1763,6 @@ ssh_packet_read_poll_seqnr(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p) + 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; +@@ -2242,6 +2254,7 @@ kex_to_blob(struct sshbuf *m, struct kex *kex) + (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_u32(m, kex->kex_strict)) != 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 || +@@ -2404,6 +2417,7 @@ kex_from_blob(struct sshbuf *m, struct kex **kexp) + (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_u32(m, &kex->kex_strict)) != 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 || +@@ -2732,6 +2746,7 @@ sshpkt_disconnect(struct ssh *ssh, const char *fmt,...) + vsnprintf(buf, sizeof(buf), fmt, args); + va_end(args); + ++ debug2("sending SSH2_MSG_DISCONNECT: %s", buf); + 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 || +diff --git a/sshconnect2.c b/sshconnect2.c +index df6caf81..0cccbcc4 100644 +--- a/sshconnect2.c ++++ b/sshconnect2.c +@@ -253,7 +253,8 @@ ssh_kex2(struct ssh *ssh, char *host, st + xxx_host = host; + xxx_hostaddr = hostaddr; + +- if ((s = kex_names_cat(options.kex_algorithms, "ext-info-c")) == NULL) ++ if ((s = kex_names_cat(options.kex_algorithms, ++ "ext-info-c,kex-strict-c-v00@openssh.com")) == NULL) + fatal("%s: kex_names_cat", __func__); + myproposal[PROPOSAL_KEX_ALGS] = compat_kex_proposal(s); + myproposal[PROPOSAL_ENC_ALGS_CTOS] = +@@ -358,7 +358,6 @@ struct cauthmethod { + }; + + static int input_userauth_service_accept(int, u_int32_t, struct ssh *); +-static int input_userauth_ext_info(int, u_int32_t, struct ssh *); + static int input_userauth_success(int, u_int32_t, struct ssh *); + static int input_userauth_failure(int, u_int32_t, struct ssh *); + static int input_userauth_banner(int, u_int32_t, struct ssh *); +@@ -472,7 +471,7 @@ ssh_userauth2(struct ssh *ssh, const char *local_user, + + ssh->authctxt = &authctxt; + ssh_dispatch_init(ssh, &input_userauth_error); +- ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &input_userauth_ext_info); ++ ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, kex_input_ext_info); + ssh_dispatch_set(ssh, SSH2_MSG_SERVICE_ACCEPT, &input_userauth_service_accept); + ssh_dispatch_run_fatal(ssh, DISPATCH_BLOCK, &authctxt.success); /* loop until success */ + pubkey_cleanup(ssh); +@@ -531,12 +530,6 @@ input_userauth_service_accept(int type, u_int32_t seq, struct ssh *ssh) + } + + /* ARGSUSED */ +-static int +-input_userauth_ext_info(int type, u_int32_t seqnr, struct ssh *ssh) +-{ +- return kex_input_ext_info(type, seqnr, ssh); +-} +- + void + userauth(struct ssh *ssh, char *authlist) + { +@@ -615,6 +608,7 @@ input_userauth_success(int type, u_int32_t seq, struct ssh *ssh) + free(authctxt->methoddata); + authctxt->methoddata = NULL; + authctxt->success = 1; /* break out */ ++ ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, dispatch_protocol_error); + return 0; + } + +diff -up openssh-8.7p1/sshd.c.kexstrict openssh-8.7p1/sshd.c +--- openssh-8.7p1/sshd.c.kexstrict 2023-11-27 13:19:18.855433602 +0100 ++++ openssh-8.7p1/sshd.c 2023-11-27 13:28:10.441325314 +0100 +@@ -2531,10 +2531,14 @@ do_ssh2_kex(struct ssh *ssh) + { + char *myproposal[PROPOSAL_MAX] = { KEX_SERVER }; + struct kex *kex; ++ char *cp; + int r; + +- myproposal[PROPOSAL_KEX_ALGS] = compat_kex_proposal( +- options.kex_algorithms); ++ if ((cp = kex_names_cat(options.kex_algorithms, ++ "kex-strict-s-v00@openssh.com")) == NULL) ++ fatal("kex_names_cat"); ++ ++ myproposal[PROPOSAL_KEX_ALGS] = compat_kex_proposal(cp); + myproposal[PROPOSAL_ENC_ALGS_CTOS] = compat_cipher_proposal( + options.ciphers); + myproposal[PROPOSAL_ENC_ALGS_STOC] = compat_cipher_proposal( +@@ -2586,7 +2586,7 @@ do_ssh2_kex(struct ssh *ssh) + if (gss && orig) + xasprintf(&newstr, "%s,%s", gss, orig); + else if (gss) +- newstr = gss; ++ xasprintf(&newstr, "%s,%s", gss, "kex-strict-s-v00@openssh.com"); + else if (orig) + newstr = orig; + +@@ -2650,6 +2654,7 @@ do_ssh2_kex(struct ssh *ssh) + packet_send(); + packet_write_wait(); + #endif ++ free(cp); + debug("KEX done"); + } + diff --git a/openssh-9.6p1-CVE-2023-51385.patch b/openssh-9.6p1-CVE-2023-51385.patch new file mode 100644 index 0000000..a7e6a32 --- /dev/null +++ b/openssh-9.6p1-CVE-2023-51385.patch @@ -0,0 +1,57 @@ +diff --git a/ssh.c b/ssh.c +index 35c48e62..48d93ddf 100644 +--- a/ssh.c ++++ b/ssh.c +@@ -626,6 +626,41 @@ ssh_conn_info_free(struct ssh_conn_info *cinfo) + } + } + ++static int ++valid_hostname(const char *s) ++{ ++ size_t i; ++ ++ if (*s == '-') ++ return 0; ++ for (i = 0; s[i] != 0; i++) { ++ if (strchr("'`\"$\\;&<>|(){}", s[i]) != NULL || ++ isspace((u_char)s[i]) || iscntrl((u_char)s[i])) ++ return 0; ++ } ++ return 1; ++} ++ ++static int ++valid_ruser(const char *s) ++{ ++ size_t i; ++ ++ if (*s == '-') ++ return 0; ++ for (i = 0; s[i] != 0; i++) { ++ if (strchr("'`\";&<>|(){}", s[i]) != NULL) ++ return 0; ++ /* Disallow '-' after whitespace */ ++ if (isspace((u_char)s[i]) && s[i + 1] == '-') ++ return 0; ++ /* Disallow \ in last position */ ++ if (s[i] == '\\' && s[i + 1] == '\0') ++ return 0; ++ } ++ return 1; ++} ++ + /* + * Main program for the ssh client. + */ +@@ -1118,6 +1153,10 @@ main(int ac, char **av) + if (!host) + usage(); + ++ if (!valid_hostname(host)) ++ fatal("hostname contains invalid characters"); ++ if (options.user != NULL && !valid_ruser(options.user)) ++ fatal("remote username contains invalid characters"); + host_arg = xstrdup(host); + + /* Initialize the command to execute on remote host. */ diff --git a/sources b/sources index c629991..bbbcdc2 100644 --- a/sources +++ b/sources @@ -1,3 +1,3 @@ -SHA512 (openssh-7.8p1.tar.gz) = 8e5b0c8682a9243e4e8b7c374ec989dccd1a752eb6f84e593b67141e8b23dcc8b9a7322b1f7525d18e2ce8830a767d0d9793f997486339db201a57986b910705 -SHA512 (openssh-7.8p1.tar.gz.asc) = 3a7bef84df3c07aa78965a11a6bbd6ca6e5d1e9265ac08871b3e5d304646be651b74f5302a195e86a56e6a83b19d79292e5599c9a9cf6f003a513d4354e8ad2f +SHA512 (openssh-8.0p1.tar.gz) = e280fa2d56f550efd37c5d2477670326261aa8b94d991f9eb17aad90e0c6c9c939efa90fe87d33260d0f709485cb05c379f0fd1bd44fc0d5190298b6398c9982 +SHA512 (openssh-8.0p1.tar.gz.asc) = fe9e7383d9467e869762864f2b719165d9a3f2c5316c07067df1d45fc7819bd2cb8ef758454865595688804a4c160dd3d3aaee4c5f887859555d2c7bb8c4592b SHA512 (DJM-GPG-KEY.gpg) = db1191ed9b6495999e05eed2ef863fb5179bdb63e94850f192dad68eed8579836f88fbcfffd9f28524fe1457aff8cd248ee3e0afc112c8f609b99a34b80ecc0d