From 089d7989310f6637997e0d9f1a845d7af479dca1 Mon Sep 17 00:00:00 2001 From: Dmitry Belyavskiy Date: Thu, 25 Jul 2024 12:54:19 +0200 Subject: [PATCH] Rebase OpenSSH to 9.8p1 Resolves: RHEL-42635 --- .gitignore | 2 + openssh-6.6.1p1-log-in-chroot.patch | 8 +- openssh-6.6.1p1-selinux-contexts.patch | 6 +- openssh-6.6p1-GSSAPIEnablek5users.patch | 9 +- openssh-6.6p1-allow-ip-opts.patch | 4 +- openssh-6.6p1-keycat.patch | 10 +- openssh-6.6p1-kuserok.patch | 4 +- openssh-6.6p1-privsep-selinux.patch | 23 +- openssh-6.7p1-coverity.patch | 8 +- openssh-7.1p2-audit-race-condition.patch | 13 +- openssh-7.2p2-x11.patch | 4 +- openssh-7.3p1-x11-max-displays.patch | 2 +- openssh-7.4p1-systemd.patch | 98 - openssh-7.6p1-audit.patch | 183 +- openssh-7.6p1-cleanup-selinux.patch | 50 +- openssh-7.7p1-fips.patch | 47 +- openssh-7.7p1-gssapi-new-unique.patch | 12 +- openssh-7.8p1-UsePAM-warning.patch | 14 +- openssh-7.8p1-role-mls.patch | 28 +- openssh-8.0p1-crypto-policies.patch | 32 +- openssh-8.0p1-gssapi-keyex.patch | 334 +-- openssh-8.0p1-pkcs11-uri.patch | 2 +- openssh-8.7p1-minrsabits.patch | 2 +- openssh-8.7p1-negotiate-supported-algs.patch | 11 +- openssh-8.7p1-nohostsha1proof.patch | 132 +- openssh-9.0p1-evp-fips-dh.patch | 2 +- openssh-9.6p1-gsskex-new-api.patch | 1965 ++++++++++++++++++ openssh-9.6p1-pam-rhost.patch | 32 + openssh.spec | 26 +- parallel_test.Makefile | 14 + parallel_test.sh | 91 + sources | 5 +- 32 files changed, 2483 insertions(+), 690 deletions(-) delete mode 100644 openssh-7.4p1-systemd.patch create mode 100644 openssh-9.6p1-gsskex-new-api.patch create mode 100644 openssh-9.6p1-pam-rhost.patch create mode 100644 parallel_test.Makefile create mode 100755 parallel_test.sh diff --git a/.gitignore b/.gitignore index 6988bb2..f760b87 100644 --- a/.gitignore +++ b/.gitignore @@ -60,3 +60,5 @@ pam_ssh_agent_auth-0.9.2.tar.bz2 /openssh-9.3p1.tar.gz.asc /openssh-9.6p1.tar.gz /openssh-9.6p1.tar.gz.asc +/openssh-9.8p1.tar.gz +/openssh-9.8p1.tar.gz.asc diff --git a/openssh-6.6.1p1-log-in-chroot.patch b/openssh-6.6.1p1-log-in-chroot.patch index 941c694..0f65279 100644 --- a/openssh-6.6.1p1-log-in-chroot.patch +++ b/openssh-6.6.1p1-log-in-chroot.patch @@ -237,11 +237,11 @@ diff -up openssh-8.6p1/sftp-server-main.c.log-in-chroot openssh-8.6p1/sftp-serve - return (sftp_server_main(argc, argv, user_pw)); + return (sftp_server_main(argc, argv, user_pw, 0)); } -diff -up openssh-8.6p1/sshd.c.log-in-chroot openssh-8.6p1/sshd.c ---- openssh-8.6p1/sshd.c.log-in-chroot 2021-04-19 14:43:08.543843426 +0200 -+++ openssh-8.6p1/sshd.c 2021-04-19 14:43:08.545843441 +0200 +diff -up openssh-8.6p1/sshd-session.c.log-in-chroot openssh-8.6p1/sshd-session.c +--- openssh-8.6p1/sshd-session.c.log-in-chroot 2021-04-19 14:43:08.543843426 +0200 ++++ openssh-8.6p1/sshd-session.c 2021-04-19 14:43:08.545843441 +0200 @@ -559,7 +559,7 @@ privsep_postauth(struct ssh *ssh, Authct - } + #endif /* New socket pair */ - monitor_reinit(pmonitor); diff --git a/openssh-6.6.1p1-selinux-contexts.patch b/openssh-6.6.1p1-selinux-contexts.patch index eab724a..96161cb 100644 --- a/openssh-6.6.1p1-selinux-contexts.patch +++ b/openssh-6.6.1p1-selinux-contexts.patch @@ -116,10 +116,10 @@ index cb51f99..8b7cda2 100644 #endif #ifdef LINUX_OOM_ADJUST -diff --git a/sshd.c b/sshd.c +diff --git a/sshd-session.c b/sshd-session.c index 2871fe9..39b9c08 100644 ---- a/sshd.c -+++ b/sshd.c +--- a/sshd-session.c ++++ b/sshd-session.c @@ -629,7 +629,7 @@ privsep_preauth_child(void) demote_sensitive_data(); diff --git a/openssh-6.6p1-GSSAPIEnablek5users.patch b/openssh-6.6p1-GSSAPIEnablek5users.patch index cccb3e0..7fef831 100644 --- a/openssh-6.6p1-GSSAPIEnablek5users.patch +++ b/openssh-6.6p1-GSSAPIEnablek5users.patch @@ -38,16 +38,16 @@ diff -up openssh-7.4p1/servconf.c.GSSAPIEnablek5users openssh-7.4p1/servconf.c if (options->password_authentication == -1) options->password_authentication = 1; if (options->kbd_interactive_authentication == -1) -@@ -418,7 +421,7 @@ typedef enum { - sHostbasedUsesNameFromPacketOnly, sHostbasedAcceptedAlgorithms, +@@ -578,7 +578,7 @@ typedef enum { sHostKeyAlgorithms, sPerSourceMaxStartups, sPerSourceNetBlockSize, + sPerSourcePenalties, sPerSourcePenaltyExemptList, sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile, - sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor, + sGssAuthentication, sGssCleanupCreds, sGssEnablek5users, sGssStrictAcceptor, sGssKeyEx, sGssKexAlgorithms, sGssStoreRekey, sAcceptEnv, sSetEnv, sPermitTunnel, sMatch, sPermitOpen, sPermitListen, sForceCommand, sChrootDirectory, -@@ -497,14 +500,16 @@ static struct { +@@ -600,14 +600,16 @@ static struct { { "gssapikeyexchange", sGssKeyEx, SSHCFG_GLOBAL }, { "gssapistorecredentialsonrekey", sGssStoreRekey, SSHCFG_GLOBAL }, { "gssapikexalgorithms", sGssKexAlgorithms, SSHCFG_GLOBAL }, @@ -129,3 +129,6 @@ diff -up openssh-7.4p1/sshd_config.GSSAPIEnablek5users openssh-7.4p1/sshd_config # Set this to 'yes' to enable PAM authentication, account processing, # and session processing. If this is enabled, PAM authentication will +diff -up openssh-9.8p1/servconf.c.xxx openssh-9.8p1/servconf.c +--- openssh-9.8p1/servconf.c.xxx 2024-07-11 13:51:19.969960781 +0200 ++++ openssh-9.8p1/servconf.c 2024-07-11 13:51:30.938231250 +0200 diff --git a/openssh-6.6p1-allow-ip-opts.patch b/openssh-6.6p1-allow-ip-opts.patch index be8d340..d969b5c 100644 --- a/openssh-6.6p1-allow-ip-opts.patch +++ b/openssh-6.6p1-allow-ip-opts.patch @@ -1,6 +1,6 @@ diff -up openssh/sshd.c.ip-opts openssh/sshd.c ---- openssh/sshd.c.ip-opts 2016-07-25 13:58:48.998507834 +0200 -+++ openssh/sshd.c 2016-07-25 14:01:28.346469878 +0200 +--- openssh/sshd-session.c.ip-opts 2016-07-25 13:58:48.998507834 +0200 ++++ openssh/sshd-session.c 2016-07-25 14:01:28.346469878 +0200 @@ -1507,12 +1507,32 @@ check_ip_options(struct ssh *ssh) if (getsockopt(sock_in, IPPROTO_IP, IP_OPTIONS, opts, diff --git a/openssh-6.6p1-keycat.patch b/openssh-6.6p1-keycat.patch index 529b508..4eb0998 100644 --- a/openssh-6.6p1-keycat.patch +++ b/openssh-6.6p1-keycat.patch @@ -39,9 +39,9 @@ diff -up openssh/Makefile.in.keycat openssh/Makefile.in SFTP_SERVER=$(libexecdir)/sftp-server SSH_KEYSIGN=$(libexecdir)/ssh-keysign +SSH_KEYCAT=$(libexecdir)/ssh-keycat + SSHD_SESSION=$(libexecdir)/sshd-session SSH_PKCS11_HELPER=$(libexecdir)/ssh-pkcs11-helper SSH_SK_HELPER=$(libexecdir)/ssh-sk-helper - PRIVSEP_PATH=@PRIVSEP_PATH@ @@ -52,6 +52,7 @@ K5LIBS=@K5LIBS@ K5LIBS=@K5LIBS@ GSSLIBS=@GSSLIBS@ @@ -54,8 +54,8 @@ diff -up openssh/Makefile.in.keycat openssh/Makefile.in .SUFFIXES: .lo --TARGETS=ssh$(EXEEXT) sshd$(EXEEXT) ssh-add$(EXEEXT) ssh-keygen$(EXEEXT) ssh-keyscan${EXEEXT} ssh-keysign${EXEEXT} ssh-pkcs11-helper$(EXEEXT) ssh-agent$(EXEEXT) scp$(EXEEXT) sftp-server$(EXEEXT) sftp$(EXEEXT) ssh-sk-helper$(EXEEXT) -+TARGETS=ssh$(EXEEXT) sshd$(EXEEXT) ssh-add$(EXEEXT) ssh-keygen$(EXEEXT) ssh-keyscan${EXEEXT} ssh-keysign${EXEEXT} ssh-pkcs11-helper$(EXEEXT) ssh-agent$(EXEEXT) scp$(EXEEXT) sftp-server$(EXEEXT) sftp$(EXEEXT) ssh-sk-helper$(EXEEXT) ssh-keycat$(EXEEXT) +-TARGETS=ssh$(EXEEXT) sshd$(EXEEXT) sshd-session$(EXEEXT) ssh-add$(EXEEXT) ssh-keygen$(EXEEXT) ssh-keyscan${EXEEXT} ssh-keysign${EXEEXT} ssh-pkcs11-helper$(EXEEXT) ssh-agent$(EXEEXT) scp$(EXEEXT) sftp-server$(EXEEXT) sftp$(EXEEXT) ssh-sk-helper$(EXEEXT) ++TARGETS=ssh$(EXEEXT) sshd$(EXEEXT) sshd-session$(EXEEXT) ssh-add$(EXEEXT) ssh-keygen$(EXEEXT) ssh-keyscan${EXEEXT} ssh-keysign${EXEEXT} ssh-pkcs11-helper$(EXEEXT) ssh-agent$(EXEEXT) scp$(EXEEXT) sftp-server$(EXEEXT) sftp$(EXEEXT) ssh-sk-helper$(EXEEXT) ssh-keycat$(EXEEXT) XMSS_OBJS=\ ssh-xmss.o \ @@ -95,8 +95,8 @@ diff -up openssh/openbsd-compat/port-linux-sshd.c.keycat openssh/openbsd-compat/ --- openssh/openbsd-compat/port-linux-sshd.c.keycat 2015-06-24 10:57:50.150849626 +0200 +++ openssh/openbsd-compat/port-linux-sshd.c 2015-06-24 10:57:50.159849603 +0200 @@ -54,6 +54,20 @@ extern Authctxt *the_authctxt; + extern Authctxt *the_authctxt; extern int inetd_flag; - extern int rexeced_flag; +/* Wrapper around is_selinux_enabled() to log its return value once only */ +int @@ -131,7 +131,7 @@ diff -up openssh/openbsd-compat/port-linux-sshd.c.keycat openssh/openbsd-compat/ - rv = do_pam_putenv("SELINUX_ROLE_REQUESTED", role ? role : ""); + rv = set_it("SELINUX_ROLE_REQUESTED", role ? role : ""); - if (inetd_flag && !rexeced_flag) { + if (inetd_flag) { use_current = "1"; } else { use_current = ""; diff --git a/openssh-6.6p1-kuserok.patch b/openssh-6.6p1-kuserok.patch index eaf4453..e43128b 100644 --- a/openssh-6.6p1-kuserok.patch +++ b/openssh-6.6p1-kuserok.patch @@ -266,8 +266,8 @@ diff -up openssh-7.4p1/sshd_config.5.kuserok openssh-7.4p1/sshd_config.5 +The default is +.Cm yes . .It Cm KexAlgorithms - Specifies the available KEX (Key Exchange) algorithms. - Multiple algorithms must be comma-separated. + Specifies the permitted KEX (Key Exchange) algorithms that the server will + offer to clients. @@ -1078,6 +1082,7 @@ Available keywords are .Cm IPQoS , .Cm KbdInteractiveAuthentication , diff --git a/openssh-6.6p1-privsep-selinux.patch b/openssh-6.6p1-privsep-selinux.patch index 8047fc3..16d98cd 100644 --- a/openssh-6.6p1-privsep-selinux.patch +++ b/openssh-6.6p1-privsep-selinux.patch @@ -49,7 +49,7 @@ diff -up openssh-7.4p1/session.c.privsep-selinux openssh-7.4p1/session.c platform_setusercontext(pw); - if (platform_privileged_uidswap()) { -+ if (platform_privileged_uidswap() && (!is_child || !use_privsep)) { ++ if (platform_privileged_uidswap() && !is_child) { #ifdef HAVE_LOGIN_CAP if (setusercontext(lc, pw, pw->pw_uid, (LOGIN_SETALL & ~(LOGIN_SETPATH|LOGIN_SETUSER))) < 0) { @@ -96,8 +96,8 @@ diff -up openssh-7.4p1/session.c.privsep-selinux openssh-7.4p1/session.c } diff -up openssh-7.4p1/sshd.c.privsep-selinux openssh-7.4p1/sshd.c ---- openssh-7.4p1/sshd.c.privsep-selinux 2016-12-23 18:58:52.973122201 +0100 -+++ openssh-7.4p1/sshd.c 2016-12-23 18:59:13.808124269 +0100 +--- openssh-7.4p1/sshd-session.c.privsep-selinux 2016-12-23 18:58:52.973122201 +0100 ++++ openssh-7.4p1/sshd-session.c 2016-12-23 18:59:13.808124269 +0100 @@ -540,6 +540,10 @@ privsep_preauth_child(void) /* Demote the private keys to public keys. */ demote_sensitive_data(); @@ -109,13 +109,12 @@ diff -up openssh-7.4p1/sshd.c.privsep-selinux openssh-7.4p1/sshd.c /* Demote the child */ if (privsep_chroot) { /* Change our root directory */ -@@ -633,6 +637,9 @@ privsep_postauth(Authctxt *authctxt) - { - #ifdef DISABLE_FD_PASSING - if (1) { -+#elif defined(WITH_SELINUX) -+ if (0) { -+ /* even root user can be confined by SELinux */ - #else - if (authctxt->pw->pw_uid == 0) { +@@ -403,7 +403,7 @@ privsep_postauth(struct ssh *ssh, Authct + * fd passing, as AFAIK PTY allocation on this platform doesn't require + * special privileges to begin with. + */ +-#if defined(DISABLE_FD_PASSING) && !defined(HAVE_CYGWIN) ++#if defined(DISABLE_FD_PASSING) && !defined(HAVE_CYGWIN) && !defined(WITH_SELINUX) + skip_privdrop = 1; #endif + diff --git a/openssh-6.7p1-coverity.patch b/openssh-6.7p1-coverity.patch index 01a061b..d98da28 100644 --- a/openssh-6.7p1-coverity.patch +++ b/openssh-6.7p1-coverity.patch @@ -27,7 +27,7 @@ diff -up openssh-8.5p1/gss-genr.c.coverity openssh-8.5p1/gss-genr.c - +#pragma GCC diagnostic ignored "-Wstringop-overflow" cp = strncpy(s, kex, strlen(kex)); -+#pragma pop ++#pragma GCC diagnostic pop for ((p = strsep(&cp, ",")); p && *p != '\0'; (p = strsep(&cp, ","))) { if (sshbuf_len(buf) != 0 && @@ -221,9 +221,9 @@ diff -up openssh-7.4p1/ssh-agent.c.coverity openssh-7.4p1/ssh-agent.c return NULL; } /* validate also provider from URI */ -diff -up openssh-7.4p1/sshd.c.coverity openssh-7.4p1/sshd.c ---- openssh-7.4p1/sshd.c.coverity 2016-12-23 16:40:26.897788690 +0100 -+++ openssh-7.4p1/sshd.c 2016-12-23 16:40:26.904788692 +0100 +diff -up openssh-7.4p1/sshd-session.c.coverity openssh-7.4p1/sshd-session.c +--- openssh-7.4p1/sshd-session.c.coverity 2016-12-23 16:40:26.897788690 +0100 ++++ openssh-7.4p1/sshd-session.c 2016-12-23 16:40:26.904788692 +0100 @@ -691,8 +691,10 @@ privsep_preauth(Authctxt *authctxt) privsep_preauth_child(ssh); diff --git a/openssh-7.1p2-audit-race-condition.patch b/openssh-7.1p2-audit-race-condition.patch index b5895f7..57ad148 100644 --- a/openssh-7.1p2-audit-race-condition.patch +++ b/openssh-7.1p2-audit-race-condition.patch @@ -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(ssh, s->command)); + s->command_handle = mm_audit_run_command(ssh, s->command); + if (pipe(paudit) < 0) + fatal("pipe: %s", strerror(errno)); #endif @@ -121,7 +121,7 @@ diff -up openssh-7.4p1/session.c.audit-race openssh-7.4p1/session.c +#ifdef SSH_AUDIT_EVENTS + close(paudit[1]); -+ if (use_privsep && ret == 0) { ++ if (ret == 0) { + /* + * Read the audit messages from forked child and send them + * back to monitor. We don't want to communicate directly, @@ -136,7 +136,7 @@ diff -up openssh-7.4p1/session.c.audit-race openssh-7.4p1/session.c return ret; } -@@ -1538,6 +1565,34 @@ child_close_fds(void) +@@ -1538,6 +1565,33 @@ child_close_fds(void) log_redirect_stderr_to(NULL); } @@ -147,12 +147,11 @@ diff -up openssh-7.4p1/session.c.audit-race openssh-7.4p1/session.c + int pparent = paudit[1]; + close(paudit[0]); + /* Hack the monitor pipe to avoid race condition with parent */ -+ if (use_privsep) -+ mm_set_monitor_pipe(pparent); ++ mm_set_monitor_pipe(pparent); +#endif + + /* remove hostkey from the child's memory */ -+ destroy_sensitive_data(ssh, use_privsep); ++ destroy_sensitive_data(ssh); + /* + * We can audit this, because we hacked the pipe to direct the + * messages over postauth child. But this message requires answer @@ -176,7 +175,7 @@ diff -up openssh-7.4p1/session.c.audit-race openssh-7.4p1/session.c sshpkt_fmt_connection_id(ssh, remote_id, sizeof(remote_id)); - /* remove hostkey from the child's memory */ -- destroy_sensitive_data(ssh, 1); +- destroy_sensitive_data(ssh); - 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. */ diff --git a/openssh-7.2p2-x11.patch b/openssh-7.2p2-x11.patch index b27d7c4..6db16be 100644 --- a/openssh-7.2p2-x11.patch +++ b/openssh-7.2p2-x11.patch @@ -11,8 +11,8 @@ diff --git a/channels.c b/channels.c int sock; struct sockaddr_un addr; -+ if (len <= 0) -+ return -1; ++ if (len <= 0) ++ return -1; sock = socket(AF_UNIX, SOCK_STREAM, 0); if (sock == -1) { error("socket: %.100s", strerror(errno)); diff --git a/openssh-7.3p1-x11-max-displays.patch b/openssh-7.3p1-x11-max-displays.patch index 2b702d4..e28c62e 100644 --- a/openssh-7.3p1-x11-max-displays.patch +++ b/openssh-7.3p1-x11-max-displays.patch @@ -2,7 +2,7 @@ diff -up openssh-7.4p1/channels.c.x11max openssh-7.4p1/channels.c --- openssh-7.4p1/channels.c.x11max 2016-12-23 15:46:32.071506625 +0100 +++ openssh-7.4p1/channels.c 2016-12-23 15:46:32.139506636 +0100 @@ -152,8 +152,8 @@ static int all_opens_permitted = 0; - #define FWD_PERMIT_ANY_HOST "*" + #define NUM_SOCKS 10 /* -- X11 forwarding */ -/* Maximum number of fake X11 displays to try. */ diff --git a/openssh-7.4p1-systemd.patch b/openssh-7.4p1-systemd.patch deleted file mode 100644 index 1242aac..0000000 --- a/openssh-7.4p1-systemd.patch +++ /dev/null @@ -1,98 +0,0 @@ -commit 0e22b79bfde45a7cf7a2e51a68ec11c4285f3b31 -Author: Jakub Jelen -Date: Mon Nov 21 15:04:06 2016 +0100 - - systemd stuff - -diff --git a/configure.ac b/configure.ac -index 2ffc369..162ce92 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -4265,6 +4265,30 @@ AC_ARG_WITH([kerberos5], - AC_SUBST([K5LIBS]) - AC_SUBST([CHANNELLIBS]) - -+# Check whether user wants systemd support -+SYSTEMD_MSG="no" -+AC_ARG_WITH(systemd, -+ [ --with-systemd Enable systemd support], -+ [ if test "x$withval" != "xno" ; then -+ AC_PATH_TOOL([PKGCONFIG], [pkg-config], [no]) -+ if test "$PKGCONFIG" != "no"; then -+ AC_MSG_CHECKING([for libsystemd]) -+ if $PKGCONFIG --exists libsystemd; then -+ SYSTEMD_CFLAGS=`$PKGCONFIG --cflags libsystemd` -+ SYSTEMD_LIBS=`$PKGCONFIG --libs libsystemd` -+ CPPFLAGS="$CPPFLAGS $SYSTEMD_CFLAGS" -+ SSHDLIBS="$SSHDLIBS $SYSTEMD_LIBS" -+ AC_MSG_RESULT([yes]) -+ AC_DEFINE(HAVE_SYSTEMD, 1, [Define if you want systemd support.]) -+ SYSTEMD_MSG="yes" -+ else -+ AC_MSG_RESULT([no]) -+ fi -+ fi -+ fi ] -+) -+ -+ - # Looking for programs, paths and files - - PRIVSEP_PATH=/var/empty -@@ -5097,6 +5121,7 @@ echo " libedit support: $LIBEDIT_MSG" - echo " Solaris process contract support: $SPC_MSG" - echo " Solaris project support: $SP_MSG" - echo " Solaris privilege support: $SPP_MSG" -+echo " systemd support: $SYSTEMD_MSG" - echo " IP address in \$DISPLAY hack: $DISPLAY_HACK_MSG" - echo " Translate v4 in v6 hack: $IPV4_IN6_HACK_MSG" - echo " BSD Auth support: $BSD_AUTH_MSG" -diff --git a/contrib/sshd.service b/contrib/sshd.service -new file mode 100644 -index 0000000..e0d4923 ---- /dev/null -+++ b/contrib/sshd.service -@@ -0,0 +1,16 @@ -+[Unit] -+Description=OpenSSH server daemon -+Documentation=man:sshd(8) man:sshd_config(5) -+After=network.target -+ -+[Service] -+Type=notify -+ExecStart=/usr/sbin/sshd -D $OPTIONS -+ExecReload=/bin/kill -HUP $MAINPID -+KillMode=process -+Restart=on-failure -+RestartPreventExitStatus=255 -+ -+[Install] -+WantedBy=multi-user.target -+ -diff --git a/sshd.c b/sshd.c -index 816611c..b8b9d13 100644 ---- a/sshd.c -+++ b/sshd.c -@@ -85,6 +85,10 @@ - #include - #endif - -+#ifdef HAVE_SYSTEMD -+#include -+#endif -+ - #include "xmalloc.h" - #include "ssh.h" - #include "ssh2.h" -@@ -1888,6 +1892,11 @@ main(int ac, char **av) - } - } - -+#ifdef HAVE_SYSTEMD -+ /* Signal systemd that we are ready to accept connections */ -+ sd_notify(0, "READY=1"); -+#endif -+ - /* Accept a connection and return in a forked child */ - server_accept_loop(&sock_in, &sock_out, - &newsock, config_s); diff --git a/openssh-7.6p1-audit.patch b/openssh-7.6p1-audit.patch index 748c4b6..c884292 100644 --- a/openssh-7.6p1-audit.patch +++ b/openssh-7.6p1-audit.patch @@ -129,19 +129,19 @@ diff -up openssh-8.6p1/audit.c.audit openssh-8.6p1/audit.c +void +audit_unsupported(struct ssh *ssh, int what) +{ -+ PRIVSEP(audit_unsupported_body(ssh, what)); ++ mm_audit_unsupported_body(ssh, what); +} + +void +audit_kex(struct ssh *ssh, int ctos, char *enc, char *mac, char *comp, char *pfs) +{ -+ PRIVSEP(audit_kex_body(ssh, ctos, enc, mac, comp, pfs, getpid(), getuid())); ++ mm_audit_kex_body(ssh, ctos, enc, mac, comp, pfs, getpid(), getuid()); +} + +void +audit_session_key_free(struct ssh *ssh, int ctos) +{ -+ PRIVSEP(audit_session_key_free_body(ssh, ctos, getpid(), getuid())); ++ mm_audit_session_key_free_body(ssh, ctos, getpid(), getuid()); +} + # ifndef CUSTOM_SSH_AUDIT_EVENTS @@ -446,7 +446,7 @@ diff -up openssh-8.6p1/audit-linux.c.audit openssh-8.6p1/audit-linux.c /* Below is the sshd audit API code */ void -@@ -76,49 +176,210 @@ audit_connection_from(const char *host, +@@ -76,49 +176,211 @@ audit_connection_from(const char *host, /* not implemented */ } @@ -525,6 +525,7 @@ diff -up openssh-8.6p1/audit-linux.c.audit openssh-8.6p1/audit-linux.c case SSH_AUTH_FAIL_PASSWD: + if (options.use_pam) + break; ++ /* Fallthrough */ + case SSH_LOGIN_EXCEED_MAXTRIES: case SSH_AUTH_FAIL_KBDINT: case SSH_AUTH_FAIL_PUBKEY: @@ -564,7 +565,7 @@ diff -up openssh-8.6p1/audit-linux.c.audit openssh-8.6p1/audit-linux.c +{ +#ifdef AUDIT_CRYPTO_SESSION + char buf[AUDIT_LOG_SIZE]; -+ const static char *name[] = { "cipher", "mac", "comp" }; ++ static const char *name[] = { "cipher", "mac", "comp" }; + char *s; + int audit_fd; + @@ -582,7 +583,7 @@ diff -up openssh-8.6p1/audit-linux.c.audit openssh-8.6p1/audit-linux.c +#endif +} + -+const static char *direction[] = { "from-server", "from-client", "both" }; ++static const char *direction[] = { "from-server", "from-client", "both" }; + +void +audit_kex_body(struct ssh *ssh, int ctos, char *enc, char *mac, char *compress, @@ -661,7 +662,7 @@ diff -up openssh-8.6p1/audit-linux.c.audit openssh-8.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(ssh), ++ ssh_remote_ipaddr(ssh), /*FIXME 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 */ @@ -733,7 +734,7 @@ diff -up openssh-8.6p1/auth2.c.audit openssh-8.6p1/auth2.c /* Invalid user, fake password information */ authctxt->pw = fakepw(); -#ifdef SSH_AUDIT_EVENTS -- PRIVSEP(audit_event(ssh, SSH_INVALID_USER)); +- mm_audit_event(ssh, SSH_INVALID_USER); -#endif } #ifdef USE_PAM @@ -743,11 +744,11 @@ diff -up openssh-8.6p1/auth2-hostbased.c.audit openssh-8.6p1/auth2-hostbased.c +++ openssh-8.6p1/auth2-hostbased.c 2021-04-19 16:47:35.754062114 +0200 @@ -158,7 +158,7 @@ userauth_hostbased(struct ssh *ssh) authenticated = 0; - if (PRIVSEP(hostbased_key_allowed(ssh, authctxt->pw, cuser, - chost, key)) && -- PRIVSEP(sshkey_verify(key, sig, slen, -+ PRIVSEP(hostbased_key_verify(ssh, key, sig, slen, - sshbuf_ptr(b), sshbuf_len(b), pkalg, ssh->compat, NULL)) == 0) + if (mm_hostbased_key_allowed(ssh, authctxt->pw, cuser, + chost, key) && +- mm_sshkey_verify(key, sig, slen, ++ mm_hostbased_key_verify(ssh, key, sig, slen, + sshbuf_ptr(b), sshbuf_len(b), pkalg, ssh->compat, NULL) == 0) authenticated = 1; @@ -175,6 +175,20 @@ done: @@ -777,12 +778,12 @@ diff -up openssh-8.6p1/auth2-pubkey.c.audit openssh-8.6p1/auth2-pubkey.c @@ -213,7 +213,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(ssh, key, sig, slen, + if (mm_user_key_allowed(ssh, pw, key, 1, &authopts) && +- mm_sshkey_verify(key, sig, slen, ++ mm_user_key_verify(ssh, key, sig, slen, sshbuf_ptr(b), sshbuf_len(b), (ssh->compat & SSH_BUG_SIGTYPE) == 0 ? pkalg : NULL, - ssh->compat, &sig_details)) == 0) { + ssh->compat, &sig_details) == 0) { @@ -305,6 +305,20 @@ done: return authenticated; } @@ -828,7 +829,7 @@ diff -up openssh-8.6p1/auth.h.audit openssh-8.6p1/auth.h + const u_char *, size_t, const char *, u_int, struct sshkey_sig_details **); /* Key / cert options linkage to auth layer */ - const struct sshauthopt *auth_options(struct ssh *); + int auth_activate_options(struct ssh *, struct sshauthopt *); @@ -239,6 +241,8 @@ struct passwd * getpwnamallow(struct ssh char *, const char *, const char *, const char *, struct sshauthopt **); int auth_check_authkeys_file(struct passwd *, FILE *, char *, @@ -915,8 +916,8 @@ diff -up openssh-8.6p1/kex.c.audit openssh-8.6p1/kex.c #include "xmalloc.h" +#include "audit.h" - #ifdef GSSAPI - #include "ssh-gss.h" + /* prototype */ + static int kex_choose_conf(struct ssh *, uint32_t seq); @@ -816,12 +817,16 @@ kex_start_rekex(struct ssh *ssh) } @@ -1108,7 +1109,7 @@ diff -up openssh-8.6p1/monitor.c.audit openssh-8.6p1/monitor.c extern struct sshbuf *loginmsg; extern struct sshauthopt *auth_opts; /* XXX move to permanent ssh->authctxt? */ -+extern void destroy_sensitive_data(struct ssh *, int); ++extern void destroy_sensitive_data(struct ssh *); + /* State exported from the child */ static struct sshbuf *child_state; @@ -1241,7 +1242,7 @@ diff -up openssh-8.6p1/monitor.c.audit openssh-8.6p1/monitor.c sshpam_cleanup(); #endif -+ destroy_sensitive_data(ssh, 0); ++ destroy_sensitive_data(ssh); + while (waitpid(pmonitor->m_pid, &status, 0) == -1) if (errno != EINTR) @@ -1536,9 +1537,9 @@ diff -up openssh-8.6p1/monitor_wrap.c.audit openssh-8.6p1/monitor_wrap.c } #endif /* SSH_AUDIT_EVENTS */ @@ -1095,3 +1137,83 @@ mm_ssh_gssapi_update_creds(ssh_gssapi_cc + return &ci; } - #endif /* GSSAPI */ +#ifdef SSH_AUDIT_EVENTS +void +mm_audit_unsupported_body(struct ssh *ssh, int what) @@ -1632,7 +1633,7 @@ diff -up openssh-8.6p1/monitor_wrap.h.audit openssh-8.6p1/monitor_wrap.h +int mm_user_key_verify(struct ssh*, const struct sshkey *, const u_char *, size_t, const u_char *, size_t, const char *, u_int, struct sshkey_sig_details **); - #ifdef GSSAPI + void mm_decode_activate_server_options(struct ssh *ssh, struct sshbuf *m); @@ -86,7 +88,12 @@ void mm_sshpam_free_ctx(void *); #ifdef SSH_AUDIT_EVENTS #include "audit.h" @@ -1817,7 +1818,7 @@ diff -up openssh-8.6p1/session.c.audit openssh-8.6p1/session.c extern u_int utmp_len; extern int startup_pipe; -extern void destroy_sensitive_data(void); -+extern void destroy_sensitive_data(struct ssh *, int); ++extern void destroy_sensitive_data(struct ssh *); extern struct sshbuf *loginmsg; extern struct sshauthopt *auth_opts; extern char *tun_fwd_ifnames; /* serverloop.c */ @@ -1843,18 +1844,18 @@ diff -up openssh-8.6p1/session.c.audit openssh-8.6p1/session.c + if (s->command != NULL || s->command_handle != -1) + fatal("do_exec: command already set"); if (command != NULL) -- PRIVSEP(audit_run_command(command)); +- mm_audit_run_command(command); + s->command = xstrdup(command); else if (s->ttyfd == -1) { char *shell = s->pw->pw_shell; if (shell[0] == '\0') /* empty shell means /bin/sh */ shell =_PATH_BSHELL; -- PRIVSEP(audit_run_command(shell)); +- mm_audit_run_command(shell); + s->command = xstrdup(shell); } + if (s->command != NULL && s->ptyfd == -1) -+ s->command_handle = PRIVSEP(audit_run_command(ssh, s->command)); ++ s->command_handle = mm_audit_run_command(ssh, s->command); #endif if (s->ttyfd != -1) ret = do_exec_pty(ssh, s, command); @@ -1863,7 +1864,7 @@ diff -up openssh-8.6p1/session.c.audit openssh-8.6p1/session.c /* remove hostkey from the child's memory */ - destroy_sensitive_data(); -+ destroy_sensitive_data(ssh, 1); ++ destroy_sensitive_data(ssh); 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. */ @@ -1923,7 +1924,7 @@ diff -up openssh-8.6p1/session.c.audit openssh-8.6p1/session.c +{ + if (s->command != NULL) { + if (s->command_handle != -1) -+ PRIVSEP(audit_end_command(ssh, s->command_handle, s->command)); ++ mm_audit_end_command(ssh, s->command_handle, s->command); + free(s->command); + s->command = NULL; + s->command_handle = -1; @@ -1981,7 +1982,7 @@ diff -up openssh-8.6p1/session.c.audit openssh-8.6p1/session.c @@ -2734,7 +2804,7 @@ do_cleanup(struct ssh *ssh, Authctxt *au * or if running in monitor. */ - if (!use_privsep || mm_is_monitor()) + if (mm_is_monitor()) - session_destroy_all(ssh, session_pty_cleanup2); + session_destroy_all(ssh, do_cleanup_one_session); } @@ -2020,25 +2021,6 @@ diff -up openssh-8.6p1/session.h.audit openssh-8.6p1/session.h diff -up openssh-8.6p1/sshd.c.audit openssh-8.6p1/sshd.c --- openssh-8.6p1/sshd.c.audit 2021-04-19 16:47:35.727061907 +0200 +++ openssh-8.6p1/sshd.c 2021-04-19 16:47:35.759062152 +0200 -@@ -122,6 +122,7 @@ - #include "ssh-gss.h" - #endif - #include "monitor_wrap.h" -+#include "audit.h" - #include "ssh-sandbox.h" - #include "auth-options.h" - #include "version.h" -@@ -260,8 +261,8 @@ struct sshbuf *loginmsg; - struct passwd *privsep_pw = NULL; - - /* Prototypes for various functions defined later in this file. */ --void destroy_sensitive_data(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 *); - - static char *listener_proctitle; @@ -279,6 +280,15 @@ close_listen_socks(void) num_listen_socks = 0; } @@ -2052,11 +2034,41 @@ diff -up openssh-8.6p1/sshd.c.audit openssh-8.6p1/sshd.c + return num_listen_socks > 0; +} + + /* Allocate and initialise the children array */ static void - close_startup_pipes(void) - { -@@ -377,18 +387,45 @@ grace_alarm_handler(int sig) - ssh_remote_port(the_active_state)); + child_alloc(void) +@@ -1204,6 +1259,7 @@ server_accept_loop(int *sock_in, int *so + if (received_sigterm) { + logit("Received signal %d; terminating.", + (int) received_sigterm); ++ /* destroy_sensitive_data(ssh, 0); FIXME */ + close_listen_socks(); + if (options.pid_file != NULL) + unlink(options.pid_file); +diff -up openssh-8.6p1/sshd-session.c.audit openssh-8.6p1/sshd-session.c +--- openssh-8.6p1/sshd-session.c.audit 2021-04-19 16:47:35.727061907 +0200 ++++ openssh-8.6p1/sshd-session.c 2021-04-19 16:47:35.759062152 +0200 +@@ -122,6 +122,7 @@ + #include "ssh-gss.h" + #endif + #include "monitor_wrap.h" ++#include "audit.h" + #include "ssh-sandbox.h" + #include "auth-options.h" + #include "version.h" +@@ -260,8 +261,8 @@ struct sshbuf *loginmsg; + struct sshbuf *loginmsg; + + /* Prototypes for various functions defined later in this file. */ +-void destroy_sensitive_data(void); +-void demote_sensitive_data(void); ++void destroy_sensitive_data(struct ssh *); ++void demote_sensitive_data(struct ssh *); + static void do_ssh2_kex(struct ssh *); + + /* +@@ -377,18 +387,40 @@ grace_alarm_handler(int sig) + _exit(EXIT_LOGIN_GRACE); } -/* Destroy the host and server keys. They will no longer be needed. */ @@ -2066,7 +2078,7 @@ diff -up openssh-8.6p1/sshd.c.audit openssh-8.6p1/sshd.c + */ void -destroy_sensitive_data(void) -+destroy_sensitive_data(struct ssh *ssh, int privsep) ++destroy_sensitive_data(struct ssh *ssh) { u_int i; +#ifdef SSH_AUDIT_EVENTS @@ -2088,12 +2100,7 @@ diff -up openssh-8.6p1/sshd.c.audit openssh-8.6p1/sshd.c sensitive_data.host_keys[i] = NULL; + if (fp != NULL) { +#ifdef SSH_AUDIT_EVENTS -+ if (privsep) -+ PRIVSEP(audit_destroy_sensitive_data(ssh, fp, -+ pid, uid)); -+ else -+ audit_destroy_sensitive_data(ssh, fp, -+ pid, uid); ++ audit_destroy_sensitive_data(ssh, fp, pid, uid); +#endif + free(fp); + } @@ -2163,9 +2170,9 @@ diff -up openssh-8.6p1/sshd.c.audit openssh-8.6p1/sshd.c #ifdef WITH_SELINUX sshd_selinux_change_privsep_preauth_context(); @@ -492,7 +547,7 @@ privsep_preauth(struct ssh *ssh) + pmonitor->m_pkex = &ssh->kex; - if (use_privsep == PRIVSEP_ON) - box = ssh_sandbox_init(pmonitor); + box = ssh_sandbox_init(pmonitor); - pid = fork(); + pmonitor->m_pid = pid = fork(); if (pid == -1) { @@ -2189,48 +2196,21 @@ diff -up openssh-8.6p1/sshd.c.audit openssh-8.6p1/sshd.c reseed_prngs(); -@@ -1143,7 +1198,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) - { - struct pollfd *pfd = NULL; - int i, j, ret, npfd; -@@ -1204,6 +1259,7 @@ server_accept_loop(int *sock_in, int *so - if (received_sigterm) { - logit("Received signal %d; terminating.", - (int) received_sigterm); -+ destroy_sensitive_data(ssh, 0); - close_listen_socks(); - if (options.pid_file != NULL) - unlink(options.pid_file); -@@ -2098,7 +2154,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); - } - @@ -2333,6 +2389,9 @@ main(int ac, char **av) do_authenticated(ssh, authctxt); /* The connection has been terminated. */ + packet_destroy_all(ssh, 1, 1); -+ destroy_sensitive_data(ssh, 1); ++ destroy_sensitive_data(ssh); + ssh_packet_get_bytes(ssh, &ibytes, &obytes); verbose("Transferred: sent %llu, received %llu bytes", (unsigned long long)obytes, (unsigned long long)ibytes); -@@ -2513,6 +2572,15 @@ do_ssh2_kex(struct ssh *ssh) +@@ -2513,6 +2572,14 @@ do_ssh2_kex(struct ssh *ssh) void cleanup_exit(int i) { + static int in_cleanup = 0; -+ int is_privsep_child; + + /* cleanup_exit can be called at the very least from the privsep + wrappers used for auditing. Make sure we don't recurse @@ -2238,24 +2218,17 @@ diff -up openssh-8.6p1/sshd.c.audit openssh-8.6p1/sshd.c + if (in_cleanup) + _exit(i); + in_cleanup = 1; + extern int auth_attempted; /* monitor.c */ + if (the_active_state != NULL && the_authctxt != NULL) { - do_cleanup(the_active_state, the_authctxt); - if (use_privsep && privsep_is_preauth && -@@ -2525,9 +2593,16 @@ cleanup_exit(int i) - } - } - } -+ is_privsep_child = use_privsep && pmonitor != NULL && pmonitor->m_pid == 0; -+ 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); +@@ -2525,7 +2593,9 @@ cleanup_exit(int i) + _exit(EXIT_AUTH_ATTEMPTED); #ifdef SSH_AUDIT_EVENTS /* done after do_cleanup so it can cancel the PAM auth 'thread' */ -- if (the_active_state != NULL && (!use_privsep || mm_is_monitor())) +- if (the_active_state != NULL && mm_is_monitor()) + if (the_active_state != NULL && + (the_authctxt == NULL || !the_authctxt->authenticated) && -+ (!use_privsep || mm_is_monitor())) ++ mm_is_monitor()) audit_event(the_active_state, SSH_CONNECTION_ABANDON); #endif _exit(i); diff --git a/openssh-7.6p1-cleanup-selinux.patch b/openssh-7.6p1-cleanup-selinux.patch index f7cd50f..cfe11ad 100644 --- a/openssh-7.6p1-cleanup-selinux.patch +++ b/openssh-7.6p1-cleanup-selinux.patch @@ -1,23 +1,22 @@ diff -up openssh/auth2-pubkey.c.refactor openssh/auth2-pubkey.c --- 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 @@ +@@ -72,6 +72,8 @@ /* import */ extern ServerOptions options; +extern int inetd_flag; -+extern int rexeced_flag; +extern Authctxt *the_authctxt; + extern struct authmethod_cfg methodcfg_pubkey; static char * - format_key(const struct sshkey *key) @@ -511,7 +514,8 @@ match_principals_command(struct ssh *ssh if ((pid = subprocess("AuthorizedPrincipalsCommand", command, ac, av, &f, SSH_SUBPROCESS_STDOUT_CAPTURE|SSH_SUBPROCESS_STDERR_DISCARD, - runas_pw, temporarily_use_uid, restore_uid)) == 0) + runas_pw, temporarily_use_uid, restore_uid, -+ (inetd_flag && !rexeced_flag), the_authctxt)) == 0) ++ inetd_flag, the_authctxt)) == 0) goto out; uid_swapped = 1; @@ -27,7 +26,7 @@ diff -up openssh/auth2-pubkey.c.refactor openssh/auth2-pubkey.c SSH_SUBPROCESS_STDOUT_CAPTURE|SSH_SUBPROCESS_STDERR_DISCARD, - runas_pw, temporarily_use_uid, restore_uid)) == 0) + runas_pw, temporarily_use_uid, restore_uid, -+ (inetd_flag && !rexeced_flag), the_authctxt)) == 0) ++ inetd_flag, the_authctxt)) == 0) goto out; uid_swapped = 1; @@ -82,14 +81,13 @@ diff -up openssh/openbsd-compat/port-linux.h.refactor openssh/openbsd-compat/por 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 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 @@ +@@ -49,10 +49,6 @@ #include #endif -extern ServerOptions options; -extern Authctxt *the_authctxt; -extern int inetd_flag; --extern int rexeced_flag; - /* Wrapper around is_selinux_enabled() to log its return value once only */ int @@ -128,7 +126,7 @@ diff -up openssh/openbsd-compat/port-linux-sshd.c.refactor openssh/openbsd-compa if (r == 0) { /* If launched from xinetd, we must use current level */ -- if (inetd_flag && !rexeced_flag) { +- if (inetd_flag) { + if (inetd) { security_context_t sshdsc=NULL; @@ -152,7 +150,7 @@ diff -up openssh/openbsd-compat/port-linux-sshd.c.refactor openssh/openbsd-compa rv = set_it("SELINUX_ROLE_REQUESTED", role ? role : ""); -- if (inetd_flag && !rexeced_flag) { +- if (inetd_flag) { + if (inetd) { use_current = "1"; } else { @@ -216,55 +214,45 @@ diff -up openssh/openbsd-compat/port-linux-sshd.c.refactor openssh/openbsd-compa diff -up openssh/platform.c.refactor openssh/platform.c --- 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 @@ +@@ -32,6 +32,8 @@ + #include "openbsd-compat/openbsd-compat.h" - extern int use_privsep; extern ServerOptions options; +extern int inetd_flag; -+extern int rexeced_flag; +extern Authctxt *the_authctxt; - void - platform_pre_listen(void) + /* return 1 if we are running with privilege to swap UIDs, 0 otherwise */ + int @@ -183,7 +186,9 @@ platform_setusercontext_post_groups(stru } #endif /* HAVE_SETPCRED */ #ifdef WITH_SELINUX - sshd_selinux_setup_exec_context(pw->pw_name); + sshd_selinux_setup_exec_context(pw->pw_name, -+ (inetd_flag && !rexeced_flag), do_pam_putenv, the_authctxt, ++ inetd_flag, do_pam_putenv, the_authctxt, + options.use_pam); #endif } -diff -up openssh/sshd.c.refactor openssh/sshd.c ---- openssh/sshd.c.refactor 2019-04-04 13:19:12.275822068 +0200 -+++ openssh/sshd.c 2019-04-04 13:19:51.270195262 +0200 +diff -up openssh/sshd-session.c.refactor openssh/sshd-session.c +--- openssh/sshd-session.c.refactor 2019-04-04 13:19:12.275822068 +0200 ++++ openssh/sshd-session.c 2019-04-04 13:19:51.270195262 +0200 @@ -158,7 +158,7 @@ int debug_flag = 0; - static int test_flag = 0; + int debug_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; + /* debug goes to stderr unless inetd_flag is set */ + static int log_stderr = 0; @@ -2192,7 +2192,9 @@ main(int ac, char **av) } #endif #ifdef WITH_SELINUX - sshd_selinux_setup_exec_context(authctxt->pw->pw_name); + sshd_selinux_setup_exec_context(authctxt->pw->pw_name, -+ (inetd_flag && !rexeced_flag), do_pam_putenv, the_authctxt, ++ inetd_flag, do_pam_putenv, the_authctxt, + options.use_pam); #endif #ifdef USE_PAM diff --git a/openssh-7.7p1-fips.patch b/openssh-7.7p1-fips.patch index 5351571..e44fef2 100644 --- a/openssh-7.7p1-fips.patch +++ b/openssh-7.7p1-fips.patch @@ -72,17 +72,17 @@ diff -up openssh-8.6p1/dh.h.fips openssh-8.6p1/dh.h u_int dh_estimate(int); void dh_set_moduli_file(const char *); -diff -up openssh-8.6p1/kex.c.fips openssh-8.6p1/kex.c ---- openssh-8.6p1/kex.c.fips 2021-05-06 12:08:36.489926807 +0200 -+++ openssh-8.6p1/kex.c 2021-05-06 12:08:36.498926877 +0200 +diff -up openssh-8.6p1/kex-names.c.fips openssh-8.6p1/kex-names.c +--- openssh-8.6p1/kex-names.c.fips 2021-05-06 12:08:36.489926807 +0200 ++++ openssh-8.6p1/kex-names.c 2021-05-06 12:08:36.498926877 +0200 @@ -39,6 +39,7 @@ #ifdef WITH_OPENSSL #include +#include - #include - # ifdef HAVE_EVP_KDF_CTX_NEW_ID - # include + #include + #endif + @@ -203,7 +203,10 @@ kex_names_valid(const char *names) for ((p = strsep(&cp, ",")); p && *p != '\0'; (p = strsep(&cp, ","))) { @@ -361,8 +361,8 @@ diff -up openssh-8.6p1/sshd.c.fips openssh-8.6p1/sshd.c #include #include @@ -77,6 +78,7 @@ - #include - #include + #ifdef WITH_OPENSSL + #include #include +#include #include "openbsd-compat/openssl-compat.h" @@ -393,7 +393,18 @@ diff -up openssh-8.6p1/sshd.c.fips openssh-8.6p1/sshd.c /* * Chdir to the root directory so that the current disk can be * unmounted if desired. -@@ -2494,10 +2501,14 @@ do_ssh2_kex(struct ssh *ssh) +diff -up openssh-8.6p1/sshd-session.c.fips openssh-8.6p1/sshd-session.c +--- a/sshd-session.c.fips 2021-05-06 12:08:36.493926838 +0200 ++++ b/sshd-session.c 2021-05-06 12:13:56.501492639 +0200 +@@ -78,6 +79,7 @@ + #include + #include + #include ++#include + #include "openbsd-compat/openssl-compat.h" + #endif + +@@ -2506,10 +2513,14 @@ do_ssh2_kex(struct ssh *ssh) if (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]) == 0) orig = NULL; @@ -475,11 +486,11 @@ diff -up openssh-8.6p1/sshkey.c.fips openssh-8.6p1/sshkey.c *lenp = 0; if (datalen > SSH_KEY_MAX_SIGN_DATA_SIZE) return SSH_ERR_INVALID_ARGUMENT; -+ if (FIPS_mode() && ((key->type == KEY_ED25519_SK) || (key->type == KEY_ED25519_SK_CERT))) { -+ logit_f("Ed25519 keys are not allowed in FIPS mode"); -+ return SSH_ERR_INVALID_ARGUMENT; -+ } -+ /* Fallthrough */ ++ if (FIPS_mode() && ((key->type == KEY_ED25519_SK) || (key->type == KEY_ED25519_SK_CERT))) { ++ logit_f("Ed25519 keys are not allowed in FIPS mode"); ++ return SSH_ERR_INVALID_ARGUMENT; ++ } ++ /* Fallthrough */ if ((impl = sshkey_impl_from_key(key)) == NULL) return SSH_ERR_KEY_TYPE_UNKNOWN; if ((r = sshkey_unshield_private(key)) != 0) @@ -487,10 +498,10 @@ diff -up openssh-8.6p1/sshkey.c.fips openssh-8.6p1/sshkey.c *detailsp = NULL; if (siglen == 0 || dlen > SSH_KEY_MAX_SIGN_DATA_SIZE) return SSH_ERR_INVALID_ARGUMENT; -+ if (FIPS_mode() && ((key->type == KEY_ED25519_SK) || (key->type == KEY_ED25519_SK_CERT))) { -+ logit_f("Ed25519 keys are not allowed in FIPS mode"); -+ return SSH_ERR_INVALID_ARGUMENT; -+ } ++ if (FIPS_mode() && ((key->type == KEY_ED25519_SK) || (key->type == KEY_ED25519_SK_CERT))) { ++ logit_f("Ed25519 keys are not allowed in FIPS mode"); ++ return SSH_ERR_INVALID_ARGUMENT; ++ } if ((impl = sshkey_impl_from_key(key)) == NULL) return SSH_ERR_KEY_TYPE_UNKNOWN; return impl->funcs->verify(key, sig, siglen, data, dlen, diff --git a/openssh-7.7p1-gssapi-new-unique.patch b/openssh-7.7p1-gssapi-new-unique.patch index 544932b..0b8ab02 100644 --- a/openssh-7.7p1-gssapi-new-unique.patch +++ b/openssh-7.7p1-gssapi-new-unique.patch @@ -368,7 +368,7 @@ diff -up openssh-8.6p1/gss-serv.c.ccache_name openssh-8.6p1/gss-serv.c + if (gssapi_client.store.envval == NULL) return; - ok = PRIVSEP(ssh_gssapi_update_creds(&gssapi_client.store)); + ok = mm_ssh_gssapi_update_creds(&gssapi_client.store); diff -up openssh-8.6p1/gss-serv-krb5.c.ccache_name openssh-8.6p1/gss-serv-krb5.c --- openssh-8.6p1/gss-serv-krb5.c.ccache_name 2021-04-19 14:05:10.852744562 +0200 +++ openssh-8.6p1/gss-serv-krb5.c 2021-04-19 14:05:10.854744577 +0200 @@ -579,9 +579,9 @@ diff -up openssh-8.6p1/session.c.ccache_name openssh-8.6p1/session.c child_set_env(&env, &envsize, "KRB5CCNAME", s->authctxt->krb5_ccname); #endif -diff -up openssh-8.6p1/sshd.c.ccache_name openssh-8.6p1/sshd.c ---- openssh-8.6p1/sshd.c.ccache_name 2021-04-19 14:05:10.849744540 +0200 -+++ openssh-8.6p1/sshd.c 2021-04-19 14:05:10.855744584 +0200 +diff -up openssh-8.6p1/sshd-session.c.ccache_name openssh-8.6p1/sshd-session.c +--- openssh-8.6p1/sshd-session.c.ccache_name 2021-04-19 14:05:10.849744540 +0200 ++++ openssh-8.6p1/sshd-session.c 2021-04-19 14:05:10.855744584 +0200 @@ -2284,7 +2284,7 @@ main(int ac, char **av) #ifdef GSSAPI if (options.gss_authentication) { @@ -607,8 +607,8 @@ diff -up openssh-8.6p1/sshd_config.5.ccache_name openssh-8.6p1/sshd_config.5 +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. + Specifies the permitted KEX (Key Exchange) algorithms that the server will + offer to clients. diff -up openssh-8.6p1/ssh-gss.h.ccache_name openssh-8.6p1/ssh-gss.h --- openssh-8.6p1/ssh-gss.h.ccache_name 2021-04-19 14:05:10.852744562 +0200 +++ openssh-8.6p1/ssh-gss.h 2021-04-19 14:05:10.855744584 +0200 diff --git a/openssh-7.8p1-UsePAM-warning.patch b/openssh-7.8p1-UsePAM-warning.patch index a94419e..f8d7042 100644 --- a/openssh-7.8p1-UsePAM-warning.patch +++ b/openssh-7.8p1-UsePAM-warning.patch @@ -1,13 +1,13 @@ diff -up openssh-8.6p1/sshd.c.log-usepam-no openssh-8.6p1/sshd.c ---- openssh-8.6p1/sshd.c.log-usepam-no 2021-04-19 14:00:45.099735129 +0200 -+++ openssh-8.6p1/sshd.c 2021-04-19 14:03:21.140920974 +0200 +--- openssh-8.6p1/sshd-session.c.log-usepam-no 2021-04-19 14:00:45.099735129 +0200 ++++ openssh-8.6p1/sshd-session.c 2021-04-19 14:03:21.140920974 +0200 @@ -1749,6 +1749,10 @@ main(int ac, char **av) - parse_server_config(&options, rexeced_flag ? "rexec" : config_file_name, - cfg, &includes, NULL, rexeced_flag); + "enabled authentication methods"); + } -+ /* 'UsePAM no' is not supported in Fedora */ ++ /* 'UsePAM no' is not supported in our builds */ + 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 this build and may cause several problems."); + #ifdef WITH_OPENSSL if (options.moduli_file != NULL) @@ -19,7 +19,7 @@ diff -up openssh-8.6p1/sshd_config.log-usepam-no openssh-8.6p1/sshd_config # If you just want the PAM account and session checks to run without # PAM authentication, then enable this but set PasswordAuthentication # and KbdInteractiveAuthentication to 'no'. -+# WARNING: 'UsePAM no' is not supported in Fedora and may cause several ++# WARNING: 'UsePAM no' is not supported in this build and may cause several +# problems. #UsePAM no diff --git a/openssh-7.8p1-role-mls.patch b/openssh-7.8p1-role-mls.patch index 347766d..7c6d0ca 100644 --- a/openssh-7.8p1-role-mls.patch +++ b/openssh-7.8p1-role-mls.patch @@ -23,20 +23,17 @@ diff -up openssh/auth2.c.role-mls openssh/auth2.c if ((style = strchr(user, ':')) != NULL) *style++ = 0; -@@ -314,8 +314,15 @@ input_userauth_request(int type, u_int32 - use_privsep ? " [net]" : ""); +@@ -314,7 +314,13 @@ input_userauth_request(int type, u_int32 + setproctitle("%s [net]", authctxt->valid ? user : "unknown"); authctxt->service = xstrdup(service); authctxt->style = style ? xstrdup(style) : NULL; -- if (use_privsep) +#ifdef WITH_SELINUX + authctxt->role = role ? xstrdup(role) : NULL; +#endif -+ if (use_privsep) { - mm_inform_authserv(service, style); + mm_inform_authserv(service, style); +#ifdef WITH_SELINUX + mm_inform_authrole(role); +#endif -+ } userauth_banner(ssh); if ((r = kex_server_update_ext_info(ssh)) != 0) fatal_fr(r, "kex_server_update_ext_info failed"); @@ -50,7 +47,7 @@ diff -up openssh/auth2-gss.c.role-mls openssh/auth2-gss.c + char *micuser; struct sshbuf *b; gss_buffer_desc mic, gssbuf; - const char *displayname; + u_char *p; @@ -298,7 +299,13 @@ input_gssapi_mic(int type, u_int32_t ple fatal_f("sshbuf_new failed"); mic.value = p; @@ -74,7 +71,7 @@ diff -up openssh/auth2-gss.c.role-mls openssh/auth2-gss.c + free(micuser); free(mic.value); - if ((!use_privsep || mm_is_monitor()) && + authctxt->postponed = 0; diff -up openssh/auth2-hostbased.c.role-mls openssh/auth2-hostbased.c --- openssh/auth2-hostbased.c.role-mls 2018-08-20 07:57:29.000000000 +0200 +++ openssh/auth2-hostbased.c 2018-08-22 11:14:56.816430924 +0200 @@ -147,7 +144,7 @@ diff -up openssh/auth-pam.h.role-mls openssh/auth-pam.h +++ openssh/auth-pam.h 2018-08-22 11:14:56.817430932 +0200 @@ -33,7 +33,7 @@ u_int do_pam_account(void); void do_pam_session(struct ssh *); - void do_pam_setcred(int ); + void do_pam_setcred(void); void do_pam_chauthtok(void); -int do_pam_putenv(char *, char *); +int do_pam_putenv(char *, const char *); @@ -418,7 +415,7 @@ diff -up openssh/openbsd-compat/port-linux.h.role-mls openssh/openbsd-compat/por diff -up openssh/openbsd-compat/port-linux-sshd.c.role-mls openssh/openbsd-compat/port-linux-sshd.c --- openssh/openbsd-compat/port-linux-sshd.c.role-mls 2018-08-22 11:14:56.819430949 +0200 +++ openssh/openbsd-compat/port-linux-sshd.c 2018-08-22 11:14:56.819430949 +0200 -@@ -0,0 +1,421 @@ +@@ -0,0 +1,420 @@ +/* + * Copyright (c) 2005 Daniel Walsh + * Copyright (c) 2014 Petr Lautrbach @@ -472,7 +469,6 @@ diff -up openssh/openbsd-compat/port-linux-sshd.c.role-mls openssh/openbsd-compa +extern ServerOptions options; +extern Authctxt *the_authctxt; +extern int inetd_flag; -+extern int rexeced_flag; + +/* Send audit message */ +static int @@ -678,7 +674,7 @@ diff -up openssh/openbsd-compat/port-linux-sshd.c.role-mls openssh/openbsd-compa + + if (r == 0) { + /* If launched from xinetd, we must use current level */ -+ if (inetd_flag && !rexeced_flag) { ++ if (inetd_flag) { + security_context_t sshdsc=NULL; + + if (getcon_raw(&sshdsc) < 0) @@ -752,7 +748,7 @@ diff -up openssh/openbsd-compat/port-linux-sshd.c.role-mls openssh/openbsd-compa + + rv = do_pam_putenv("SELINUX_ROLE_REQUESTED", role ? role : ""); + -+ if (inetd_flag && !rexeced_flag) { ++ if (inetd_flag) { + use_current = "1"; + } else { + use_current = ""; @@ -853,8 +849,8 @@ diff -up openssh/platform.c.role-mls openssh/platform.c } diff -up openssh/sshd.c.role-mls openssh/sshd.c ---- openssh/sshd.c.role-mls 2018-08-20 07:57:29.000000000 +0200 -+++ openssh/sshd.c 2018-08-22 11:14:56.820430957 +0200 +--- openssh/sshd-session.c.role-mls 2018-08-20 07:57:29.000000000 +0200 ++++ openssh/sshd-session.c 2018-08-22 11:14:56.820430957 +0200 @@ -2186,6 +2186,9 @@ main(int ac, char **av) restore_uid(); } @@ -864,4 +860,4 @@ diff -up openssh/sshd.c.role-mls openssh/sshd.c +#endif #ifdef USE_PAM if (options.use_pam) { - do_pam_setcred(1); + do_pam_setcred(); diff --git a/openssh-8.0p1-crypto-policies.patch b/openssh-8.0p1-crypto-policies.patch index 86c08db..fd1e59d 100644 --- a/openssh-8.0p1-crypto-policies.patch +++ b/openssh-8.0p1-crypto-policies.patch @@ -155,7 +155,7 @@ diff --color -ru -x regress -x autom4te.cache -x '*.o' -x '*.lo' -x Makefile -x .It Cm HostKeyAlias Specifies an alias that should be used instead of the real host name when looking up or saving the host key -@@ -1232,30 +1229,25 @@ +@@ -1330,6 +1330,11 @@ it may be zero or more of: and .Cm pam . .It Cm KexAlgorithms @@ -164,8 +164,11 @@ diff --color -ru -x regress -x autom4te.cache -x '*.o' -x '*.lo' -x Makefile -x +Information about defaults, how to modify the defaults and how to customize existing policies with sub-policies are present in manual page +.Xr update-crypto-policies 8 . +.Pp - Specifies the available KEX (Key Exchange) algorithms. - Multiple algorithms must be comma-separated. + Specifies the permitted KEX (Key Exchange) algorithms that will be used and + their preference order. + The selected algorithm will the the first algorithm in this list that +@@ -1338,28 +1343,17 @@ Multiple algorithms must be comma-separa + .Pp If the specified list begins with a .Sq + -character, then the specified algorithms will be appended to the default set @@ -181,6 +184,7 @@ diff --color -ru -x regress -x autom4te.cache -x '*.o' -x '*.lo' -x Makefile -x .Sq ^ character, then the specified algorithms will be placed at the head of the -default set. +-.Pp -The default is: -.Bd -literal -offset indent -sntrup761x25519-sha512@openssh.com, @@ -191,10 +195,11 @@ diff --color -ru -x regress -x autom4te.cache -x '*.o' -x '*.lo' -x Makefile -x -diffie-hellman-group18-sha512, -diffie-hellman-group14-sha256 -.Ed -+built-in openssh default set. .Pp - The list of available key exchange algorithms may also be obtained using ++built-in openssh default set. + The list of supported key exchange algorithms may also be obtained using .Qq ssh -Q kex . + .It Cm KnownHostsCommand @@ -1365,37 +1357,33 @@ file. This option is intended for debugging and no overrides are enabled by default. @@ -479,7 +484,7 @@ diff --color -ru -x regress -x autom4te.cache -x '*.o' -x '*.lo' -x Makefile -x The list of available signature algorithms may also be obtained using .Qq ssh -Q HostKeyAlgorithms . .It Cm IgnoreRhosts -@@ -1044,20 +1017,25 @@ +@@ -1025,6 +1025,11 @@ Specifies whether to look at .k5login fi The default is .Cm yes . .It Cm KexAlgorithms @@ -488,9 +493,12 @@ diff --color -ru -x regress -x autom4te.cache -x '*.o' -x '*.lo' -x Makefile -x +Information about defaults, how to modify the defaults and how to customize existing policies with sub-policies are present in 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 list begins with a + Specifies the permitted KEX (Key Exchange) algorithms that the server will + offer to clients. + The ordering of this list is not important, as the client specifies the +@@ -1033,16 +1038,16 @@ Multiple algorithms must be comma-separa + .Pp + If the specified list begins with a .Sq + -character, then the specified algorithms will be appended to the default set -instead of replacing them. @@ -506,10 +514,10 @@ diff --color -ru -x regress -x autom4te.cache -x '*.o' -x '*.lo' -x Makefile -x character, then the specified algorithms will be placed at the head of the -default set. +built-in openssh default set. + .Pp The supported algorithms are: .Pp - .Bl -item -compact -offset indent -@@ -1089,16 +1067,6 @@ +@@ -1075,16 +1080,6 @@ ecdh-sha2-nistp521 sntrup761x25519-sha512@openssh.com .El .Pp @@ -523,7 +531,7 @@ diff --color -ru -x regress -x autom4te.cache -x '*.o' -x '*.lo' -x Makefile -x -diffie-hellman-group14-sha256 -.Ed -.Pp - The list of available key exchange algorithms may also be obtained using + The list of supported key exchange algorithms may also be obtained using .Qq ssh -Q KexAlgorithms . .It Cm ListenAddress @@ -1184,21 +1152,26 @@ diff --git a/openssh-8.0p1-gssapi-keyex.patch b/openssh-8.0p1-gssapi-keyex.patch index 3e5ccec..c609a00 100644 --- a/openssh-8.0p1-gssapi-keyex.patch +++ b/openssh-8.0p1-gssapi-keyex.patch @@ -3,7 +3,7 @@ index e7549470..b68c1710 100644 --- a/Makefile.in +++ b/Makefile.in @@ -109,6 +109,7 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \ - kex.o kexdh.o kexgex.o kexecdh.o kexc25519.o \ + kex.o kex-names.o kexdh.o kexgex.o kexecdh.o kexc25519.o \ kexgexc.o kexgexs.o \ kexsntrup761x25519.o sntrup761.o kexgen.o \ + kexgssc.o \ @@ -11,14 +11,14 @@ index e7549470..b68c1710 100644 sshbuf-io.o @@ -125,7 +126,7 @@ SSHDOBJS=sshd.o auth-rhosts.o auth-passwd.o \ + auth2-chall.o groupaccess.o \ auth-bsdauth.o auth2-hostbased.o auth2-kbdint.o \ auth2-none.o auth2-passwd.o auth2-pubkey.o auth2-pubkeyfile.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 \ +- monitor.o monitor_wrap.o auth-krb5.o \ ++ monitor.o monitor_wrap.o auth-krb5.o kexgsss.o \ + auth2-gss.o gss-serv.o gss-serv-krb5.o \ loginrec.o auth-pam.o auth-shadow.o auth-sia.o \ - srclimit.o sftp-server.o sftp-common.o \ - sandbox-null.o sandbox-rlimit.o sandbox-systrace.o sandbox-darwin.o \ + sftp-server.o sftp-common.o \ @@ -523,7 +523,7 @@ regress-prep: ln -s `cd $(srcdir) && pwd`/regress/Makefile `pwd`/regress/Makefile @@ -41,117 +41,10 @@ diff -up a/auth.c.gsskex b/auth.c return 1; break; case PERMIT_FORCED_ONLY: -@@ -730,97 +731,6 @@ fakepw(void) - } - - /* -- * 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 based on conflation of hostnames and IP addresses. -- */ -- --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) == -1) { -- debug("getpeername failed: %.100s", strerror(errno)); -- return xstrdup(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 xstrdup(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 xstrdup(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 xstrdup(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 xstrdup(ntop); -- } -- return xstrdup(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. 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.34 2023/03/31 04:22: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 *); @@ -187,10 +80,10 @@ index 9351e042..d6446c0c 100644 + 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)); ++ if (!GSS_ERROR(mm_ssh_gssapi_checkmic(gss_kex_context, ++ &gssbuf, &mic))) ++ authenticated = mm_ssh_gssapi_userok(authctxt->user, ++ authctxt->pw, 1); + + sshbuf_free(b); + free(mic.value); @@ -201,40 +94,42 @@ index 9351e042..d6446c0c 100644 /* * 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) +@@ -260,7 +302,7 @@ input_gssapi_exchange_complete(int type, u_int32_t plen, struct ssh *ssh) if ((r = sshpkt_get_end(ssh)) != 0) fatal_fr(r, "parse packet"); -- authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user)); -+ authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user, -+ authctxt->pw, 1)); +- authenticated = mm_ssh_gssapi_userok(authctxt->user); ++ authenticated = mm_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) + authctxt->postponed = 0; + ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); +@@ -306,7 +349,7 @@ 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)); + if (!GSS_ERROR(mm_ssh_gssapi_checkmic(gssctxt, &gssbuf, &mic))) +- authenticated = mm_ssh_gssapi_userok(authctxt->user); ++ authenticated = mm_ssh_gssapi_userok(authctxt->user, authctxt->pw, 0); else logit("GSSAPI MIC check failed"); -@@ -326,6 +370,13 @@ input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh) +@@ -326,6 +370,17 @@ input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh) return 0; } -+Authmethod method_gsskeyex = { ++struct authmethod_cfg methodcfg_gsskeyex = { + "gssapi-keyex", + NULL, -+ userauth_gsskeyex, + &options.gss_authentication +}; ++ ++Authmethod method_gsskeyex = { ++ &methodcfg_gsskeyex, ++ userauth_gsskeyex, ++}; + Authmethod method_gssapi = { - "gssapi-with-mic", - NULL, + &methodcfg_gssapi, + userauth_gssapi, diff --git a/auth2.c b/auth2.c index 0e776224..1c217268 100644 --- a/auth2.c @@ -442,15 +337,6 @@ diff --git a/gss-genr.c b/gss-genr.c index d56257b4..763a63ff 100644 --- a/gss-genr.c +++ b/gss-genr.c -@@ -1,7 +1,7 @@ - /* $OpenBSD: gss-genr.c,v 1.28 2021/01/27 10:05:28 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,9 +41,33 @@ #include "sshbuf.h" #include "log.h" @@ -735,8 +621,8 @@ index d56257b4..763a63ff 100644 + ctx = &intctx; /* RFC 4462 says we MUST NOT do SPNEGO */ - if (oid->length == spnego_oid.length && -@@ -287,6 +519,10 @@ ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host) + if (oid->length == spnego_oid.length && +@@ -500,6 +500,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); @@ -745,13 +631,13 @@ index d56257b4..763a63ff 100644 + major = ssh_gssapi_client_identity(*ctx, client); + if (!GSS_ERROR(major)) { - major = ssh_gssapi_init_ctx(*ctx, 0, GSS_C_NO_BUFFER, &token, + major = ssh_gssapi_init_ctx(*ctx, 0, GSS_C_NO_BUFFER, &token, NULL); -@@ -296,10 +532,66 @@ ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host) +@@ -527,10 +527,66 @@ ssh_gssapi_check_mechanism(Gssctxt **ctx GSS_C_NO_BUFFER); } -- if (GSS_ERROR(major)) +- if (GSS_ERROR(major)) + if (GSS_ERROR(major) || intctx != NULL) ssh_gssapi_delete_ctx(ctx); @@ -1012,7 +898,7 @@ index ab3a15f0..6ce56e92 100644 + Gssctxt *ctx = NULL; + int res; + -+ res = !GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctx, oid))); ++ res = !GSS_ERROR(mm_ssh_gssapi_server_ctx(&ctx, oid)); + ssh_gssapi_delete_ctx(&ctx); + + return (res); @@ -1158,7 +1044,7 @@ index ab3a15f0..6ce56e92 100644 /* 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) +@@ -382,14 +471,85 @@ ssh_gssapi_userok(char *user) return (0); } @@ -1201,7 +1087,7 @@ index ab3a15f0..6ce56e92 100644 + gssapi_client.store.envvar == NULL) + return; + -+ ok = PRIVSEP(ssh_gssapi_update_creds(&gssapi_client.store)); ++ ok = mm_ssh_gssapi_update_creds(&gssapi_client.store); + + if (!ok) + return; @@ -1213,11 +1099,6 @@ index ab3a15f0..6ce56e92 100644 + * 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) @@ -1256,20 +1137,34 @@ index ab3a15f0..6ce56e92 100644 /* Privileged */ diff --git a/kex.c b/kex.c -index ce85f043..574c7609 100644 +index a5ae6ac0..fe714141 100644 --- a/kex.c +++ b/kex.c +@@ -698,6 +755,9 @@ kex_free(struct kex *kex) + sshbuf_free(kex->server_version); + sshbuf_free(kex->client_pub); + sshbuf_free(kex->session_id); ++#ifdef GSSAPI ++ free(kex->gss_host); ++#endif /* GSSAPI */ + sshbuf_free(kex->initial_sig); + sshkey_free(kex->initial_hostkey); + free(kex->failed_choice); +diff --git a/kex-names.c b/kex-names.c +index ce85f043..574c7609 100644 +--- a/kex-names.c ++++ b/kex-names.c @@ -57,6 +57,10 @@ - #include "digest.h" + #include "ssherr.h" #include "xmalloc.h" +#ifdef GSSAPI +#include "ssh-gss.h" +#endif + - /* prototype */ - static int kex_choose_conf(struct ssh *, uint32_t seq); - static int kex_input_newkeys(int, u_int32_t, struct ssh *); + struct kexalg { + char *name; + u_int type; @@ -115,15 +120,28 @@ static const struct kexalg kexalgs[] = { #endif /* HAVE_EVP_SHA256 || !WITH_OPENSSL */ { NULL, 0, -1, -1}, @@ -1332,10 +1227,11 @@ index ce85f043..574c7609 100644 return NULL; } -@@ -315,6 +349,29 @@ kex_assemble_names(char **listp, const char *def, const char *all) +@@ -315,3 +349,26 @@ kex_assemble_names(char **listp, const char *def, const char *all) + free(ret); return r; } - ++ +/* Validate GSS KEX method name list */ +int +kex_gss_names_valid(const char *names) @@ -1358,20 +1254,6 @@ index ce85f043..574c7609 100644 + free(s); + return 1; +} -+ - /* - * Fill out a proposal array with dynamically allocated values, which may - * be modified as required for compatibility reasons. -@@ -698,6 +755,9 @@ kex_free(struct kex *kex) - sshbuf_free(kex->server_version); - sshbuf_free(kex->client_pub); - sshbuf_free(kex->session_id); -+#ifdef GSSAPI -+ free(kex->gss_host); -+#endif /* GSSAPI */ - sshbuf_free(kex->initial_sig); - sshkey_free(kex->initial_hostkey); - free(kex->failed_choice); diff --git a/kex.h b/kex.h index a5ae6ac0..fe714141 100644 --- a/kex.h @@ -1406,16 +1288,16 @@ index a5ae6ac0..fe714141 100644 int (*verify_host_key)(struct sshkey *, struct ssh *); struct sshkey *(*load_host_public_key)(int, int, struct ssh *); @@ -174,8 +189,10 @@ struct kex { - + int kex_nid_from_name(const char *); 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_has_any_alg(const char *, const char *); +int kex_gss_names_valid(const char *); + int kex_assemble_names(char **, const char *, const char *); void kex_proposal_populate_entries(struct ssh *, char *prop[PROPOSAL_MAX], const char *, const char *, const char *, const char *, const char *); - void kex_proposal_free_entries(char *prop[PROPOSAL_MAX]); @@ -202,6 +219,12 @@ int kexgex_client(struct ssh *); int kexgex_server(struct ssh *); int kex_gen_client(struct ssh *); @@ -2205,7 +2087,7 @@ index 00000000..60bc02de + + debug2_f("Acquiring credentials"); + -+ if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, oid)))) ++ if (GSS_ERROR(mm_ssh_gssapi_server_ctx(&ctxt, oid))) + fatal("Unable to acquire credentials for the server"); + + do { @@ -2282,8 +2164,8 @@ index 00000000..60bc02de + type); + } + -+ maj_status = PRIVSEP(ssh_gssapi_accept_ctx(ctxt, &recv_tok, -+ &send_tok, &ret_flags)); ++ maj_status = mm_ssh_gssapi_accept_ctx(ctxt, &recv_tok, ++ &send_tok, &ret_flags); + + gss_release_buffer(&min_status, &recv_tok); + @@ -2319,7 +2201,7 @@ index 00000000..60bc02de + if (!(ret_flags & GSS_C_INTEG_FLAG)) + fatal("Integrity flag wasn't set"); + -+ if (GSS_ERROR(PRIVSEP(ssh_gssapi_sign(ctxt, &gssbuf, &msg_tok)))) ++ if (GSS_ERROR(mm_ssh_gssapi_sign(ctxt, &gssbuf, &msg_tok))) + fatal("Couldn't get MIC"); + + if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_COMPLETE)) != 0 || @@ -2409,7 +2291,7 @@ index 00000000..60bc02de + + debug2_f("Acquiring credentials"); + -+ if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, oid)))) ++ if (GSS_ERROR(mm_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) */ @@ -2431,7 +2313,7 @@ index 00000000..60bc02de + 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)); ++ kex->dh = mm_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"); @@ -2478,8 +2360,8 @@ index 00000000..60bc02de + type); + } + -+ maj_status = PRIVSEP(ssh_gssapi_accept_ctx(ctxt, &recv_tok, -+ &send_tok, &ret_flags)); ++ maj_status = mm_ssh_gssapi_accept_ctx(ctxt, &recv_tok, ++ &send_tok, &ret_flags); + + gss_release_buffer(&min_status, &recv_tok); + @@ -2544,7 +2426,7 @@ index 00000000..60bc02de + gssbuf.value = hash; + gssbuf.length = hashlen; + -+ if (GSS_ERROR(PRIVSEP(ssh_gssapi_sign(ctxt, &gssbuf, &msg_tok)))) ++ if (GSS_ERROR(mm_ssh_gssapi_sign(ctxt, &gssbuf, &msg_tok))) + fatal("Couldn't get MIC"); + + if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_COMPLETE)) != 0 || @@ -2873,7 +2755,7 @@ index 001a8fa1..6edb509a 100644 mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSUSEROK, m); mm_request_receive_expect(pmonitor->m_recvfd, -@@ -1012,4 +1014,57 @@ mm_ssh_gssapi_userok(char *user) +@@ -1012,6 +1014,59 @@ mm_ssh_gssapi_userok(char *user) debug3_f("user %sauthenticated", authenticated ? "" : "not "); return (authenticated); } @@ -2931,6 +2813,8 @@ index 001a8fa1..6edb509a 100644 +} + #endif /* GSSAPI */ + + /* diff --git a/monitor_wrap.h b/monitor_wrap.h index 23ab096a..485590c1 100644 --- a/monitor_wrap.h @@ -3107,8 +2991,8 @@ diff -up a/servconf.c.gsskex b/servconf.c #include "digest.h" +#include "ssh-gss.h" - static void add_listen_addr(ServerOptions *, const char *, - const char *, int); + #if !defined(SSHD_PAM_SERVICE) + # define SSHD_PAM_SERVICE "sshd" @@ -136,8 +137,11 @@ initialize_server_options(ServerOptions options->kerberos_ticket_cleanup = -1; options->kerberos_get_afs_token = -1; @@ -3141,7 +3025,7 @@ diff -up a/servconf.c.gsskex b/servconf.c options->password_authentication = 1; if (options->kbd_interactive_authentication == -1) @@ -506,6 +518,7 @@ typedef enum { - sHostKeyAlgorithms, sPerSourceMaxStartups, sPerSourceNetBlockSize, + sPerSourcePenalties, sPerSourcePenaltyExemptList, sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile, sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor, + sGssKeyEx, sGssKexAlgorithms, sGssStoreRekey, @@ -3262,14 +3146,6 @@ 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.15 2021/01/27 10:05:28 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 @@ -3523,17 +3399,8 @@ diff --git a/sshconnect2.c b/sshconnect2.c index af00fb30..03bc87eb 100644 --- a/sshconnect2.c +++ b/sshconnect2.c -@@ -80,8 +80,6 @@ - #endif - - /* import */ --extern char *client_version_string; --extern char *server_version_string; - extern Options options; - - /* @@ -163,6 +161,11 @@ ssh_kex2(struct ssh *ssh, char *host, struct sockaddr *hostaddr, u_short port) - char *s, *all_key, *hkalgs = NULL; + char *all_key, *hkalgs = NULL; int r, use_known_hosts_order = 0; +#if defined(GSSAPI) && defined(WITH_OPENSSL) @@ -3545,8 +3412,8 @@ index af00fb30..03bc87eb 100644 xxx_hostaddr = hostaddr; xxx_conn_info = cinfo; @@ -206,6 +209,42 @@ ssh_kex2(struct ssh *ssh, char *host, struct sockaddr *hostaddr, u_short port) - kex_proposal_populate_entries(ssh, myproposal, s, options.ciphers, - options.macs, compression_alg_list(options.compression), + options.kex_algorithms, options.ciphers, options.macs, + compression_alg_list(options.compression), hkalgs ? hkalgs : options.hostkeyalgorithms); + +#if defined(GSSAPI) && defined(WITH_OPENSSL) @@ -3587,7 +3454,7 @@ index af00fb30..03bc87eb 100644 free(hkalgs); -@@ -224,17 +256,47 @@ ssh_kex2(struct ssh *ssh, char *host, struct sockaddr *hostaddr, u_short port) +@@ -224,14 +256,44 @@ 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 @@ -3618,11 +3485,8 @@ index af00fb30..03bc87eb 100644 +#endif + ssh_dispatch_run_fatal(ssh, DISPATCH_BLOCK, &ssh->kex->done); + kex_proposal_free_entries(myproposal); - /* remove ext-info from the KEX proposals for rekeying */ - free(myproposal[PROPOSAL_KEX_ALGS]); - myproposal[PROPOSAL_KEX_ALGS] = - compat_kex_proposal(ssh, options.kex_algorithms); +#if defined(GSSAPI) && defined(WITH_OPENSSL) + /* repair myproposal after it was crumpled by the */ + /* ext-info removal above */ @@ -3633,9 +3497,9 @@ index af00fb30..03bc87eb 100644 + free(gss); + } +#endif - if ((r = kex_prop2buf(ssh->kex->my, myproposal)) != 0) - fatal_r(r, "kex_prop2buf"); - + #ifdef DEBUG_KEXDH + /* send 1st encrypted/maced/compressed message */ + if ((r = sshpkt_start(ssh, SSH2_MSG_IGNORE)) != 0 || @@ -330,6 +392,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 *); @@ -3767,6 +3631,20 @@ diff --git a/sshd.c b/sshd.c index 60b2aaf7..d92f03aa 100644 --- a/sshd.c +++ b/sshd.c +@@ -1852,7 +1852,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); + } +diff --git a/sshd-session.c b/sshd-session.c +index 60b2aaf7..d92f03aa 100644 +--- a/sshd-session.c ++++ b/sshd-session.c @@ -817,8 +817,8 @@ notify_hostkeys(struct ssh *ssh) } debug3_f("sent %u hostkeys", nkeys); @@ -3778,16 +3656,6 @@ index 60b2aaf7..d92f03aa 100644 sshpkt_fatal(ssh, r, "%s: send", __func__); sshbuf_free(buf); } -@@ -1852,7 +1852,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); - } @@ -2347,6 +2348,48 @@ do_ssh2_kex(struct ssh *ssh) free(hkalgs); @@ -3838,9 +3706,9 @@ index 60b2aaf7..d92f03aa 100644 if ((r = kex_setup(ssh, myproposal)) != 0) fatal_r(r, "kex_setup"); @@ -2362,7 +2405,18 @@ do_ssh2_kex(struct ssh *ssh) - # ifdef OPENSSL_HAS_ECC + #ifdef OPENSSL_HAS_ECC kex->kex[KEX_ECDH_SHA2] = kex_gen_server; - # endif + #endif -#endif +# ifdef GSSAPI + if (options.gss_keyex) { @@ -4036,9 +3904,9 @@ diff --git a/packet.h b/packet.h int ssh_packet_read(struct ssh *); +int ssh_packet_read_expect(struct ssh *, u_int type); - int ssh_packet_read_poll(struct ssh *); int ssh_packet_read_poll2(struct ssh *, u_char *, u_int32_t *seqnr_p); int ssh_packet_process_incoming(struct ssh *, const char *buf, u_int len); + int ssh_packet_process_read(struct ssh *, int); diff --git a/packet.c b/packet.c --- a/packet.c (revision 8241b9c0529228b4b86d88b1a6076fb9f97e4a99) +++ b/packet.c (date 1703172586447) diff --git a/openssh-8.0p1-pkcs11-uri.patch b/openssh-8.0p1-pkcs11-uri.patch index 1e7940d..c3958cd 100644 --- a/openssh-8.0p1-pkcs11-uri.patch +++ b/openssh-8.0p1-pkcs11-uri.patch @@ -965,9 +965,9 @@ diff -up openssh-9.6p1/ssh-agent.c.pkcs11-uri openssh-9.6p1/ssh-agent.c --- openssh-9.6p1/ssh-agent.c.pkcs11-uri 2023-12-18 15:59:50.000000000 +0100 +++ openssh-9.6p1/ssh-agent.c 2024-01-12 14:25:25.234942360 +0100 @@ -1549,10 +1549,72 @@ add_p11_identity(struct sshkey *key, cha + idtab->nentries++; } - #ifdef ENABLE_PKCS11 +static char * +sanitize_pkcs11_provider(const char *provider) +{ diff --git a/openssh-8.7p1-minrsabits.patch b/openssh-8.7p1-minrsabits.patch index 479b55d..d8577d2 100644 --- a/openssh-8.7p1-minrsabits.patch +++ b/openssh-8.7p1-minrsabits.patch @@ -19,4 +19,4 @@ diff --git a/servconf.c b/servconf.c + { "rsaminsize", sRequiredRSASize, SSHCFG_ALL }, /* alias */ { "channeltimeout", sChannelTimeout, SSHCFG_ALL }, { "unusedconnectiontimeout", sUnusedConnectionTimeout, SSHCFG_ALL }, - { NULL, sBadOption, 0 } + { "sshdsessionpath", sSshdSessionPath, SSHCFG_GLOBAL }, diff --git a/openssh-8.7p1-negotiate-supported-algs.patch b/openssh-8.7p1-negotiate-supported-algs.patch index ee3637f..c4d86e7 100644 --- a/openssh-8.7p1-negotiate-supported-algs.patch +++ b/openssh-8.7p1-negotiate-supported-algs.patch @@ -79,12 +79,12 @@ diff -up openssh-9.3p1/sshconnect2.c.xxx openssh-9.3p1/sshconnect2.c const struct ssh_conn_info *cinfo) { char *myproposal[PROPOSAL_MAX]; -- char *s, *all_key, *hkalgs = NULL; -+ char *s, *all_key, *hkalgs = NULL, *filtered_algs = NULL; +- char *all_key, *hkalgs = NULL; ++ char *all_key, *hkalgs = NULL, *filtered_algs = NULL; int r, use_known_hosts_order = 0; #if defined(GSSAPI) && defined(WITH_OPENSSL) -@@ -260,9 +260,21 @@ ssh_kex2(struct ssh *ssh, char *host, st +@@ -260,10 +260,22 @@ ssh_kex2(struct ssh *ssh, char *host, st if (use_known_hosts_order) hkalgs = order_hostkeyalgs(host, hostaddr, port, cinfo); @@ -100,8 +100,9 @@ diff -up openssh-9.3p1/sshconnect2.c.xxx openssh-9.3p1/sshconnect2.c + options.hostkeyalgorithms, options.pubkey_accepted_algos); + } + - kex_proposal_populate_entries(ssh, myproposal, s, options.ciphers, - options.macs, compression_alg_list(options.compression), + kex_proposal_populate_entries(ssh, myproposal, + options.kex_algorithms, options.ciphers, options.macs, + compression_alg_list(options.compression), - hkalgs ? hkalgs : options.hostkeyalgorithms); + filtered_algs); diff --git a/openssh-8.7p1-nohostsha1proof.patch b/openssh-8.7p1-nohostsha1proof.patch index 7fea800..abc6aa4 100644 --- a/openssh-8.7p1-nohostsha1proof.patch +++ b/openssh-8.7p1-nohostsha1proof.patch @@ -148,15 +148,6 @@ diff -up openssh-8.7p1/regress/unittests/kex/test_kex.c.sshrsacheck openssh-8.7p ASSERT_PTR_NE(keyname, NULL); kex_params.proposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = keyname; ASSERT_INT_EQ(ssh_init(&client, 0, &kex_params), 0); -@@ -180,7 +181,7 @@ do_kex(char *kex) - { - #ifdef WITH_OPENSSL - do_kex_with_key(kex, KEY_RSA, 2048); -- do_kex_with_key(kex, KEY_DSA, 1024); -+ /* do_kex_with_key(kex, KEY_DSA, 1024); */ - #ifdef OPENSSL_HAS_ECC - do_kex_with_key(kex, KEY_ECDSA, 256); - #endif /* OPENSSL_HAS_ECC */ diff -up openssh-8.7p1/regress/unittests/sshkey/test_file.c.sshrsacheck openssh-8.7p1/regress/unittests/sshkey/test_file.c --- openssh-8.7p1/regress/unittests/sshkey/test_file.c.sshrsacheck 2023-01-26 12:04:55.946343408 +0100 +++ openssh-8.7p1/regress/unittests/sshkey/test_file.c 2023-01-26 12:06:35.235164432 +0100 @@ -196,22 +187,6 @@ diff -up openssh-8.7p1/regress/unittests/sshkey/test_fuzz.c.sshrsacheck openssh- TEST_START("fuzz RSA SHA256 sig"); buf = load_file("rsa_1"); -@@ -357,6 +358,7 @@ sshkey_fuzz_tests(void) - sshkey_free(k1); - TEST_DONE(); - -+ /* Skip this test, SHA1 signatures are not supported - TEST_START("fuzz DSA sig"); - buf = load_file("dsa_1"); - ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0); -@@ -364,6 +366,7 @@ sshkey_fuzz_tests(void) - sig_fuzz(k1, NULL); - sshkey_free(k1); - TEST_DONE(); -+ */ - - #ifdef OPENSSL_HAS_ECC - TEST_START("fuzz ECDSA sig"); diff -up openssh-8.7p1/regress/unittests/sshkey/test_sshkey.c.sshrsacheck openssh-8.7p1/regress/unittests/sshkey/test_sshkey.c --- openssh-8.7p1/regress/unittests/sshkey/test_sshkey.c.sshrsacheck 2023-01-26 11:02:52.339413463 +0100 +++ openssh-8.7p1/regress/unittests/sshkey/test_sshkey.c 2023-01-26 11:58:42.324253896 +0100 @@ -241,10 +216,10 @@ diff -up openssh-8.7p1/regress/unittests/sshkey/test_sshkey.c.sshrsacheck openss { size_t len; u_char *sig; -+ /* ssh-rsa implies SHA1, forbidden in DEFAULT cp */ -+ int expected = (sig_alg && strcmp(sig_alg, "ssh-rsa") == 0) ? SSH_ERR_LIBCRYPTO_ERROR : 0; ++ /* ssh-rsa implies SHA1, forbidden in DEFAULT cp in RHEL, permitted in Fedora */ ++ int expected = (sig_alg && strcmp(sig_alg, "ssh-rsa") == 0) ? sshkey_sign(k, &sig, &len, d, l, sig_alg, NULL, NULL, 0) : 0; + if (k && (sshkey_type_plain(k->type) == KEY_DSA || sshkey_type_plain(k->type) == KEY_DSA_CERT)) -+ expected = SSH_ERR_LIBCRYPTO_ERROR; ++ expected = sshkey_sign(k, &sig, &len, d, l, sig_alg, NULL, NULL, 0); ASSERT_INT_EQ(sshkey_sign(k, &sig, &len, d, l, sig_alg, - NULL, NULL, 0), 0); @@ -277,21 +252,6 @@ diff -up openssh-8.7p1/regress/unittests/sshkey/test_sshkey.c.sshrsacheck openss ASSERT_INT_EQ(sshkey_from_blob(sshbuf_ptr(b), sshbuf_len(b), &k4), SSH_ERR_KEY_CERT_INVALID_SIGN_KEY); ASSERT_PTR_EQ(k4, NULL); -diff -up openssh-8.7p1/regress/unittests/sshsig/tests.c.sshrsacheck openssh-8.7p1/regress/unittests/sshsig/tests.c ---- openssh-8.7p1/regress/unittests/sshsig/tests.c.sshrsacheck 2023-01-26 12:19:23.659513651 +0100 -+++ openssh-8.7p1/regress/unittests/sshsig/tests.c 2023-01-26 12:20:28.021044803 +0100 -@@ -102,9 +102,11 @@ tests(void) - check_sig("rsa.pub", "rsa.sig", msg, namespace); - TEST_DONE(); - -+ /* Skip this test, SHA1 signatures are not supported - TEST_START("check DSA signature"); - check_sig("dsa.pub", "dsa.sig", msg, namespace); - TEST_DONE(); -+ */ - - #ifdef OPENSSL_HAS_ECC - TEST_START("check ECDSA signature"); diff -up openssh-8.7p1/serverloop.c.sshrsacheck openssh-8.7p1/serverloop.c --- openssh-8.7p1/serverloop.c.sshrsacheck 2023-01-12 14:57:08.118400073 +0100 +++ openssh-8.7p1/serverloop.c 2023-01-12 14:59:17.330470518 +0100 @@ -332,61 +292,6 @@ diff -up openssh-8.7p1/sshconnect2.c.sshrsacheck openssh-8.7p1/sshconnect2.c goto out; } -diff -up openssh-8.7p1/sshd.c.sshrsacheck openssh-8.7p1/sshd.c ---- openssh-8.7p1/sshd.c.sshrsacheck 2023-01-12 13:29:06.355711140 +0100 -+++ openssh-8.7p1/sshd.c 2023-01-12 13:29:06.358711178 +0100 -@@ -1640,6 +1651,7 @@ main(int ac, char **av) - Authctxt *authctxt; - struct connection_info *connection_info = NULL; - sigset_t sigmask; -+ int forbid_ssh_rsa = 0; - - #ifdef HAVE_SECUREWARE - (void)set_auth_parameters(ac, av); -@@ -1938,6 +1950,33 @@ main(int ac, char **av) - key = NULL; - continue; - } -+ if (key && (sshkey_type_plain(key->type) == KEY_RSA || sshkey_type_plain(key->type) == KEY_RSA_CERT)) { -+ size_t sign_size = 0; -+ u_char *tmp = NULL; -+ u_char data[] = "Test SHA1 vector"; -+ int res; -+ -+ res = sshkey_sign(key, &tmp, &sign_size, data, sizeof(data), NULL, NULL, NULL, 0); -+ free(tmp); -+ if (res == SSH_ERR_LIBCRYPTO_ERROR) { -+ verbose_f("sshd: SHA1 in signatures is disabled for RSA keys"); -+ forbid_ssh_rsa = 1; -+ } -+ } -+ if (key && (sshkey_type_plain(key->type) == KEY_DSA || sshkey_type_plain(key->type) == KEY_DSA_CERT)) { -+ size_t sign_size = 0; -+ u_char *tmp = NULL; -+ u_char data[] = "Test SHA1 vector"; -+ int res; -+ -+ res = sshkey_sign(key, &tmp, &sign_size, data, sizeof(data), NULL, NULL, NULL, 0); -+ free(tmp); -+ if (res == SSH_ERR_LIBCRYPTO_ERROR) { -+ logit_f("sshd: ssh-dss is disabled, skipping key file %s", options.host_key_files[i]); -+ key = NULL; -+ continue; -+ } -+ } - if (sshkey_is_sk(key) && - key->sk_flags & SSH_SK_USER_PRESENCE_REQD) { - debug("host key %s requires user presence, ignoring", -@@ -2275,6 +2306,9 @@ main(int ac, char **av) - - check_ip_options(ssh); - -+ if (forbid_ssh_rsa) -+ ssh->compat |= SSH_RH_RSASIGSHA; -+ - /* Prepare the channels layer */ - channel_init_channels(ssh); - channel_set_af(ssh, options.address_family); diff -up openssh-8.7p1/ssh-rsa.c.sshrsacheck openssh-8.7p1/ssh-rsa.c --- openssh-8.7p1/ssh-rsa.c.sshrsacheck 2023-01-20 13:07:54.180676144 +0100 +++ openssh-8.7p1/ssh-rsa.c 2023-01-20 13:07:54.290677074 +0100 @@ -400,3 +305,34 @@ diff -up openssh-8.7p1/ssh-rsa.c.sshrsacheck openssh-8.7p1/ssh-rsa.c ret = SSH_ERR_SIGNATURE_INVALID; goto out; } +diff -up openssh-9.8p1/sshd-session.c.xxx openssh-9.8p1/sshd-session.c +--- openssh-9.8p1/sshd-session.c.xxx 2024-07-23 15:08:14.794350818 +0200 ++++ openssh-9.8p1/sshd-session.c 2024-07-23 15:40:21.658456636 +0200 +@@ -1305,6 +1305,27 @@ main(int ac, char **av) + + check_ip_options(ssh); + ++ { ++ struct sshkey *rsakey = NULL; ++ rsakey = get_hostkey_private_by_type(KEY_RSA, 0, ssh); ++ if (rsakey == NULL) ++ rsakey = get_hostkey_private_by_type(KEY_RSA_CERT, 0, ssh); ++ ++ if (rsakey != NULL) { ++ size_t sign_size = 0; ++ u_char *tmp = NULL; ++ u_char data[] = "Test SHA1 vector"; ++ int res; ++ ++ res = sshkey_sign(rsakey, &tmp, &sign_size, data, sizeof(data), NULL, NULL, NULL, 0); ++ free(tmp); ++ if (res == SSH_ERR_LIBCRYPTO_ERROR) { ++ verbose_f("SHA1 in signatures is disabled for RSA keys"); ++ ssh->compat |= SSH_RH_RSASIGSHA; ++ } ++ } ++ } ++ + /* Prepare the channels layer */ + channel_init_channels(ssh); + channel_set_af(ssh, options.address_family); diff --git a/openssh-9.0p1-evp-fips-dh.patch b/openssh-9.0p1-evp-fips-dh.patch index d0470d9..8c70197 100644 --- a/openssh-9.0p1-evp-fips-dh.patch +++ b/openssh-9.0p1-evp-fips-dh.patch @@ -200,7 +200,7 @@ diff --color -ru -x regress -x autom4te.cache -x '*.o' -x '*.lo' -x Makefile -x u_char *kbuf = NULL; size_t klen = 0; - int kout, r; -+ int kout, r = 0; ++ int r = 0; #ifdef DEBUG_KEXDH fprintf(stderr, "dh_pub= "); diff --git a/openssh-9.6p1-gsskex-new-api.patch b/openssh-9.6p1-gsskex-new-api.patch new file mode 100644 index 0000000..0f2eabb --- /dev/null +++ b/openssh-9.6p1-gsskex-new-api.patch @@ -0,0 +1,1965 @@ +diff --color -ruNp a/gss-genr.c b/gss-genr.c +--- a/gss-genr.c 2024-05-16 15:49:43.999411060 +0200 ++++ b/gss-genr.c 2024-06-26 12:17:55.586856954 +0200 +@@ -346,6 +346,7 @@ ssh_gssapi_build_ctx(Gssctxt **ctx) + (*ctx)->creds = GSS_C_NO_CREDENTIAL; + (*ctx)->client = GSS_C_NO_NAME; + (*ctx)->client_creds = GSS_C_NO_CREDENTIAL; ++ (*ctx)->first = 1; + } + + /* Delete our context, providing it has been built correctly */ +@@ -371,6 +372,12 @@ ssh_gssapi_delete_ctx(Gssctxt **ctx) + gss_release_name(&ms, &(*ctx)->client); + if ((*ctx)->client_creds != GSS_C_NO_CREDENTIAL) + gss_release_cred(&ms, &(*ctx)->client_creds); ++ sshbuf_free((*ctx)->shared_secret); ++ sshbuf_free((*ctx)->server_pubkey); ++ sshbuf_free((*ctx)->server_host_key_blob); ++ sshbuf_free((*ctx)->server_blob); ++ explicit_bzero((*ctx)->hash, sizeof((*ctx)->hash)); ++ BN_clear_free((*ctx)->dh_client_pub); + + free(*ctx); + *ctx = NULL; +diff --color -ruNp a/kexgssc.c b/kexgssc.c +--- a/kexgssc.c 2024-05-16 15:49:43.820407648 +0200 ++++ b/kexgssc.c 2024-07-02 16:26:25.628746744 +0200 +@@ -47,566 +47,658 @@ + + #include "ssh-gss.h" + +-int +-kexgss_client(struct ssh *ssh) ++static int input_kexgss_hostkey(int, u_int32_t, struct ssh *); ++static int input_kexgss_continue(int, u_int32_t, struct ssh *); ++static int input_kexgss_complete(int, u_int32_t, struct ssh *); ++static int input_kexgss_error(int, u_int32_t, struct ssh *); ++static int input_kexgssgex_group(int, u_int32_t, struct ssh *); ++static int input_kexgssgex_continue(int, u_int32_t, struct ssh *); ++static int input_kexgssgex_complete(int, u_int32_t, struct ssh *); ++ ++static int ++kexgss_final(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; ++ Gssctxt *gss = kex->gss; + struct sshbuf *empty = NULL; +- u_char *msg; +- int type = 0; +- int first = 1; ++ struct sshbuf *shared_secret = NULL; + 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_f("Unexpected KEX type %d", 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) { +- u_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); ++ r = kex_dh_dec(kex, gss->server_blob, &shared_secret); + break; + case KEX_GSS_C25519_SHA256: +- if (sshbuf_ptr(server_blob)[sshbuf_len(server_blob)] & 0x80) ++ if (sshbuf_ptr(gss->server_blob)[sshbuf_len(gss->server_blob)] & 0x80) + fatal("The received key has MSB of last octet set!"); +- r = kex_c25519_dec(kex, server_blob, &shared_secret); ++ r = kex_c25519_dec(kex, gss->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_len(gss->server_blob) != 65) ++ fatal("The received NIST-P256 key did not match " ++ "expected length (expected 65, got %zu)", ++ sshbuf_len(gss->server_blob)); + +- if (sshbuf_ptr(server_blob)[0] != POINT_CONVERSION_UNCOMPRESSED) ++ if (sshbuf_ptr(gss->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); ++ r = kex_ecdh_dec(kex, gss->server_blob, &shared_secret); + break; + default: + r = SSH_ERR_INVALID_ARGUMENT; + break; + } +- if (r != 0) ++ if (r != 0) { ++ ssh_gssapi_delete_ctx(&kex->gss); + goto out; ++ } + + if ((empty = sshbuf_new()) == NULL) { ++ ssh_gssapi_delete_ctx(&kex->gss); + 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) ++ r = kex_gen_hash(kex->hash_alg, kex->client_version, ++ kex->server_version, kex->my, kex->peer, ++ (gss->server_host_key_blob ? gss->server_host_key_blob : empty), ++ kex->client_pub, gss->server_blob, shared_secret, ++ hash, &hashlen); ++ sshbuf_free(empty); ++ if (r != 0) + fatal_f("Unexpected KEX type %d", kex->kex_type); + +- gssbuf.value = hash; +- gssbuf.length = hashlen; ++ gss->buf.value = hash; ++ gss->buf.length = hashlen; + + /* Verify that the hash matches the MIC we just got. */ +- if (GSS_ERROR(ssh_gssapi_checkmic(ctxt, &gssbuf, &msg_tok))) ++ if (GSS_ERROR(ssh_gssapi_checkmic(gss, &gss->buf, &gss->msg_tok))) + sshpkt_disconnect(ssh, "Hash's MIC didn't verify"); + +- gss_release_buffer(&min_status, &msg_tok); ++ gss_release_buffer(&gss->minor, &gss->msg_tok); + + if (kex->gss_deleg_creds) +- ssh_gssapi_credentials_updated(ctxt); ++ ssh_gssapi_credentials_updated(gss); + + if (gss_kex_context == NULL) +- gss_kex_context = ctxt; ++ gss_kex_context = gss; + else +- ssh_gssapi_delete_ctx(&ctxt); ++ ssh_gssapi_delete_ctx(&kex->gss); + + if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) == 0) + r = kex_send_newkeys(ssh); + ++ if (kex->gss != NULL) { ++ sshbuf_free(gss->server_host_key_blob); ++ gss->server_host_key_blob = NULL; ++ sshbuf_free(gss->server_blob); ++ gss->server_blob = NULL; ++ } + 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); ++ explicit_bzero(hash, sizeof(hash)); + sshbuf_free(shared_secret); + sshbuf_free(kex->client_pub); + kex->client_pub = NULL; + return r; + } + ++static int ++kexgss_init_ctx(struct ssh *ssh, ++ gss_buffer_desc *token_ptr) ++{ ++ struct kex *kex = ssh->kex; ++ Gssctxt *gss = kex->gss; ++ gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; ++ OM_uint32 ret_flags; ++ int r; ++ ++ debug("Calling gss_init_sec_context"); ++ ++ gss->major = ssh_gssapi_init_ctx(gss, kex->gss_deleg_creds, ++ token_ptr, &send_tok, &ret_flags); ++ ++ if (GSS_ERROR(gss->major)) { ++ /* XXX Useless 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(&gss->minor, token_ptr); ++ ++ if (gss->major == 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 (gss->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)); ++ gss->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(&gss->minor, &send_tok); ++ ++ /* If we've sent them data, they should reply */ ++ ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_HOSTKEY, &input_kexgss_hostkey); ++ ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_CONTINUE, &input_kexgss_continue); ++ ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_COMPLETE, &input_kexgss_complete); ++ ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_ERROR, &input_kexgss_error); ++ return 0; ++ } ++ /* No data, and not complete */ ++ if (gss->major != GSS_S_COMPLETE) ++ fatal("Not complete, and no token output"); ++ ++ if (gss->major & GSS_S_CONTINUE_NEEDED) ++ return kexgss_init_ctx(ssh, token_ptr); ++ ++ return kexgss_final(ssh); ++} ++ + int +-kexgssgex_client(struct ssh *ssh) ++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 *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) ++ ssh_gssapi_build_ctx(&kex->gss); ++ if (ssh_gssapi_id_kex(kex->gss, kex->name, kex->kex_type) == GSS_C_NO_OID) + fatal("Couldn't identify host exchange"); + +- if (ssh_gssapi_import_name(ctxt, kex->gss_host)) ++ if (ssh_gssapi_import_name(kex->gss, kex->gss_host)) + fatal("Couldn't import hostname"); + + if (kex->gss_client && +- ssh_gssapi_client_identity(ctxt, kex->gss_client)) ++ ssh_gssapi_client_identity(kex->gss, kex->gss_client)) + fatal("Couldn't acquire client credentials"); + +- debug("Doing group exchange"); +- nbits = dh_estimate(kex->dh_need * 8); ++ /* 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_f("Unexpected KEX type %d", kex->kex_type); ++ } ++ if (r != 0) { ++ ssh_gssapi_delete_ctx(&kex->gss); ++ return r; ++ } ++ return kexgss_init_ctx(ssh, GSS_C_NO_BUFFER); ++} + +- 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)); ++static int ++input_kexgss_hostkey(int type, ++ u_int32_t seq, ++ struct ssh *ssh) ++{ ++ Gssctxt *gss = ssh->kex->gss; ++ u_char *tmp = NULL; ++ size_t tmp_len = 0; ++ int r; ++ ++ debug("Received KEXGSS_HOSTKEY"); ++ if (gss->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 ((gss->server_host_key_blob = sshbuf_from(tmp, tmp_len)) == NULL) ++ fatal("sshbuf_from failed"); ++ return 0; ++} + +- if ((r = ssh_packet_read_expect(ssh, SSH2_MSG_KEXGSS_GROUP)) != 0) +- fatal("Error: %s", ssh_err(r)); ++static int ++input_kexgss_continue(int type, ++ u_int32_t seq, ++ struct ssh *ssh) ++{ ++ Gssctxt *gss = ssh->kex->gss; ++ gss_buffer_desc recv_tok = GSS_C_EMPTY_BUFFER; ++ int r; + +- if ((r = sshpkt_get_bignum2(ssh, &p)) != 0 || +- (r = sshpkt_get_bignum2(ssh, &g)) != 0 || ++ ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_HOSTKEY, NULL); ++ ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_CONTINUE, NULL); ++ ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_COMPLETE, NULL); ++ ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_ERROR, NULL); ++ ++ debug("Received GSSAPI_CONTINUE"); ++ if (gss->major == 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("shpkt_get_bignum2 failed: %s", ssh_err(r)); ++ fatal("Failed to read token: %s", ssh_err(r)); ++ if (!(gss->major & GSS_S_CONTINUE_NEEDED)) ++ fatal("Didn't receive a SSH2_MSG_KEXGSS_COMPLETE when I expected it"); ++ return kexgss_init_ctx(ssh, &recv_tok); ++} + +- 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); ++static int ++input_kexgss_complete(int type, ++ u_int32_t seq, ++ struct ssh *ssh) ++{ ++ Gssctxt *gss = ssh->kex->gss; ++ gss_buffer_desc recv_tok = GSS_C_EMPTY_BUFFER; ++ u_char c; ++ int r; + +- if ((kex->dh = dh_new_group(g, p)) == NULL) +- fatal("dn_new_group() failed"); +- p = g = NULL; /* belong to kex->dh now */ ++ ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_HOSTKEY, NULL); ++ ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_CONTINUE, NULL); ++ ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_COMPLETE, NULL); ++ ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_ERROR, NULL); ++ ++ debug("Received GSSAPI_COMPLETE"); ++ if (gss->msg_tok.value != NULL) ++ fatal("Received GSSAPI_COMPLETE twice?"); ++ if ((r = sshpkt_getb_froms(ssh, &gss->server_blob)) != 0 || ++ (r = ssh_gssapi_sshpkt_get_buffer_desc(ssh, &gss->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 (gss->major == GSS_S_COMPLETE) ++ sshpkt_disconnect(ssh, "Protocol error: received token when complete"); ++ } else { ++ if (gss->major != 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."); + +- if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0) +- goto out; +- DH_get0_key(kex->dh, &pub_key, NULL); ++ if (gss->major & GSS_S_CONTINUE_NEEDED) ++ return kexgss_init_ctx(ssh, &recv_tok); + +- token_ptr = GSS_C_NO_BUFFER; ++ return kexgss_final(ssh); ++} + +- 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"); +- } ++static int ++input_kexgss_error(int type, ++ u_int32_t seq, ++ struct ssh *ssh) ++{ ++ Gssctxt *gss = ssh->kex->gss; ++ u_char *msg; ++ int r; + +- /* 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"); +- } ++ debug("Received Error"); ++ if ((r = sshpkt_get_u32(ssh, &gss->major)) != 0 || ++ (r = sshpkt_get_u32(ssh, &gss->minor)) != 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); ++ return 0; ++} + +- /* +- * 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) { +- u_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); ++/*******************************************************/ ++/******************** KEXGSSGEX ************************/ ++/*******************************************************/ ++ ++int ++kexgssgex_client(struct ssh *ssh) ++{ ++ struct kex *kex = ssh->kex; ++ int r; ++ ++ /* Initialise our GSSAPI world */ ++ ssh_gssapi_build_ctx(&kex->gss); ++ if (ssh_gssapi_id_kex(kex->gss, kex->name, kex->kex_type) == GSS_C_NO_OID) ++ fatal("Couldn't identify host exchange"); ++ ++ if (ssh_gssapi_import_name(kex->gss, kex->gss_host)) ++ fatal("Couldn't import hostname"); ++ ++ if (kex->gss_client && ++ ssh_gssapi_client_identity(kex->gss, kex->gss_client)) ++ fatal("Couldn't acquire client credentials"); ++ ++ debug("Doing group exchange"); ++ kex->min = DH_GRP_MIN; ++ kex->max = DH_GRP_MAX; ++ kex->nbits = dh_estimate(kex->dh_need * 8); ++ ++ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_GROUPREQ)) != 0 || ++ (r = sshpkt_put_u32(ssh, kex->min)) != 0 || ++ (r = sshpkt_put_u32(ssh, kex->nbits)) != 0 || ++ (r = sshpkt_put_u32(ssh, kex->max)) != 0 || ++ (r = sshpkt_send(ssh)) != 0) ++ fatal("Failed to construct a packet: %s", ssh_err(r)); ++ ++ debug("Wait SSH2_MSG_KEXGSS_GROUP"); ++ ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_GROUP, &input_kexgssgex_group); ++ return 0; ++} ++ ++static int ++kexgssgex_final(struct ssh *ssh) ++{ ++ struct kex *kex = ssh->kex; ++ Gssctxt *gss = kex->gss; ++ struct sshbuf *buf = NULL; ++ struct sshbuf *empty = NULL; ++ struct sshbuf *shared_secret = NULL; ++ BIGNUM *dh_server_pub = NULL; ++ const BIGNUM *pub_key, *dh_p, *dh_g; ++ u_char hash[SSH_DIGEST_MAX_LENGTH]; ++ size_t hashlen; ++ int r = SSH_ERR_INTERNAL_ERROR; + + /* + * We _must_ have received a COMPLETE message in reply from the +- * server, which will have set dh_server_pub and msg_tok ++ * 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"); +- + /* 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) ++ (r = sshbuf_put_stringb(buf, gss->server_blob)) != 0 || ++ (r = sshbuf_get_bignum2(buf, &dh_server_pub)) != 0) { ++ ssh_gssapi_delete_ctx(&kex->gss); + goto out; ++ } + sshbuf_free(buf); + buf = NULL; + + if ((shared_secret = sshbuf_new()) == NULL) { ++ ssh_gssapi_delete_ctx(&kex->gss); + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + +- if ((r = kex_dh_compute_key(kex, dh_server_pub, shared_secret)) != 0) ++ if ((r = kex_dh_compute_key(kex, dh_server_pub, shared_secret)) != 0) { ++ ssh_gssapi_delete_ctx(&kex->gss); + goto out; ++ } ++ + if ((empty = sshbuf_new()) == NULL) { ++ ssh_gssapi_delete_ctx(&kex->gss); + r = SSH_ERR_ALLOC_FAIL; + 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->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) ++ r = kexgex_hash(kex->hash_alg, kex->client_version, ++ kex->server_version, kex->my, kex->peer, ++ (gss->server_host_key_blob ? gss->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); ++ sshbuf_free(empty); ++ if (r != 0) + fatal("Failed to calculate hash: %s", ssh_err(r)); + +- gssbuf.value = hash; +- gssbuf.length = hashlen; ++ gss->buf.value = hash; ++ gss->buf.length = hashlen; + + /* Verify that the hash matches the MIC we just got. */ +- if (GSS_ERROR(ssh_gssapi_checkmic(ctxt, &gssbuf, &msg_tok))) ++ if (GSS_ERROR(ssh_gssapi_checkmic(gss, &gss->buf, &gss->msg_tok))) + sshpkt_disconnect(ssh, "Hash's MIC didn't verify"); + +- gss_release_buffer(&min_status, &msg_tok); ++ gss_release_buffer(&gss->minor, &gss->msg_tok); + + if (kex->gss_deleg_creds) +- ssh_gssapi_credentials_updated(ctxt); ++ ssh_gssapi_credentials_updated(gss); + + if (gss_kex_context == NULL) +- gss_kex_context = ctxt; ++ gss_kex_context = gss; + else +- ssh_gssapi_delete_ctx(&ctxt); ++ ssh_gssapi_delete_ctx(&kex->gss); + + /* Finally derive the keys and send them */ + if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) == 0) + r = kex_send_newkeys(ssh); ++ ++ if (kex->gss != NULL) { ++ sshbuf_free(gss->server_host_key_blob); ++ gss->server_host_key_blob = NULL; ++ sshbuf_free(gss->server_blob); ++ gss->server_blob = NULL; ++ } + 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; + } + ++static int ++kexgssgex_init_ctx(struct ssh *ssh, ++ gss_buffer_desc *token_ptr) ++{ ++ struct kex *kex = ssh->kex; ++ Gssctxt *gss = kex->gss; ++ const BIGNUM *pub_key; ++ gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; ++ OM_uint32 ret_flags; ++ int r; ++ ++ /* Step 2 - call GSS_Init_sec_context() */ ++ debug("Calling gss_init_sec_context"); ++ ++ gss->major = ssh_gssapi_init_ctx(gss, kex->gss_deleg_creds, ++ token_ptr, &send_tok, &ret_flags); ++ ++ if (GSS_ERROR(gss->major)) { ++ /* XXX Useless 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(&gss->minor, token_ptr); ++ ++ if (gss->major == 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 (gss->first) { ++ DH_get0_key(kex->dh, &pub_key, NULL); ++ 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("failed to construct packet: %s", ssh_err(r)); ++ gss->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(&gss->minor, &send_tok); ++ ++ /* If we've sent them data, they should reply */ ++ ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_HOSTKEY, &input_kexgss_hostkey); ++ ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_CONTINUE, &input_kexgssgex_continue); ++ ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_COMPLETE, &input_kexgssgex_complete); ++ ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_ERROR, &input_kexgss_error); ++ return 0; ++ } ++ /* No data, and not complete */ ++ if (gss->major != GSS_S_COMPLETE) ++ fatal("Not complete, and no token output"); ++ ++ if (gss->major & GSS_S_CONTINUE_NEEDED) ++ return kexgssgex_init_ctx(ssh, token_ptr); ++ ++ return kexgssgex_final(ssh); ++} ++ ++static int ++input_kexgssgex_group(int type, ++ u_int32_t seq, ++ struct ssh *ssh) ++{ ++ struct kex *kex = ssh->kex; ++ BIGNUM *p = NULL; ++ BIGNUM *g = NULL; ++ int r; ++ ++ debug("Received SSH2_MSG_KEXGSS_GROUP"); ++ ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_GROUP, NULL); ++ ++ 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) < kex->min || BN_num_bits(p) > kex->max) ++ fatal("GSSGRP_GEX group out of range: %d !< %d !< %d", ++ kex->min, BN_num_bits(p), kex->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) { ++ ssh_gssapi_delete_ctx(&kex->gss); ++ DH_free(kex->dh); ++ kex->dh = NULL; ++ return r; ++ } ++ ++ return kexgssgex_init_ctx(ssh, GSS_C_NO_BUFFER); ++} ++ ++static int ++input_kexgssgex_continue(int type, ++ u_int32_t seq, ++ struct ssh *ssh) ++{ ++ Gssctxt *gss = ssh->kex->gss; ++ gss_buffer_desc recv_tok = GSS_C_EMPTY_BUFFER; ++ int r; ++ ++ ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_HOSTKEY, NULL); ++ ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_CONTINUE, NULL); ++ ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_COMPLETE, NULL); ++ ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_ERROR, NULL); ++ ++ debug("Received GSSAPI_CONTINUE"); ++ if (gss->major == 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)); ++ if (!(gss->major & GSS_S_CONTINUE_NEEDED)) ++ fatal("Didn't receive a SSH2_MSG_KEXGSS_COMPLETE when I expected it"); ++ return kexgssgex_init_ctx(ssh, &recv_tok); ++} ++ ++static int ++input_kexgssgex_complete(int type, ++ u_int32_t seq, ++ struct ssh *ssh) ++{ ++ Gssctxt *gss = ssh->kex->gss; ++ gss_buffer_desc recv_tok = GSS_C_EMPTY_BUFFER; ++ u_char c; ++ int r; ++ ++ ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_HOSTKEY, NULL); ++ ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_CONTINUE, NULL); ++ ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_COMPLETE, NULL); ++ ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_ERROR, NULL); ++ ++ debug("Received GSSAPI_COMPLETE"); ++ if (gss->msg_tok.value != NULL) ++ fatal("Received GSSAPI_COMPLETE twice?"); ++ if ((r = sshpkt_getb_froms(ssh, &gss->server_blob)) != 0 || ++ (r = ssh_gssapi_sshpkt_get_buffer_desc(ssh, &gss->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 (gss->major == GSS_S_COMPLETE) ++ sshpkt_disconnect(ssh, "Protocol error: received token when complete"); ++ } else { ++ if (gss->major != 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."); ++ ++ if (gss->major & GSS_S_CONTINUE_NEEDED) ++ return kexgssgex_init_ctx(ssh, &recv_tok); ++ ++ return kexgssgex_final(ssh); ++} ++ + #endif /* defined(GSSAPI) && defined(WITH_OPENSSL) */ +diff --color -ruNp a/kexgsss.c b/kexgsss.c +--- a/kexgsss.c 2024-05-16 15:49:43.820407648 +0200 ++++ b/kexgsss.c 2024-07-02 16:29:05.744790839 +0200 +@@ -50,33 +50,18 @@ + + extern ServerOptions options; + ++static int input_kexgss_init(int, u_int32_t, struct ssh *); ++static int input_kexgss_continue(int, u_int32_t, struct ssh *); ++static int input_kexgssgex_groupreq(int, u_int32_t, struct ssh *); ++static int input_kexgssgex_init(int, u_int32_t, struct ssh *); ++static int input_kexgssgex_continue(int, u_int32_t, struct ssh *); ++ + 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 */ + +@@ -92,135 +77,91 @@ kexgss_server(struct ssh *ssh) + debug2_f("Identifying %s", kex->name); + oid = ssh_gssapi_id_kex(NULL, kex->name, kex->kex_type); + if (oid == GSS_C_NO_OID) +- fatal("Unknown gssapi mechanism"); ++ fatal("Unknown gssapi mechanism"); + + debug2_f("Acquiring credentials"); + +- if (GSS_ERROR(mm_ssh_gssapi_server_ctx(&ctxt, oid))) ++ if (GSS_ERROR(mm_ssh_gssapi_server_ctx(&kex->gss, 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)); ++ ssh_gssapi_build_ctx(&kex->gss); ++ if (kex->gss == NULL) ++ fatal("Unable to allocate memory for gss context"); ++ ++ ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_INIT, &input_kexgss_init); ++ ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_CONTINUE, &input_kexgss_continue); ++ debug("Wait SSH2_MSG_KEXGSS_INIT"); ++ return 0; ++} + +- 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_f("Unexpected KEX type %d", 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); +- } ++static inline void ++kexgss_accept_ctx(struct ssh *ssh, ++ gss_buffer_desc *recv_tok, ++ gss_buffer_desc *send_tok, ++ OM_uint32 *ret_flags) ++{ ++ Gssctxt *gss = ssh->kex->gss; ++ int r; + +- maj_status = mm_ssh_gssapi_accept_ctx(ctxt, &recv_tok, +- &send_tok, &ret_flags); ++ gss->major = mm_ssh_gssapi_accept_ctx(gss, recv_tok, send_tok, ret_flags); ++ gss_release_buffer(&gss->minor, recv_tok); + +- gss_release_buffer(&min_status, &recv_tok); ++ if (gss->major != GSS_S_COMPLETE && send_tok->length == 0) ++ fatal("Zero length token output when incomplete"); + +- if (maj_status != GSS_S_COMPLETE && send_tok.length == 0) +- fatal("Zero length token output when incomplete"); ++ if (gss->buf.value == NULL) ++ fatal("No client public key"); + +- if (gssbuf.value == NULL) +- fatal("No client public key"); ++ if (gss->major & 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(&gss->minor, send_tok); ++ } ++} + +- 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); ++static inline int ++kexgss_final(struct ssh *ssh, ++ gss_buffer_desc *send_tok, ++ OM_uint32 *ret_flags) ++{ ++ struct kex *kex = ssh->kex; ++ Gssctxt *gss = kex->gss; ++ gss_buffer_desc msg_tok; ++ int r; ++ ++ ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_INIT, NULL); ++ ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_CONTINUE, NULL); + +- if (GSS_ERROR(maj_status)) { +- if (send_tok.length > 0) { ++ if (GSS_ERROR(gss->major)) { ++ 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_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)) ++ if (!(*ret_flags & GSS_C_MUTUAL_FLAG)) + fatal("Mutual Authentication flag wasn't set"); + +- if (!(ret_flags & GSS_C_INTEG_FLAG)) ++ if (!(*ret_flags & GSS_C_INTEG_FLAG)) + fatal("Integrity flag wasn't set"); + +- if (GSS_ERROR(mm_ssh_gssapi_sign(ctxt, &gssbuf, &msg_tok))) ++ if (GSS_ERROR(mm_ssh_gssapi_sign(gss, &gss->buf, &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_stringb(ssh, gss->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 (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) ++ (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 */ +@@ -229,59 +170,139 @@ kexgss_server(struct ssh *ssh) + 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); ++ gss_release_buffer(&gss->minor, send_tok); ++ gss_release_buffer(&gss->minor, &msg_tok); + + if (gss_kex_context == NULL) +- gss_kex_context = ctxt; ++ gss_kex_context = gss; + else +- ssh_gssapi_delete_ctx(&ctxt); ++ ssh_gssapi_delete_ctx(&kex->gss); + +- if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) == 0) ++ if ((r = kex_derive_keys(ssh, gss->hash, gss->hashlen, gss->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); ++ ++ if (kex->gss != NULL) { ++ explicit_bzero(gss->hash, sizeof(gss->hash)); ++ sshbuf_free(gss->shared_secret); ++ gss->shared_secret = NULL; ++ sshbuf_free(gss->server_pubkey); ++ gss->server_pubkey = NULL; ++ } + return r; + } + +-int +-kexgssgex_server(struct ssh *ssh) ++static int ++input_kexgss_init(int type, ++ u_int32_t seq, ++ struct ssh *ssh) + { + struct kex *kex = ssh->kex; +- OM_uint32 maj_status, min_status; ++ Gssctxt *gss = kex->gss; ++ struct sshbuf *empty; ++ struct sshbuf *client_pubkey = NULL; ++ gss_buffer_desc recv_tok, send_tok = GSS_C_EMPTY_BUFFER; ++ OM_uint32 ret_flags = 0; ++ int r; ++ ++ debug("SSH2_MSG_KEXGSS_INIT received"); ++ ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_INIT, NULL); + +- /* +- * 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. ++ 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, &gss->server_pubkey, &gss->shared_secret); ++ break; ++ case KEX_GSS_NISTP256_SHA256: ++ r = kex_ecdh_enc(kex, client_pubkey, &gss->server_pubkey, &gss->shared_secret); ++ break; ++ case KEX_GSS_C25519_SHA256: ++ r = kex_c25519_enc(kex, client_pubkey, &gss->server_pubkey, &gss->shared_secret); ++ break; ++ default: ++ fatal_f("Unexpected KEX type %d", kex->kex_type); ++ } ++ if (r != 0) { ++ sshbuf_free(client_pubkey); ++ ssh_gssapi_delete_ctx(&kex->gss); ++ return r; ++ } ++ ++ /* Send SSH_MSG_KEXGSS_HOSTKEY here, if we want */ ++ ++ if ((empty = sshbuf_new()) == NULL) { ++ sshbuf_free(client_pubkey); ++ ssh_gssapi_delete_ctx(&kex->gss); ++ return SSH_ERR_ALLOC_FAIL; ++ } ++ ++ /* Calculate the hash early so we can free the ++ * client_pubkey, which has reference to the parent ++ * buffer state->incoming_packet + */ ++ gss->hashlen = sizeof(gss->hash); ++ r = kex_gen_hash(kex->hash_alg, kex->client_version, kex->server_version, ++ kex->peer, kex->my, empty, client_pubkey, gss->server_pubkey, ++ gss->shared_secret, gss->hash, &gss->hashlen); ++ sshbuf_free(empty); ++ sshbuf_free(client_pubkey); ++ if (r != 0) { ++ ssh_gssapi_delete_ctx(&kex->gss); ++ return r; ++ } ++ ++ gss->buf.value = gss->hash; ++ gss->buf.length = gss->hashlen; ++ ++ kexgss_accept_ctx(ssh, &recv_tok, &send_tok, &ret_flags); ++ if (gss->major & GSS_S_CONTINUE_NEEDED) ++ return 0; + ++ return kexgss_final(ssh, &send_tok, &ret_flags); ++} ++ ++static int ++input_kexgss_continue(int type, ++ u_int32_t seq, ++ struct ssh *ssh) ++{ ++ Gssctxt *gss = ssh->kex->gss; ++ gss_buffer_desc recv_tok, send_tok = GSS_C_EMPTY_BUFFER; + 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; ++ int r; ++ ++ 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)); ++ ++ kexgss_accept_ctx(ssh, &recv_tok, &send_tok, &ret_flags); ++ if (gss->major & GSS_S_CONTINUE_NEEDED) ++ return 0; ++ ++ return kexgss_final(ssh, &send_tok, &ret_flags); ++} ++ ++/*******************************************************/ ++/******************** KEXGSSGEX ************************/ ++/*******************************************************/ ++ ++int ++kexgssgex_server(struct ssh *ssh) ++{ ++ struct kex *kex = ssh->kex; + 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 */ + +@@ -289,153 +310,125 @@ kexgssgex_server(struct ssh *ssh) + * 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); ++ if (!ssh_gssapi_oid_table_ok()) { ++ mechs = ssh_gssapi_server_mechanisms(); ++ free(mechs); ++ } + + debug2_f("Identifying %s", kex->name); + oid = ssh_gssapi_id_kex(NULL, kex->name, kex->kex_type); + if (oid == GSS_C_NO_OID) +- fatal("Unknown gssapi mechanism"); ++ fatal("Unknown gssapi mechanism"); + + debug2_f("Acquiring credentials"); + +- if (GSS_ERROR(mm_ssh_gssapi_server_ctx(&ctxt, oid))) ++ if (GSS_ERROR(mm_ssh_gssapi_server_ctx(&kex->gss, 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 = mm_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"); +- } ++ ssh_gssapi_build_ctx(&kex->gss); ++ if (kex->gss == NULL) ++ fatal("Unable to allocate memory for gss context"); + +- 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; ++ debug("Doing group exchange"); ++ ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_GROUPREQ, &input_kexgssgex_groupreq); ++ return 0; ++} + +- 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)); ++static inline void ++kexgssgex_accept_ctx(struct ssh *ssh, ++ gss_buffer_desc *recv_tok, ++ gss_buffer_desc *send_tok, ++ OM_uint32 *ret_flags) ++{ ++ Gssctxt *gss = ssh->kex->gss; ++ int 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); +- } ++ gss->major = mm_ssh_gssapi_accept_ctx(gss, recv_tok, send_tok, ret_flags); ++ gss_release_buffer(&gss->minor, recv_tok); + +- maj_status = mm_ssh_gssapi_accept_ctx(ctxt, &recv_tok, +- &send_tok, &ret_flags); ++ if (gss->major != GSS_S_COMPLETE && send_tok->length == 0) ++ fatal("Zero length token output when incomplete"); + +- gss_release_buffer(&min_status, &recv_tok); ++ if (gss->dh_client_pub == NULL) ++ fatal("No client public key"); + +- if (maj_status != GSS_S_COMPLETE && send_tok.length == 0) +- fatal("Zero length token output when incomplete"); ++ if (gss->major & 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(&gss->minor, send_tok); ++ } ++} + +- if (dh_client_pub == NULL) +- fatal("No client public key"); ++static inline int ++kexgssgex_final(struct ssh *ssh, ++ gss_buffer_desc *send_tok, ++ OM_uint32 *ret_flags) ++{ ++ struct kex *kex = ssh->kex; ++ Gssctxt *gss = kex->gss; ++ gss_buffer_desc msg_tok; ++ u_char hash[SSH_DIGEST_MAX_LENGTH]; ++ size_t hashlen; ++ const BIGNUM *pub_key, *dh_p, *dh_g; ++ struct sshbuf *shared_secret = NULL; ++ struct sshbuf *empty = NULL; ++ int r; + +- 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); ++ ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_INIT, NULL); ++ ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_CONTINUE, NULL); + +- if (GSS_ERROR(maj_status)) { +- if (send_tok.length > 0) { ++ if (GSS_ERROR(gss->major)) { ++ 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_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)) ++ if (!(*ret_flags & GSS_C_MUTUAL_FLAG)) + fatal("Mutual Authentication flag wasn't set"); + +- if (!(ret_flags & GSS_C_INTEG_FLAG)) ++ if (!(*ret_flags & GSS_C_INTEG_FLAG)) + fatal("Integrity flag wasn't set"); + + /* calculate shared secret */ +- if ((shared_secret = sshbuf_new()) == NULL) { ++ shared_secret = sshbuf_new(); ++ if (shared_secret == NULL) { ++ ssh_gssapi_delete_ctx(&kex->gss); + r = SSH_ERR_ALLOC_FAIL; + goto out; + } +- if ((r = kex_dh_compute_key(kex, dh_client_pub, shared_secret)) != 0) ++ if ((r = kex_dh_compute_key(kex, gss->dh_client_pub, shared_secret)) != 0) { ++ ssh_gssapi_delete_ctx(&kex->gss); + goto out; ++ } ++ ++ if ((empty = sshbuf_new()) == NULL) { ++ ssh_gssapi_delete_ctx(&kex->gss); ++ r = SSH_ERR_ALLOC_FAIL; ++ 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) ++ r = kexgex_hash(kex->hash_alg, kex->client_version, kex->server_version, ++ kex->peer, kex->my, empty, kex->min, kex->nbits, kex->max, dh_p, dh_g, ++ gss->dh_client_pub, pub_key, sshbuf_ptr(shared_secret), ++ sshbuf_len(shared_secret), hash, &hashlen); ++ sshbuf_free(empty); ++ if (r != 0) + fatal("kexgex_hash failed: %s", ssh_err(r)); + +- gssbuf.value = hash; +- gssbuf.length = hashlen; ++ gss->buf.value = hash; ++ gss->buf.length = hashlen; + +- if (GSS_ERROR(mm_ssh_gssapi_sign(ctxt, &gssbuf, &msg_tok))) ++ if (GSS_ERROR(mm_ssh_gssapi_sign(gss, &gss->buf, &msg_tok))) + fatal("Couldn't get MIC"); + + if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_COMPLETE)) != 0 || +@@ -443,24 +436,24 @@ kexgssgex_server(struct ssh *ssh) + (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 (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) ++ (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)); ++ fatal("sshpkt_send failed: %s", ssh_err(r)); + +- gss_release_buffer(&min_status, &send_tok); +- gss_release_buffer(&min_status, &msg_tok); ++ gss_release_buffer(&gss->minor, send_tok); ++ gss_release_buffer(&gss->minor, &msg_tok); + + if (gss_kex_context == NULL) +- gss_kex_context = ctxt; ++ gss_kex_context = gss; + else +- ssh_gssapi_delete_ctx(&ctxt); ++ ssh_gssapi_delete_ctx(&kex->gss); + + /* Finally derive the keys and send them */ + if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) == 0) +@@ -470,13 +463,128 @@ kexgssgex_server(struct ssh *ssh) + * just exchanged. */ + if (options.gss_store_rekey) + ssh_gssapi_rekey_creds(); ++ ++ if (kex->gss != NULL) ++ BN_clear_free(gss->dh_client_pub); ++ + 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; + } ++ ++static int ++input_kexgssgex_groupreq(int type, ++ u_int32_t seq, ++ struct ssh *ssh) ++{ ++ struct kex *kex = ssh->kex; ++ const BIGNUM *dh_p, *dh_g; ++ int min = -1, max = -1, nbits = -1; ++ int cmin = -1, cmax = -1; /* client proposal */ ++ int r; ++ ++ /* 5. S generates an ephemeral key pair (do the allocations early) */ ++ ++ debug("SSH2_MSG_KEXGSS_GROUPREQ received"); ++ ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_GROUPREQ, NULL); ++ ++ /* 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 = mm_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) { ++ ssh_gssapi_delete_ctx(&kex->gss); ++ DH_free(kex->dh); ++ kex->dh = NULL; ++ return r; ++ } ++ ++ ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_INIT, &input_kexgssgex_init); ++ ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_CONTINUE, &input_kexgssgex_continue); ++ debug("Wait SSH2_MSG_KEXGSS_INIT"); ++ return 0; ++} ++ ++static int ++input_kexgssgex_init(int type, ++ u_int32_t seq, ++ struct ssh *ssh) ++{ ++ Gssctxt *gss = ssh->kex->gss; ++ gss_buffer_desc recv_tok, send_tok = GSS_C_EMPTY_BUFFER; ++ OM_uint32 ret_flags = 0; ++ int r; ++ ++ debug("SSH2_MSG_KEXGSS_INIT received"); ++ ssh_dispatch_set(ssh, SSH2_MSG_KEXGSS_INIT, NULL); ++ ++ if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh, &recv_tok)) != 0 || ++ (r = sshpkt_get_bignum2(ssh, &gss->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 */ ++ ++ kexgssgex_accept_ctx(ssh, &recv_tok, &send_tok, &ret_flags); ++ if (gss->major & GSS_S_CONTINUE_NEEDED) ++ return 0; ++ ++ return kexgssgex_final(ssh, &send_tok, &ret_flags); ++} ++ ++static int ++input_kexgssgex_continue(int type, ++ u_int32_t seq, ++ struct ssh *ssh) ++{ ++ Gssctxt *gss = ssh->kex->gss; ++ gss_buffer_desc recv_tok, send_tok = GSS_C_EMPTY_BUFFER; ++ OM_uint32 ret_flags = 0; ++ int r; ++ ++ 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)); ++ ++ kexgssgex_accept_ctx(ssh, &recv_tok, &send_tok, &ret_flags); ++ if (gss->major & GSS_S_CONTINUE_NEEDED) ++ return 0; ++ ++ return kexgssgex_final(ssh, &send_tok, &ret_flags); ++} ++ + #endif /* defined(GSSAPI) && defined(WITH_OPENSSL) */ +diff --color -ruNp a/kex.h b/kex.h +--- a/kex.h 2024-05-16 15:49:43.986410812 +0200 ++++ b/kex.h 2024-06-18 12:19:48.580347469 +0200 +@@ -29,6 +29,10 @@ + #include "mac.h" + #include "crypto_api.h" + ++#ifdef GSSAPI ++# include "ssh-gss.h" /* Gssctxt */ ++#endif ++ + #ifdef WITH_OPENSSL + # include + # include +@@ -177,6 +181,7 @@ struct kex { + int hash_alg; + int ec_nid; + #ifdef GSSAPI ++ Gssctxt *gss; + int gss_deleg_creds; + int gss_trust_dns; + char *gss_host; +diff --color -ruNp a/ssh-gss.h b/ssh-gss.h +--- a/ssh-gss.h 2024-05-16 15:49:43.837407972 +0200 ++++ b/ssh-gss.h 2024-06-27 14:12:48.659866937 +0200 +@@ -88,6 +88,8 @@ extern char **k5users_allowed_cmds; + KEX_GSS_GRP14_SHA1_ID "," \ + KEX_GSS_GEX_SHA1_ID + ++#include "digest.h" /* SSH_DIGEST_MAX_LENGTH */ ++ + typedef struct { + char *filename; + char *envvar; +@@ -127,6 +129,16 @@ typedef struct { + gss_cred_id_t creds; /* server */ + gss_name_t client; /* server */ + gss_cred_id_t client_creds; /* both */ ++ struct sshbuf *shared_secret; /* both */ ++ struct sshbuf *server_pubkey; /* server */ ++ struct sshbuf *server_blob; /* client */ ++ struct sshbuf *server_host_key_blob; /* client */ ++ gss_buffer_desc msg_tok; /* client */ ++ gss_buffer_desc buf; /* both */ ++ u_char hash[SSH_DIGEST_MAX_LENGTH]; /* both */ ++ size_t hashlen; /* both */ ++ int first; /* client */ ++ BIGNUM *dh_client_pub; /* server (gex) */ + } Gssctxt; + + extern ssh_gssapi_mech *supported_mechs[]; diff --git a/openssh-9.6p1-pam-rhost.patch b/openssh-9.6p1-pam-rhost.patch new file mode 100644 index 0000000..b1b0d04 --- /dev/null +++ b/openssh-9.6p1-pam-rhost.patch @@ -0,0 +1,32 @@ +From 26f366e263e575c4e1a18e2e64ba418f58878b37 Mon Sep 17 00:00:00 2001 +From: Daan De Meyer +Date: Mon, 20 Mar 2023 20:22:14 +0100 +Subject: [PATCH] Only set PAM_RHOST if the remote host is not "UNKNOWN" + +When using sshd's -i option with stdio that is not a AF_INET/AF_INET6 +socket, auth_get_canonical_hostname() returns "UNKNOWN" which is then +set as the value of PAM_RHOST, causing pam to try to do a reverse DNS +query of "UNKNOWN", which times out multiple times, causing a +substantial slowdown when logging in. + +To fix this, let's only set PAM_RHOST if the hostname is not "UNKNOWN". +--- + auth-pam.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/auth-pam.c b/auth-pam.c +index e143304e3..39b4e4563 100644 +--- a/auth-pam.c ++++ b/auth-pam.c +@@ -735,7 +735,7 @@ sshpam_init(struct ssh *ssh, Authctxt *authctxt) + sshpam_laddr = get_local_ipaddr( + ssh_packet_get_connection_in(ssh)); + } +- if (sshpam_rhost != NULL) { ++ if (sshpam_rhost != NULL && strcmp(sshpam_rhost, "UNKNOWN") != 0) { + debug("PAM: setting PAM_RHOST to \"%s\"", sshpam_rhost); + sshpam_err = pam_set_item(sshpam_handle, PAM_RHOST, + sshpam_rhost); +-- +2.44.0 + diff --git a/openssh.spec b/openssh.spec index e743f15..aa48028 100644 --- a/openssh.spec +++ b/openssh.spec @@ -38,13 +38,13 @@ # rpm -ba|--rebuild --define "static_openssl 1" %{?static_openssl:%global static_libcrypto 1} -%global openssh_ver 9.6p1 +%global openssh_ver 9.8p1 %global openssh_rel 1 Summary: An open source implementation of SSH protocol version 2 Name: openssh Version: %{openssh_ver} -Release: %{openssh_rel}%{?dist}.5 +Release: %{openssh_rel}%{?dist}.0 URL: http://www.openssh.com/portable.html Source0: ftp://ftp.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-%{version}.tar.gz Source1: ftp://ftp.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-%{version}.tar.gz.asc @@ -63,6 +63,8 @@ Source17: ssh-agent.socket Source19: openssh-server-systemd-sysusers.conf Source20: ssh-host-keys-migration.sh Source21: ssh-host-keys-migration.service +Source22: parallel_test.sh +Source23: parallel_test.Makefile #https://bugzilla.mindrot.org/show_bug.cgi?id=2581 Patch100: openssh-6.7p1-coverity.patch @@ -122,7 +124,8 @@ Patch802: openssh-6.6p1-GSSAPIEnablek5users.patch Patch804: openssh-7.7p1-gssapi-new-unique.patch # Respect k5login_directory option in krk5.conf (#1328243) Patch805: openssh-7.2p2-k5login_directory.patch - +# Rewriting OpenSSH GSS KEX to use new packet API +Patch806: openssh-9.6p1-gsskex-new-api.patch #https://bugzilla.mindrot.org/show_bug.cgi?id=1780 Patch901: openssh-6.6p1-kuserok.patch @@ -142,8 +145,6 @@ Patch926: openssh-6.7p1-sftp-force-permission.patch Patch939: openssh-7.2p2-s390-closefrom.patch # Move MAX_DISPLAYS to a configuration option (#1341302) Patch944: openssh-7.3p1-x11-max-displays.patch -# Help systemd to track the running service -Patch948: openssh-7.4p1-systemd.patch # Pass inetd flags for SELinux down to openbsd compat level Patch949: openssh-7.6p1-cleanup-selinux.patch # Sandbox adjustments for s390 and audit @@ -199,6 +200,8 @@ Patch1012: openssh-9.0p1-evp-fips-dh.patch Patch1013: openssh-9.0p1-evp-fips-ecdh.patch Patch1014: openssh-8.7p1-nohostsha1proof.patch +Patch1015: openssh-9.6p1-pam-rhost.patch + License: BSD-3-Clause AND BSD-2-Clause AND ISC AND SSH-OpenSSH AND ssh-keyscan AND sprintf AND LicenseRef-Fedora-Public-Domain AND X11-distribute-modifications-variant Requires: /sbin/nologin @@ -329,6 +332,7 @@ gpgv2 --quiet --keyring %{SOURCE3} %{SOURCE1} %{SOURCE0} %patch -P 801 -p1 -b .force_krb %patch -P 804 -p1 -b .ccache_name %patch -P 805 -p1 -b .k5login +%patch -P 806 -p1 -b .gsskex-new-api # %patch -P 901 -p1 -b .kuserok %patch -P 906 -p1 -b .fromto-remote @@ -340,7 +344,6 @@ gpgv2 --quiet --keyring %{SOURCE3} %{SOURCE1} %{SOURCE0} %patch -P 926 -p1 -b .sftp-force-mode %patch -P 939 -p1 -b .s390-dev %patch -P 944 -p1 -b .x11max -%patch -P 948 -p1 -b .systemd %patch -P 949 -p1 -b .refactor %patch -P 950 -p1 -b .sandbox %patch -P 951 -p1 -b .pkcs11-uri @@ -372,6 +375,7 @@ gpgv2 --quiet --keyring %{SOURCE3} %{SOURCE1} %{SOURCE0} %patch -P 1012 -p1 -b .evp-fips-dh %patch -P 1013 -p1 -b .evp-fips-ecdh %patch -P 1014 -p1 -b .nosha1hostproof +%patch -P 1015 -p1 -b .pam-rhost %patch -P 100 -p1 -b .coverity @@ -470,10 +474,7 @@ popd %endif %check -#to run tests use "--with check" -%if %{?_with_check:1}%{!?_with_check:0} -make tests -%endif +%{SOURCE22} %{SOURCE23} # ./parallel_tests.sh parallel_tests.Makefile %install rm -rf $RPM_BUILD_ROOT @@ -613,6 +614,7 @@ test -f %{sysconfig_anaconda} && \ %files server %dir %attr(0711,root,root) %{_datadir}/empty.sshd %attr(0755,root,root) %{_sbindir}/sshd +%attr(0755,root,root) %{_libexecdir}/openssh/sshd-session %attr(0755,root,root) %{_libexecdir}/openssh/sftp-server %attr(0755,root,root) %{_libexecdir}/openssh/sshd-keygen %attr(0644,root,root) %{_mandir}/man5/sshd_config.5* @@ -651,6 +653,10 @@ test -f %{sysconfig_anaconda} && \ %attr(0755,root,root) %{_libdir}/sshtest/sk-dummy.so %changelog +* Thu Jul 25 2024 Dmitry Belyavskiy - 9.8p1-1.0 +- Rebase OpenSSH to 9.8p1 + Resolves: RHEL-42635 + * Fri Jul 12 2024 Zoltan Fridrich - 9.6p1-1.5 - Build OpenSSH without ENGINE API Resolves: RHEL-45507 diff --git a/parallel_test.Makefile b/parallel_test.Makefile new file mode 100644 index 0000000..f49df30 --- /dev/null +++ b/parallel_test.Makefile @@ -0,0 +1,14 @@ +# just a Makefile parallel_test.sh uses to run stuff in parallel with make +%: + $(MAKE) -j1 -C .t/$* $* + +t-exec-%: + $(MAKE) -j1 -C ".t/t-exec-$*" \ + TEST_SSH_PORT=10$*0 \ + SKIP_LTESTS="$(shell cat .ltests/not-in/$*)" \ + BUILDDIR="$(shell pwd)/.t/t-exec-$*" \ + TEST_SHELL=sh \ + MAKE=make \ + TEST_SSH_TRACE=yes \ + TEST_SSH_FAIL_FATAL=yes \ + t-exec \ diff --git a/parallel_test.sh b/parallel_test.sh new file mode 100755 index 0000000..682c7e8 --- /dev/null +++ b/parallel_test.sh @@ -0,0 +1,91 @@ +#!/usr/bin/bash +set -uexo pipefail + +# The custom %check script to run the OpenSSH upstream testsuite in parallel. +# +# The upstream testsuite is serial, +# so the idea here is to split the testsuite into several $PARTS: +# * file-tests +# * interop-tests +# * unit +# * ltests-00 +# * ltests-01 +# * ... +# * ltests-23 +# and run them in parallel, using make, each in its own build subtree. + +PARALLEL_MAKEFILE=$1 + +SPLIT=24 +PARTS='file-tests interop-tests unit ' +for ((i = 1; i < SPLIT; i++)); do ii=$(printf %02d $i); + PARTS+="t-exec-$ii " +done + +# work around a selinux restriction: +chcon -t unconfined_exec_t ssh-sk-helper || : + +# work around something else that only crops up in brew +export TEST_SSH_UNSAFE_PERMISSIONS=1 + +# create a .test directory to store all our files in: +mkdir -p .t .ltests/{in,not-in} + +# patch testsuite: use different ports to avoid port collisions +grep -REi 'port=[2-9][0-9]*' regress +sed -i 's|PORT=4242|PORT=$(expr $TEST_SSH_PORT + 1)|' \ + regress/test-exec.sh* +sed -i 's|^P=3301 # test port|P=$(expr $TEST_SSH_PORT + 1)|' \ + regress/multiplex.sh* +sed -i 's|^fwdport=3301|fwdport=$(expr $TEST_SSH_PORT + 1)|' \ + regress/cfgmatch.sh* regress/cfgmatchlisten.sh* +sed -i 's|^LFWD_PORT=.*|LFWD_PORT=$(expr $TEST_SSH_PORT + 1)|' \ + regress/forward-control.sh* +sed -i 's|^RFWD_PORT=.*|RFWD_PORT=$(expr $TEST_SSH_PORT + 2)|' \ + regress/forward-control.sh* +( ! grep -REi 'port=[2-9][0-9]*' regress) # try to find more of those + +# patch testsuite: speed up +sed -i 's|sleep 1$|sleep .25|' regress/forward-control.sh + +# extract LTESTS list to .tests/ltests/all: +grep -Ex 'tests:[[:space:]]*file-tests t-exec interop-tests extra-tests unit' Makefile +echo -ne '\necho-ltests:\n\techo ${LTESTS}' >> regress/Makefile +make -s -C regress echo-ltests | tr ' ' '\n' > .ltests/all + +# separate ltests into $SPLIT roughly equal .tests/ltests/in/$ii parts: +grep -qFx connect .ltests/all +( ! grep -qFx nonex .ltests/all ) +split -d -a2 --number=l/$SPLIT .ltests/all .ltests/in/ +wc -l .ltests/in/* +grep -qFx connect .ltests/in/* + +# generate the inverses of them --- .ltests/not-in/$ii: +( ! grep -qFx nonex .ltests/in/* ) +for ((i = 0; i < SPLIT; i++)); do ii=$(printf %02d $i); + while read -r tname; do + if ! grep -qFx "$tname" ".ltests/in/$ii"; then + echo -n "$tname " >> ".ltests/not-in/$ii" + fi + done < .ltests/all +done +grep . .ltests/not-in/* +( ! grep -q ^connect .ltests/not-in/0 ) +for ((i = 1; i < SPLIT; i++)); do ii=$(printf %02d $i); + grep -q ^connect .ltests/not-in/$ii +done + +# prepare several test directories: +for PART in $PARTS; do + mkdir .t/${PART} + cp -ra * .t/${PART}/ + sed -i "s|abs_top_srcdir=.*|abs_top_srcdir=$(pwd)/.t/${PART}|" \ + .t/${PART}/Makefile + sed -i "s|abs_top_builddir=.*|abs_top_builddir=$(pwd)/.t/${PART}|" \ + .t/${PART}/Makefile + sed -i "s|^BUILDDIR=.*|BUILDDIR=$(pwd)/.t/${PART}|" \ + .t/${PART}/Makefile +done + +# finally, run tests $PARTS in parallel in their own subtrees: +time make -f "$PARALLEL_MAKEFILE" -j$(nproc) $PARTS diff --git a/sources b/sources index 62f22c0..1f1ded8 100644 --- a/sources +++ b/sources @@ -1,4 +1,3 @@ -SHA512 (openssh-9.6p1.tar.gz) = 0ebf81e39914c3a90d7777a001ec7376a94b37e6024baf3e972c58f0982b7ddef942315f5e01d56c00ff95603b4a20ee561ab918ecc55511df007ac138160509 -SHA512 (openssh-9.6p1.tar.gz.asc) = aec5a5bd6ce480a8e5b5879dc55f8186aec90fe61f085aa92ad7d07f324574aa781be09c83b7443a32848d091fd44fb12c1842d49cee77afc351e550ffcc096d -SHA512 (pam_ssh_agent_auth-0.10.4.tar.gz) = caccf72174d15e43f4c86a459ac6448682e62116557cf1e1e828955f3d1731595b238df42adec57860e7f341e92daf5d8285020bcb5018f3b8a5145aa32ee1c2 +SHA512 (openssh-9.8p1.tar.gz) = 95dec2f18e58eb47994f3de4430253e0665e185564b65088ca5f4108870e05feddef8cda8d3c0a4b75f18b98cc2c024df0e27de53b48c1a16da8da483cb8292a +SHA512 (openssh-9.8p1.tar.gz.asc) = 4df1f1be2c6ab7f3aebaedd0a773b0e8c8929abb30cd3415873ad55d012cfa113f792e888e5e772dd468c394aeb7e35d62893a514dbc0ab1a03acd79918657f7 SHA512 (gpgkey-736060BA.gpg) = df44f3fdbcd1d596705348c7f5aed3f738c5f626a55955e0642f7c6c082995cf36a1b1891bb41b8715cb2aff34fef1c877e0eff0d3507dd00a055ba695757a21