RFC: add a 'policy' value to GSSAPIDelegateCredentials (OK-AS-DELEGATE)

Avinash Duduskar avinash.duduskar at gmail.com
Tue Sep 15 18:39:17 AEST 2026


Patch below implementing what was discussed in July. yes/no
semantics, the default, and ssh -G output for them are unchanged.
policy requests GSS_C_DELEG_POLICY_FLAG, degrades to requesting no
delegation where the macro is missing at build time, and the client
logs the outcome at verbose level.

The only client readers of gss_deleg_creds are the two
ssh_gssapi_init_ctx() calls and the -K/-k assignments in ssh.c;
the gss-serv.c hit in a tree grep is sshd's own option of the
same name.

Tested: builds with and without GSSAPI and with the macro compiled
out; the option parses and dumps correctly in all three, and ssh -G
output for yes/no is unchanged. I have not exercised it against a
realm with OK-AS-DELEGATE set, so testing from an AD or FreeIPA
environment would be very welcome. Happy to rebase against OpenBSD
-current if that is the preferred landing order.

Avi

-- >8 --
From: Avinash Duduskar <avinash.duduskar at gmail.com>
Date: Tue, 15 Sep 2026 13:17:19 +0530
Subject: [PATCH] Add a 'policy' value to the client GSSAPIDelegateCredentials
 option

GSSAPIDelegateCredentials=yes forwards the user's TGT to any host the
client authenticates to. Kerberos marks services trusted to receive
delegated credentials with the OK-AS-DELEGATE ticket flag (RFC 4120),
which GSS-API exposes through GSS_C_DELEG_POLICY_FLAG (RFC 5896):
delegation is requested only when the service ticket carries the
flag.

Under the new value ssh_gssapi_init_ctx() requests
GSS_C_DELEG_POLICY_FLAG instead of GSS_C_DELEG_FLAG. Where the macro
is missing at build time policy requests no delegation. MIT and
Heimdal both mask request flags to the bits they implement, so a
library that predates RFC 5896 ignores the unknown bit, which is
also no delegation; under policy no build or library combination
delegates unconditionally.

Since non-delegation is a normal outcome under policy, the client
logs at verbose level whether credentials were delegated once the
context establishes. The log runs under policy only, so yes and no
behave exactly as before, including their logging.

The only client readers of gss_deleg_creds are the two
ssh_gssapi_init_ctx() call sites and the -K/-k assignments in ssh.c,
so no boolean use survives that would treat policy as yes. Existing
yes/no semantics, the default, and ssh -G output for them are
unchanged. Client only; sshd and the monitor are untouched.
---
 gss-genr.c   | 15 ++++++++++++++-
 readconf.c   | 14 +++++++++++++-
 ssh-gss.h    |  5 +++++
 ssh_config.5 | 12 ++++++++++++
 4 files changed, 44 insertions(+), 2 deletions(-)

