Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

nfsd: decouple the xprtsec policy check from check_nfsd_access()

A while back I had reported that an NFSv3 client could successfully
mount using '-o xprtsec=none' an export that had been exported with
'xprtsec=tls:mtls'. By "successfully" I mean that the mount command
would succeed and the mount would show up in /proc/mount. Attempting
to do anything futher with the mount would be met with NFS3ERR_ACCES.

This was fixed (albeit accidentally) by commit bb4f07f2409c ("nfsd:
Fix NFSD_MAY_BYPASS_GSS and NFSD_MAY_BYPASS_GSS_ON_ROOT") and was
subsequently re-broken by commit 0813c5f01249 ("nfsd: fix access
checking for NLM under XPRTSEC policies").

Transport Layer Security isn't an RPC security flavor or pseudo-flavor,
so we shouldn't be conflating them when determining whether the access
checks can be bypassed. Split check_nfsd_access() into two helpers, and
have __fh_verify() call the helpers directly since __fh_verify() has
logic that allows one or both of the checks to be skipped. All other
sites will continue to call check_nfsd_access().

Link: https://lore.kernel.org/linux-nfs/ZjO3Qwf_G87yNXb2@aion/
Fixes: 9280c5774314 ("NFSD: Handle new xprtsec= export option")
Cc: stable@vger.kernel.org
Signed-off-by: Scott Mayhew <smayhew@redhat.com>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>

authored by

Scott Mayhew and committed by
Chuck Lever
e4f574ca ab1c282c

+83 -26
+57 -25
fs/nfsd/export.c
··· 1082 1082 } 1083 1083 1084 1084 /** 1085 - * check_nfsd_access - check if access to export is allowed. 1085 + * check_xprtsec_policy - check if access to export is allowed by the 1086 + * xprtsec policy 1086 1087 * @exp: svc_export that is being accessed. 1087 - * @rqstp: svc_rqst attempting to access @exp (will be NULL for LOCALIO). 1088 - * @may_bypass_gss: reduce strictness of authorization check 1088 + * @rqstp: svc_rqst attempting to access @exp. 1089 + * 1090 + * Helper function for check_nfsd_access(). Note that callers should be 1091 + * using check_nfsd_access() instead of calling this function directly. The 1092 + * one exception is __fh_verify() since it has logic that may result in one 1093 + * or both of the helpers being skipped. 1089 1094 * 1090 1095 * Return values: 1091 1096 * %nfs_ok if access is granted, or 1092 1097 * %nfserr_wrongsec if access is denied 1093 1098 */ 1094 - __be32 check_nfsd_access(struct svc_export *exp, struct svc_rqst *rqstp, 1095 - bool may_bypass_gss) 1099 + __be32 check_xprtsec_policy(struct svc_export *exp, struct svc_rqst *rqstp) 1096 1100 { 1097 - struct exp_flavor_info *f, *end = exp->ex_flavors + exp->ex_nflavors; 1098 - struct svc_xprt *xprt; 1099 - 1100 - /* 1101 - * If rqstp is NULL, this is a LOCALIO request which will only 1102 - * ever use a filehandle/credential pair for which access has 1103 - * been affirmed (by ACCESS or OPEN NFS requests) over the 1104 - * wire. So there is no need for further checks here. 1105 - */ 1106 - if (!rqstp) 1107 - return nfs_ok; 1108 - 1109 - xprt = rqstp->rq_xprt; 1101 + struct svc_xprt *xprt = rqstp->rq_xprt; 1110 1102 1111 1103 if (exp->ex_xprtsec_modes & NFSEXP_XPRTSEC_NONE) { 1112 1104 if (!test_bit(XPT_TLS_SESSION, &xprt->xpt_flags)) 1113 - goto ok; 1105 + return nfs_ok; 1114 1106 } 1115 1107 if (exp->ex_xprtsec_modes & NFSEXP_XPRTSEC_TLS) { 1116 1108 if (test_bit(XPT_TLS_SESSION, &xprt->xpt_flags) && 1117 1109 !test_bit(XPT_PEER_AUTH, &xprt->xpt_flags)) 1118 - goto ok; 1110 + return nfs_ok; 1119 1111 } 1120 1112 if (exp->ex_xprtsec_modes & NFSEXP_XPRTSEC_MTLS) { 1121 1113 if (test_bit(XPT_TLS_SESSION, &xprt->xpt_flags) && 1122 1114 test_bit(XPT_PEER_AUTH, &xprt->xpt_flags)) 1123 - goto ok; 1115 + return nfs_ok; 1124 1116 } 1125 - if (!may_bypass_gss) 1126 - goto denied; 1117 + return nfserr_wrongsec; 1118 + } 1127 1119 1128 - ok: 1120 + /** 1121 + * check_security_flavor - check if access to export is allowed by the 1122 + * security flavor 1123 + * @exp: svc_export that is being accessed. 1124 + * @rqstp: svc_rqst attempting to access @exp. 1125 + * @may_bypass_gss: reduce strictness of authorization check 1126 + * 1127 + * Helper function for check_nfsd_access(). Note that callers should be 1128 + * using check_nfsd_access() instead of calling this function directly. The 1129 + * one exception is __fh_verify() since it has logic that may result in one 1130 + * or both of the helpers being skipped. 1131 + * 1132 + * Return values: 1133 + * %nfs_ok if access is granted, or 1134 + * %nfserr_wrongsec if access is denied 1135 + */ 1136 + __be32 check_security_flavor(struct svc_export *exp, struct svc_rqst *rqstp, 1137 + bool may_bypass_gss) 1138 + { 1139 + struct exp_flavor_info *f, *end = exp->ex_flavors + exp->ex_nflavors; 1140 + 1129 1141 /* legacy gss-only clients are always OK: */ 1130 1142 if (exp->ex_client == rqstp->rq_gssclient) 1131 1143 return nfs_ok; ··· 1179 1167 } 1180 1168 } 1181 1169 1182 - denied: 1183 1170 return nfserr_wrongsec; 1171 + } 1172 + 1173 + /** 1174 + * check_nfsd_access - check if access to export is allowed. 1175 + * @exp: svc_export that is being accessed. 1176 + * @rqstp: svc_rqst attempting to access @exp. 1177 + * @may_bypass_gss: reduce strictness of authorization check 1178 + * 1179 + * Return values: 1180 + * %nfs_ok if access is granted, or 1181 + * %nfserr_wrongsec if access is denied 1182 + */ 1183 + __be32 check_nfsd_access(struct svc_export *exp, struct svc_rqst *rqstp, 1184 + bool may_bypass_gss) 1185 + { 1186 + __be32 status; 1187 + 1188 + status = check_xprtsec_policy(exp, rqstp); 1189 + if (status != nfs_ok) 1190 + return status; 1191 + return check_security_flavor(exp, rqstp, may_bypass_gss); 1184 1192 } 1185 1193 1186 1194 /*
+3
fs/nfsd/export.h
··· 101 101 102 102 struct svc_cred; 103 103 int nfsexp_flags(struct svc_cred *cred, struct svc_export *exp); 104 + __be32 check_xprtsec_policy(struct svc_export *exp, struct svc_rqst *rqstp); 105 + __be32 check_security_flavor(struct svc_export *exp, struct svc_rqst *rqstp, 106 + bool may_bypass_gss); 104 107 __be32 check_nfsd_access(struct svc_export *exp, struct svc_rqst *rqstp, 105 108 bool may_bypass_gss); 106 109
+23 -1
fs/nfsd/nfsfh.c
··· 364 364 if (error) 365 365 goto out; 366 366 367 + /* 368 + * If rqstp is NULL, this is a LOCALIO request which will only 369 + * ever use a filehandle/credential pair for which access has 370 + * been affirmed (by ACCESS or OPEN NFS requests) over the 371 + * wire. Skip both the xprtsec policy and the security flavor 372 + * checks. 373 + */ 374 + if (!rqstp) 375 + goto check_permissions; 376 + 367 377 if ((access & NFSD_MAY_NLM) && (exp->ex_flags & NFSEXP_NOAUTHNLM)) 368 378 /* NLM is allowed to fully bypass authentication */ 369 379 goto out; 380 + 381 + /* 382 + * NLM is allowed to bypass the xprtsec policy check because lockd 383 + * doesn't support xprtsec. 384 + */ 385 + if (!(access & NFSD_MAY_NLM)) { 386 + error = check_xprtsec_policy(exp, rqstp); 387 + if (error) 388 + goto out; 389 + } 370 390 371 391 if (access & NFSD_MAY_BYPASS_GSS) 372 392 may_bypass_gss = true; ··· 399 379 && exp->ex_path.dentry == dentry) 400 380 may_bypass_gss = true; 401 381 402 - error = check_nfsd_access(exp, rqstp, may_bypass_gss); 382 + error = check_security_flavor(exp, rqstp, may_bypass_gss); 403 383 if (error) 404 384 goto out; 385 + 405 386 /* During LOCALIO call to fh_verify will be called with a NULL rqstp */ 406 387 if (rqstp) 407 388 svc_xprt_set_valid(rqstp->rq_xprt); 408 389 390 + check_permissions: 409 391 /* Finally, check access permissions. */ 410 392 error = nfsd_permission(cred, exp, dentry, access); 411 393 out: