Skip to content

Commit ed94164

Browse files
aweitsJ. Bruce Fields
authored andcommitted
nfsd: implement machine credential support for some operations
This addresses the conundrum referenced in RFC5661 18.35.3, and will allow clients to return state to the server using the machine credentials. The biggest part of the problem is that we need to allow the client to send a compound op with integrity/privacy on mounts that don't have it enabled. Add server support for properly decoding and using spo_must_enforce and spo_must_allow bits. Add support for machine credentials to be used for CLOSE, OPEN_DOWNGRADE, LOCKU, DELEGRETURN, and TEST/FREE STATEID. Implement a check so as to not throw WRONGSEC errors when these operations are used if integrity/privacy isn't turned on. Without this, Linux clients with credentials that expired while holding delegations were getting stuck in an endless loop. Signed-off-by: Andrew Elble <[email protected]> Reviewed-by: Jeff Layton <[email protected]> Signed-off-by: J. Bruce Fields <[email protected]>
1 parent dedeb13 commit ed94164

File tree

7 files changed

+99
-28
lines changed

7 files changed

+99
-28
lines changed

fs/nfsd/export.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -954,6 +954,16 @@ __be32 check_nfsd_access(struct svc_export *exp, struct svc_rqst *rqstp)
954954
rqstp->rq_cred.cr_flavor == RPC_AUTH_UNIX)
955955
return 0;
956956
}
957+
958+
/* If the compound op contains a spo_must_allowed op,
959+
* it will be sent with integrity/protection which
960+
* will have to be expressly allowed on mounts that
961+
* don't support it
962+
*/
963+
964+
if (nfsd4_spo_must_allow(rqstp))
965+
return 0;
966+
957967
return nfserr_wrongsec;
958968
}
959969

fs/nfsd/nfs4proc.c

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2335,6 +2335,45 @@ static struct nfsd4_operation nfsd4_ops[] = {
23352335
},
23362336
};
23372337

