git: a28a65140f11 - main - rpcsec_gss.c: Avoid panics in clnt_nl_call()

From: Rick Macklem <rmacklem_at_FreeBSD.org>
Date: Sat, 19 Jul 2025 14:54:21 UTC
The branch main has been updated by rmacklem:

URL: https://cgit.FreeBSD.org/src/commit/?id=a28a65140f11b54c4555d7c30ef5ae42fdf4c8a0

commit a28a65140f11b54c4555d7c30ef5ae42fdf4c8a0
Author:     Rick Macklem <rmacklem@FreeBSD.org>
AuthorDate: 2025-07-19 14:51:40 +0000
Commit:     Rick Macklem <rmacklem@FreeBSD.org>
CommitDate: 2025-07-19 14:51:40 +0000

    rpcsec_gss.c: Avoid panics in clnt_nl_call()
    
    When rpc_gss_init() calls gssd upcall functions like
    gssd_import_name(), clnt_nl_call() gets calls.
    clnt_nl_call() requires that the currect vnet be set up.
    The calling sequence looks something like:
    rpc_gss_init()->gssd_import_name()->gssd_import_name_1()->
    clnt_call_private()->clnt_nl_call()
    (The call to rpc_gss_init() can happen from various places,
    such as nfs_getauth() and clnt_vc_call().)
    
    rpc_gss_init() does not know what socket is being
    used for NFS client->server RPCs and may be called
    from various places when any VOP_xxx()->RPC call
    is made. As such, the calling thread might be running
    within a jail. However, the gssd upcalls for the NFS
    client must always be done to the gssd running outside
    of any jails.
    
    This patch sets the CURVNET to vnet0 in rpc_gss_init()
    to fix the problem, since that is the vnet that always
    should be used.
    
    This must somehow be changed if NFS mounts in vnet
    jails is enabled.
    
    Reviewed by:    glebius
    Differential Revision:  https://reviews.freebsd.org/D51086
---
 sys/rpc/rpcsec_gss/rpcsec_gss.c | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/sys/rpc/rpcsec_gss/rpcsec_gss.c b/sys/rpc/rpcsec_gss/rpcsec_gss.c
index 62c71937a185..983dd251f81f 100644
--- a/sys/rpc/rpcsec_gss/rpcsec_gss.c
+++ b/sys/rpc/rpcsec_gss/rpcsec_gss.c
@@ -67,6 +67,7 @@
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/hash.h>
+#include <sys/jail.h>
 #include <sys/kernel.h>
 #include <sys/kobj.h>
 #include <sys/lock.h>
@@ -771,6 +772,17 @@ rpc_gss_init(AUTH *auth, rpc_gss_options_ret_t *options_ret)
 	gd->gd_cred.gc_proc = RPCSEC_GSS_INIT;
 	gd->gd_cred.gc_seq = 0;
 
+	/*
+	 * XXX Threads from inside jails can get here via calls
+	 * to clnt_vc_call()->AUTH_REFRESH()->rpc_gss_refresh()
+	 * but the NFS mount is always done outside of the
+	 * jails in vnet0.  Since the thread credentials won't
+	 * necessarily have cr_prison == vnet0 and this function
+	 * has no access to the socket, using vnet0 seems the
+	 * only option.  This is broken if NFS mounts are enabled
+	 * within vnet prisons.
+	 */
+	KGSS_CURVNET_SET_QUIET(vnet0);
 	/*
 	 * For KerberosV, if there is a client principal name, that implies
 	 * that this is a host based initiator credential in the default
@@ -994,12 +1006,14 @@ out:
 			gss_delete_sec_context(&min_stat, &gd->gd_ctx,
 				GSS_C_NO_BUFFER);
 		}
+		KGSS_CURVNET_RESTORE();
 		mtx_lock(&gd->gd_lock);
 		gd->gd_state = RPCSEC_GSS_START;
 		wakeup(gd);
 		mtx_unlock(&gd->gd_lock);
 		return (FALSE);
 	}
+	KGSS_CURVNET_RESTORE();
 	
 	mtx_lock(&gd->gd_lock);
 	gd->gd_state = RPCSEC_GSS_ESTABLISHED;