Backport of:

From d43ba60c91cb323ca921049b7d43b1908c318454 Mon Sep 17 00:00:00 2001
From: "djm@openbsd.org" <djm@openbsd.org>
Date: Mon, 6 Jul 2026 07:44:48 +0000
Subject: [PATCH] upstream: Fix cases in GSSAPI and keyboard-interactive

authentication where the minimum per-attempt delay was not being enforced.

Reported by Orange Cyberdefense Vulnerability Team

OpenBSD-Commit-ID: c40bd35cc2428fcaccad7a141703c28baa6da01e
---
 auth.h        |  3 ++-
 auth2-chall.c |  6 +++++-
 auth2-gss.c   |  9 ++++++++-
 auth2.c       | 12 +++++++++---
 4 files changed, 24 insertions(+), 6 deletions(-)

--- a/auth.h
+++ b/auth.h
@@ -172,6 +172,7 @@ void	auth_log(struct ssh *, int, int, co
 void	auth_maxtries_exceeded(struct ssh *) __attribute__((noreturn));
 void	userauth_finish(struct ssh *, int, const char *, const char *);
 int	auth_root_allowed(struct ssh *, const char *);
+void	auth_failure_delay(Authctxt *, double);
 
 char	*auth2_read_banner(void);
 int	 auth2_methods_valid(const char *, int);
--- a/auth2-chall.c
+++ b/auth2-chall.c
@@ -296,6 +296,7 @@ input_userauth_info_response(int type, u
 	u_int i, nresp;
 	const char *devicename = NULL;
 	char **response = NULL;
+	double tstart = monotime_double();
 
 	if (authctxt == NULL)
 		fatal_f("no authctxt");
@@ -354,6 +355,9 @@ input_userauth_info_response(int type, u
 			auth2_challenge_start(ssh);
 		}
 	}
+
+	if (!authenticated)
+		auth_failure_delay(authctxt, tstart);
 	userauth_finish(ssh, authenticated, "keyboard-interactive",
 	    devicename);
 	return 0;
--- a/auth2-gss.c
+++ b/auth2-gss.c
@@ -282,6 +282,7 @@ input_gssapi_exchange_complete(int type,
 {
 	Authctxt *authctxt = ssh->authctxt;
 	int r, authenticated;
+	double tstart = monotime_double();
 	const char *displayname;
 
 	if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep))
@@ -297,6 +298,8 @@ input_gssapi_exchange_complete(int type,
 
 	authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user,
 	    authctxt->pw, 1));
+	if (!authenticated)
+		auth_failure_delay(authctxt, tstart);
 
 	if ((!use_privsep || mm_is_monitor()) &&
 	    (displayname = ssh_gssapi_displayname()) != NULL)
@@ -322,6 +325,7 @@ input_gssapi_mic(int type, u_int32_t ple
 	const char *displayname;
 	u_char *p;
 	size_t len;
+	double tstart = monotime_double();
 
 	if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep))
 		fatal("No authentication or GSSAPI context");
@@ -354,6 +358,9 @@ input_gssapi_mic(int type, u_int32_t ple
 	    (displayname = ssh_gssapi_displayname()) != NULL)
 		auth2_record_info(authctxt, "%s", displayname);
 
+	if (!authenticated)
+		auth_failure_delay(authctxt, tstart);
+
 	authctxt->postponed = 0;
 	ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
 	ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL);
--- a/auth2.c
+++ b/auth2.c
@@ -346,8 +346,8 @@ input_userauth_request(int type, u_int32
 		authenticated =	m->userauth(ssh, method);
 	}
 	if (!authctxt->authenticated)
-		ensure_minimum_time_since(tstart,
-		    user_specific_delay(authctxt->user));
+		auth_failure_delay(authctxt, tstart);
+
 	userauth_finish(ssh, authenticated, method, NULL);
 	r = 0;
  out:
@@ -646,6 +646,12 @@ auth2_setup_methods_lists(Authctxt *auth
 	return 0;
 }
 
+void
+auth_failure_delay(Authctxt *authctxt, double tstart)
+{
+	ensure_minimum_time_since(tstart, user_specific_delay(authctxt->user));
+}
+
 static int
 list_starts_with(const char *methods, const char *method,
     const char *submethod)