2338+
/**
2339+
* nfsd4_spo_must_allow - Determine if the compound op contains an
2340+
* operation that is allowed to be sent with machine credentials
2341+
*
2342+
* @rqstp: a pointer to the struct svc_rqst
2343+
*
2344+
* Checks to see if the compound contains a spo_must_allow op
2345+
* and confirms that it was sent with the proper machine creds.
2346+
*/
2347+
2348+
bool nfsd4_spo_must_allow(struct svc_rqst *rqstp)
2349+
{
2350+
struct nfsd4_compoundres *resp = rqstp->rq_resp;
2351+
struct nfsd4_compoundargs *argp = rqstp->rq_argp;
2352+
struct nfsd4_op *this = &argp->ops[resp->opcnt - 1];
2353+
struct nfsd4_compound_state *cstate = &resp->cstate;
2354+
struct nfs4_op_map *allow = &cstate->clp->cl_spo_must_allow;
2355+
u32 opiter;
2356+
2357+
if (!cstate->minorversion)
2358+
return false;
2359+
2360+
if (cstate->spo_must_allowed == true)
2361+
return true;
2362+
2363+
opiter = resp->opcnt;
2364+
while (opiter < argp->opcnt) {
2365+
this = &argp->ops[opiter++];
2366+
if (test_bit(this->opnum, allow->u.longs) &&
2367+
cstate->clp->cl_mach_cred &&
2368+
nfsd4_mach_creds_match(cstate->clp, rqstp)) {
2369+
cstate->spo_must_allowed = true;
2370+
return true;
2371+
}
2372+
}
2373+
cstate->spo_must_allowed = false;
2374+
return false;
2375+
}
2376+
23382377
int nfsd4_max_reply(struct svc_rqst *rqstp, struct nfsd4_op *op)
23392378
{
23402379
struct nfsd4_operation *opdesc;

fs/nfsd/nfs4state.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2388,6 +2388,22 @@ nfsd4_exchange_id(struct svc_rqst *rqstp,
23882388

23892389
switch (exid->spa_how) {
23902390
case SP4_MACH_CRED:
2391+
exid->spo_must_enforce[0] = 0;
2392+
exid->spo_must_enforce[1] = (
2393+
1 << (OP_BIND_CONN_TO_SESSION - 32) |
2394+
1 << (OP_EXCHANGE_ID - 32) |
2395+
1 << (OP_CREATE_SESSION - 32) |
2396+
1 << (OP_DESTROY_SESSION - 32) |
2397+
1 << (OP_DESTROY_CLIENTID - 32));
2398+
2399+
exid->spo_must_allow[0] &= (1 << (OP_CLOSE) |
2400+
1 << (OP_OPEN_DOWNGRADE) |
2401+
1 << (OP_LOCKU) |
2402+
1 << (OP_DELEGRETURN));
2403+
2404+
exid->spo_must_allow[1] &= (
2405+
1 << (OP_TEST_STATEID - 32) |
2406+
1 << (OP_FREE_STATEID - 32));
23912407
if (!svc_rqst_integrity_protected(rqstp)) {
23922408
status = nfserr_inval;
23932409
goto out_nolock;
@@ -2473,6 +2489,8 @@ nfsd4_exchange_id(struct svc_rqst *rqstp,
24732489
goto out;
24742490
}
24752491
new->cl_minorversion = cstate->minorversion;
2492+
new->cl_spo_must_allow.u.words[0] = exid->spo_must_allow[0];
2493+
new->cl_spo_must_allow.u.words[1] = exid->spo_must_allow[1];
24762494

24772495
gen_clid(new, nn);
24782496
add_to_unconfirmed(new);

fs/nfsd/nfs4xdr.c

Lines changed: 23 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1299,16 +1299,14 @@ nfsd4_decode_exchange_id(struct nfsd4_compoundargs *argp,
12991299
break;
13001300
case SP4_MACH_CRED:
13011301
/* spo_must_enforce */
1302-
READ_BUF(4);
1303-
dummy = be32_to_cpup(p++);
1304-
READ_BUF(dummy * 4);
1305-
p += dummy;
1306-
1302+
status = nfsd4_decode_bitmap(argp,
1303+
exid->spo_must_enforce);
1304+
if (status)
1305+
goto out;
13071306
/* spo_must_allow */
1308-
READ_BUF(4);
1309-
dummy = be32_to_cpup(p++);
1310-
READ_BUF(dummy * 4);
1311-
p += dummy;
1307+
status = nfsd4_decode_bitmap(argp, exid->spo_must_allow);
1308+
if (status)
1309+
goto out;
13121310
break;
13131311
case SP4_SSV:
13141312
/* ssp_ops */
@@ -3867,14 +3865,6 @@ nfsd4_encode_write(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_w
38673865
return nfserr;
38683866
}
38693867

3870-
static const u32 nfs4_minimal_spo_must_enforce[2] = {
3871-
[1] = 1 << (OP_BIND_CONN_TO_SESSION - 32) |
3872-
1 << (OP_EXCHANGE_ID - 32) |
3873-
1 << (OP_CREATE_SESSION - 32) |
3874-
1 << (OP_DESTROY_SESSION - 32) |
3875-
1 << (OP_DESTROY_CLIENTID - 32)
3876-
};
3877-
38783868
static __be32
38793869
nfsd4_encode_exchange_id(struct nfsd4_compoundres *resp, __be32 nfserr,
38803870
struct nfsd4_exchange_id *exid)
@@ -3885,6 +3875,7 @@ nfsd4_encode_exchange_id(struct nfsd4_compoundres *resp, __be32 nfserr,
38853875
char *server_scope;
38863876
int major_id_sz;
38873877
int server_scope_sz;
3878+
int status = 0;
38883879
uint64_t minor_id = 0;
38893880

38903881
if (nfserr)
@@ -3913,18 +3904,20 @@ nfsd4_encode_exchange_id(struct nfsd4_compoundres *resp, __be32 nfserr,
39133904
case SP4_NONE:
39143905
break;
39153906
case SP4_MACH_CRED:
3916-
/* spo_must_enforce, spo_must_allow */
3917-
p = xdr_reserve_space(xdr, 16);
3918-
if (!p)
3919-
return nfserr_resource;
3920-
39213907
/* spo_must_enforce bitmap: */
3922-
*p++ = cpu_to_be32(2);
3923-
*p++ = cpu_to_be32(nfs4_minimal_spo_must_enforce[0]);
3924-
*p++ = cpu_to_be32(nfs4_minimal_spo_must_enforce[1]);
3925-
/* empty spo_must_allow bitmap: */
3926-
*p++ = cpu_to_be32(0);
3927-
3908+
status = nfsd4_encode_bitmap(xdr,
3909+
exid->spo_must_enforce[0],
3910+
exid->spo_must_enforce[1],
3911+
exid->spo_must_enforce[2]);
3912+
if (status)
3913+
goto out;
3914+
/* spo_must_allow bitmap: */
3915+
status = nfsd4_encode_bitmap(xdr,
3916+
exid->spo_must_allow[0],
3917+
exid->spo_must_allow[1],
3918+
exid->spo_must_allow[2]);
3919+
if (status)
3920+
goto out;
39283921
break;
39293922
default:
39303923
WARN_ON_ONCE(1);
@@ -3951,6 +3944,8 @@ nfsd4_encode_exchange_id(struct nfsd4_compoundres *resp, __be32 nfserr,
39513944
/* Implementation id */
39523945
*p++ = cpu_to_be32(0); /* zero length nfs_impl_id4 array */
39533946
return 0;
3947+
out:
3948+
return status;
39543949
}
39553950

39563951
static __be32

fs/nfsd/nfsd.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ void nfs4_state_shutdown_net(struct net *net);
124124
void nfs4_reset_lease(time_t leasetime);
125125
int nfs4_reset_recoverydir(char *recdir);
126126
char * nfs4_recoverydir(void);
127+
bool nfsd4_spo_must_allow(struct svc_rqst *rqstp);
127128
#else
128129
static inline int nfsd4_init_slabs(void) { return 0; }
129130
static inline void nfsd4_free_slabs(void) { }
@@ -134,6 +135,10 @@ static inline void nfs4_state_shutdown_net(struct net *net) { }
134135
static inline void nfs4_reset_lease(time_t leasetime) { }
135136
static inline int nfs4_reset_recoverydir(char *recdir) { return 0; }
136137
static inline char * nfs4_recoverydir(void) {return NULL; }
138+
static inline bool nfsd4_spo_must_allow(struct svc_rqst *rqstp)
139+
{
140+
return false;
141+
}
137142
#endif
138143

139144
/*

fs/nfsd/state.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,7 @@ struct nfs4_client {
345345
u32 cl_exchange_flags;
346346
/* number of rpc's in progress over an associated session: */
347347
atomic_t cl_refcount;
348+
struct nfs4_op_map cl_spo_must_allow;
348349

349350
/* for nfs41 callbacks */
350351
/* We currently support a single back channel with a single slot */

fs/nfsd/xdr4.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ struct nfsd4_compound_state {
5959
struct nfsd4_session *session;
6060
struct nfsd4_slot *slot;
6161
int data_offset;
62+
bool spo_must_allowed;
6263
size_t iovlen;
6364
u32 minorversion;
6465
__be32 status;
@@ -403,6 +404,8 @@ struct nfsd4_exchange_id {
403404
clientid_t clientid;
404405
u32 seqid;
405406
int spa_how;
407+
u32 spo_must_enforce[3];
408+
u32 spo_must_allow[3];
406409
};
407410

408411
struct nfsd4_sequence {

0 commit comments

Comments
 (0)