diff --git a/gss-genr.c b/gss-genr.c
index 7088d93b4..a7d0c15ae 100644
--- a/gss-genr.c
+++ b/gss-genr.c
@@ -210,9 +210,18 @@ ssh_gssapi_init_ctx(Gssctxt *ctx, int deleg_creds, gss_buffer_desc *recv_tok,
 {
 	int deleg_flag = 0;
 
-	if (deleg_creds) {
+	if (deleg_creds == SSH_GSS_DELEG_CREDS_YES) {
 		deleg_flag = GSS_C_DELEG_FLAG;
 		debug("Delegating credentials");
+	} else if (deleg_creds == SSH_GSS_DELEG_CREDS_POLICY) {
+#ifdef GSS_C_DELEG_POLICY_FLAG
+		deleg_flag = GSS_C_DELEG_POLICY_FLAG;
+		debug("Delegating credentials if OK-AS-DELEGATE is set");
+#else
+		/* Library predates RFC 5896; request no delegation. */
+		debug("Cannot delegate subject to policy, "
+		    "GSS_C_DELEG_POLICY_FLAG not available");
+#endif
 	}
 
 	ctx->major = gss_init_sec_context(&ctx->minor,
@@ -222,6 +231,10 @@ ssh_gssapi_init_ctx(Gssctxt *ctx, int deleg_creds, gss_buffer_desc *recv_tok,
 
 	if (GSS_ERROR(ctx->major))
 		ssh_gssapi_error(ctx);
+	else if (ctx->major == GSS_S_COMPLETE && flags != NULL &&
+	    deleg_creds == SSH_GSS_DELEG_CREDS_POLICY)
+		verbose("GSSAPI credentials %sdelegated",
+		    (*flags & GSS_C_DELEG_FLAG) ? "" : "not ");
 
 	return (ctx->major);
 }
diff --git a/readconf.c b/readconf.c
index c746ec3f7..3dc8aaa4a 100644
--- a/readconf.c
+++ b/readconf.c
@@ -56,6 +56,7 @@
 #include "mac.h"
 #include "myproposal.h"
 #include "digest.h"
+#include "ssh-gss.h"
 #include "version.h"
 
 /* Format of the configuration file:
@@ -1050,6 +1051,14 @@ static const struct multistate multistate_tunnel[] = {
 	{ "no",				SSH_TUNMODE_NO },
 	{ NULL, -1 }
 };
+static const struct multistate multistate_gssdelegcreds[] = {
+	{ "true",			SSH_GSS_DELEG_CREDS_YES },
+	{ "yes",			SSH_GSS_DELEG_CREDS_YES },
+	{ "false",			SSH_GSS_DELEG_CREDS_NO },
+	{ "no",				SSH_GSS_DELEG_CREDS_NO },
+	{ "policy",			SSH_GSS_DELEG_CREDS_POLICY },
+	{ NULL, -1 }
+};
 static const struct multistate multistate_requesttty[] = {
 	{ "true",			REQUEST_TTY_YES },
 	{ "yes",			REQUEST_TTY_YES },
@@ -1321,7 +1330,8 @@ parse_time:
 
 	case oGssDelegateCreds:
 		intptr = &options->gss_deleg_creds;
-		goto parse_flag;
+		multistate_ptr = multistate_gssdelegcreds;
+		goto parse_multistate;
 
 	case oBatchMode:
 		intptr = &options->batch_mode;
@@ -3593,6 +3603,8 @@ fmt_intarg(OpCodes code, int val)
 	case oVerifyHostKeyDNS:
 	case oUpdateHostkeys:
 		return fmt_multistate_int(val, multistate_yesnoask);
+	case oGssDelegateCreds:
+		return fmt_multistate_int(val, multistate_gssdelegcreds);
 	case oStrictHostKeyChecking:
 		return fmt_multistate_int(val, multistate_strict_hostkey);
 	case oControlMaster:
diff --git a/ssh-gss.h b/ssh-gss.h
index 7b14e74a8..0bea37c01 100644
--- a/ssh-gss.h
+++ b/ssh-gss.h
@@ -26,6 +26,11 @@
 #ifndef _SSH_GSS_H
 #define _SSH_GSS_H
 
+/* Values for the client GSSAPIDelegateCredentials option */
+#define SSH_GSS_DELEG_CREDS_NO		0
+#define SSH_GSS_DELEG_CREDS_YES		1
+#define SSH_GSS_DELEG_CREDS_POLICY	2
+
 #ifdef GSSAPI
 
 #ifdef HAVE_GSSAPI_H
diff --git a/ssh_config.5 b/ssh_config.5
index 3240732e7..b3fa9e81c 100644
--- a/ssh_config.5
+++ b/ssh_config.5
@@ -1016,6 +1016,18 @@ The default is
 .Cm no .
 .It Cm GSSAPIDelegateCredentials
 Forward (delegate) credentials to the server.
+The argument must be
+.Cm yes ,
+.Cm no ,
+or
+.Cm policy .
+If set to
+.Cm policy ,
+credentials are forwarded only when the Kerberos service ticket is
+marked OK-AS-DELEGATE, that is, when the realm marks the host as
+trusted for delegation.
+If the GSSAPI library does not support requesting delegation by
+policy, no credentials are forwarded.
 The default is
 .Cm no .
 .It Cm HashKnownHosts
-- 
2.55.0



More information about the openssh-unix-dev mailing list