aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew Elble <aweits@rit.edu>2016-06-15 12:52:09 -0400
committerJ. Bruce Fields <bfields@redhat.com>2016-07-13 15:32:47 -0400
commited94164398c935a42be7b129a478eb19c598b68a (patch)
tree54e540b879c62b023ff3d0f93ad537209bdf3ff4
parentdedeb13f9efb4439a37cf56317c8f25860dd667b (diff)
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 <aweits@rit.edu> Reviewed-by: Jeff Layton <jlayton@redhat.com> Signed-off-by: J. Bruce Fields <bfields@redhat.com>
-rw-r--r--fs/nfsd/export.c10
-rw-r--r--fs/nfsd/nfs4proc.c39
-rw-r--r--fs/nfsd/nfs4state.c18
-rw-r--r--fs/nfsd/nfs4xdr.c51
-rw-r--r--fs/nfsd/nfsd.h5
-rw-r--r--fs/nfsd/state.h1
-rw-r--r--fs/nfsd/xdr4.h3
7 files changed, 99 insertions, 28 deletions
diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c
index b4d84b579f20..79de2f38dd63 100644
--- a/fs/nfsd/export.c
+++ b/fs/nfsd/export.c
@@ -954,6 +954,16 @@ __be32 check_nfsd_access(struct svc_export *exp, struct svc_rqst *rqstp)
954 rqstp->rq_cred.cr_flavor == RPC_AUTH_UNIX) 954 rqstp->rq_cred.cr_flavor == RPC_AUTH_UNIX)
955 return 0; 955 return 0;
956 } 956 }
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
957 return nfserr_wrongsec; 967 return nfserr_wrongsec;
958} 968}
959 969
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index de1ff1d98bb1..b1159b3e9816 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -2335,6 +2335,45 @@ static struct nfsd4_operation nfsd4_ops[] = {
2335 }, 2335 },
2336}; 2336};
2337 2337
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
2348bool 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
2338int nfsd4_max_reply(struct svc_rqst *rqstp, struct nfsd4_op *op) 2377int nfsd4_max_reply(struct svc_rqst *rqstp, struct nfsd4_op *op)
2339{ 2378{
2340 struct nfsd4_operation *opdesc; 2379 struct nfsd4_operation *opdesc;
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index ef583507d276..ebfcebd5eab1 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -2388,6 +2388,22 @@ nfsd4_exchange_id(struct svc_rqst *rqstp,
2388 2388
2389 switch (exid->spa_how) { 2389 switch (exid->spa_how) {
2390 case SP4_MACH_CRED: 2390 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));
2391 if (!svc_rqst_integrity_protected(rqstp)) { 2407 if (!svc_rqst_integrity_protected(rqstp)) {
2392 status = nfserr_inval; 2408 status = nfserr_inval;
2393 goto out_nolock; 2409 goto out_nolock;
@@ -2473,6 +2489,8 @@ out_new:
2473 goto out; 2489 goto out;
2474 } 2490 }
2475 new->cl_minorversion = cstate->minorversion; 2491 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];
2476 2494
2477 gen_clid(new, nn); 2495 gen_clid(new, nn);
2478 add_to_unconfirmed(new); 2496 add_to_unconfirmed(new);
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 9df898ba648f..84ef94794496 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -1299,16 +1299,14 @@ nfsd4_decode_exchange_id(struct nfsd4_compoundargs *argp,
1299 break; 1299 break;
1300 case SP4_MACH_CRED: 1300 case SP4_MACH_CRED:
1301 /* spo_must_enforce */ 1301 /* spo_must_enforce */
1302 READ_BUF(4); 1302 status = nfsd4_decode_bitmap(argp,
1303 dummy = be32_to_cpup(p++); 1303 exid->spo_must_enforce);
1304 READ_BUF(dummy * 4); 1304 if (status)
1305 p += dummy; 1305 goto out;
1306
1307 /* spo_must_allow */ 1306 /* spo_must_allow */
1308 READ_BUF(4); 1307 status = nfsd4_decode_bitmap(argp, exid->spo_must_allow);
1309 dummy = be32_to_cpup(p++); 1308 if (status)
1310 READ_BUF(dummy * 4); 1309 goto out;
1311 p += dummy;
1312 break; 1310 break;
1313 case SP4_SSV: 1311 case SP4_SSV:
1314 /* ssp_ops */ 1312 /* ssp_ops */
@@ -3867,14 +3865,6 @@ nfsd4_encode_write(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_w
3867 return nfserr; 3865 return nfserr;
3868} 3866}
3869 3867
3870static 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
3878static __be32 3868static __be32
3879nfsd4_encode_exchange_id(struct nfsd4_compoundres *resp, __be32 nfserr, 3869nfsd4_encode_exchange_id(struct nfsd4_compoundres *resp, __be32 nfserr,
3880 struct nfsd4_exchange_id *exid) 3870 struct nfsd4_exchange_id *exid)
@@ -3885,6 +3875,7 @@ nfsd4_encode_exchange_id(struct nfsd4_compoundres *resp, __be32 nfserr,
3885 char *server_scope; 3875 char *server_scope;
3886 int major_id_sz; 3876 int major_id_sz;
3887 int server_scope_sz; 3877 int server_scope_sz;
3878 int status = 0;
3888 uint64_t minor_id = 0; 3879 uint64_t minor_id = 0;
3889 3880
3890 if (nfserr) 3881 if (nfserr)
@@ -3913,18 +3904,20 @@ nfsd4_encode_exchange_id(struct nfsd4_compoundres *resp, __be32 nfserr,
3913 case SP4_NONE: 3904 case SP4_NONE:
3914 break; 3905 break;
3915 case SP4_MACH_CRED: 3906 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
3921 /* spo_must_enforce bitmap: */ 3907 /* spo_must_enforce bitmap: */
3922 *p++ = cpu_to_be32(2); 3908 status = nfsd4_encode_bitmap(xdr,
3923 *p++ = cpu_to_be32(nfs4_minimal_spo_must_enforce[0]); 3909 exid->spo_must_enforce[0],
3924 *p++ = cpu_to_be32(nfs4_minimal_spo_must_enforce[1]); 3910 exid->spo_must_enforce[1],
3925 /* empty spo_must_allow bitmap: */ 3911 exid->spo_must_enforce[2]);
3926 *p++ = cpu_to_be32(0); 3912 if (status)
3927 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;
3928 break; 3921 break;
3929 default: 3922 default:
3930 WARN_ON_ONCE(1); 3923 WARN_ON_ONCE(1);
@@ -3951,6 +3944,8 @@ nfsd4_encode_exchange_id(struct nfsd4_compoundres *resp, __be32 nfserr,
3951 /* Implementation id */ 3944 /* Implementation id */
3952 *p++ = cpu_to_be32(0); /* zero length nfs_impl_id4 array */ 3945 *p++ = cpu_to_be32(0); /* zero length nfs_impl_id4 array */
3953 return 0; 3946 return 0;
3947out:
3948 return status;
3954} 3949}
3955 3950
3956static __be32 3951static __be32
diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h
index cf980523898b..9446849888d5 100644
--- a/fs/nfsd/nfsd.h
+++ b/fs/nfsd/nfsd.h
@@ -124,6 +124,7 @@ void nfs4_state_shutdown_net(struct net *net);
124void nfs4_reset_lease(time_t leasetime); 124void nfs4_reset_lease(time_t leasetime);
125int nfs4_reset_recoverydir(char *recdir); 125int nfs4_reset_recoverydir(char *recdir);
126char * nfs4_recoverydir(void); 126char * nfs4_recoverydir(void);
127bool nfsd4_spo_must_allow(struct svc_rqst *rqstp);
127#else 128#else
128static inline int nfsd4_init_slabs(void) { return 0; } 129static inline int nfsd4_init_slabs(void) { return 0; }
129static inline void nfsd4_free_slabs(void) { } 130static inline void nfsd4_free_slabs(void) { }
@@ -134,6 +135,10 @@ static inline void nfs4_state_shutdown_net(struct net *net) { }
134static inline void nfs4_reset_lease(time_t leasetime) { } 135static inline void nfs4_reset_lease(time_t leasetime) { }
135static inline int nfs4_reset_recoverydir(char *recdir) { return 0; } 136static inline int nfs4_reset_recoverydir(char *recdir) { return 0; }
136static inline char * nfs4_recoverydir(void) {return NULL; } 137static inline char * nfs4_recoverydir(void) {return NULL; }
138static inline bool nfsd4_spo_must_allow(struct svc_rqst *rqstp)
139{
140 return false;
141}
137#endif 142#endif
138 143
139/* 144/*
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 64053eadeb81..b95adf9a1595 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -345,6 +345,7 @@ struct nfs4_client {
345 u32 cl_exchange_flags; 345 u32 cl_exchange_flags;
346 /* number of rpc's in progress over an associated session: */ 346 /* number of rpc's in progress over an associated session: */
347 atomic_t cl_refcount; 347 atomic_t cl_refcount;
348 struct nfs4_op_map cl_spo_must_allow;
348 349
349 /* for nfs41 callbacks */ 350 /* for nfs41 callbacks */
350 /* We currently support a single back channel with a single slot */ 351 /* We currently support a single back channel with a single slot */
diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
index 74342a7c208a..beea0c5edc51 100644
--- a/fs/nfsd/xdr4.h
+++ b/fs/nfsd/xdr4.h
@@ -59,6 +59,7 @@ struct nfsd4_compound_state {
59 struct nfsd4_session *session; 59 struct nfsd4_session *session;
60 struct nfsd4_slot *slot; 60 struct nfsd4_slot *slot;
61 int data_offset; 61 int data_offset;
62 bool spo_must_allowed;
62 size_t iovlen; 63 size_t iovlen;
63 u32 minorversion; 64 u32 minorversion;
64 __be32 status; 65 __be32 status;
@@ -403,6 +404,8 @@ struct nfsd4_exchange_id {
403 clientid_t clientid; 404 clientid_t clientid;
404 u32 seqid; 405 u32 seqid;
405 int spa_how; 406 int spa_how;
407 u32 spo_must_enforce[3];
408 u32 spo_must_allow[3];
406}; 409};
407 410
408struct nfsd4_sequence { 411struct nfsd4_sequence {