From: "J. Bruce Fields" <bfields@fieldses.org>
To: Andrew Elble <aweits@rit.edu>
Cc: linux-nfs@vger.kernel.org, dros@primarydata.com
Subject: Re: [PATCH v2 3/3] nfsd: implement machine credential support for some operations
Date: Thu, 21 Jan 2016 11:07:31 -0500 [thread overview]
Message-ID: <20160121160731.GA1793@fieldses.org> (raw)
In-Reply-To: <20160120225325.GB26860@fieldses.org>
On Wed, Jan 20, 2016 at 05:53:25PM -0500, J. Bruce Fields wrote:
> On Mon, Jan 18, 2016 at 03:08:22PM -0500, Andrew Elble wrote:
> > 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.
>
> I'm OK with supporting MACH_CRED on these additional operation, but
> could you explain why it's necessary?
>
> Rereading the spec.... Is it that you're hitting the "conundrum"
> described in https://tools.ietf.org/html/rfc5661#page-504 ? I guess
> that would explain the connection to KEYEXPIRED as well, OK. I think
> it'd be worth an explanation in the changelog and maybe a comment in the
> code referencing that bit of the spec.
>
> I'm a little concerned that we're bypassing file permissions
> completely--can any rogue client unlock another client's locks or return
> their delegations regardless of any file or other permissions? (Looks
> like that may be a preexisting problem, to some degree; e.g. does
> nfsd4_locku() need more checks?)
Thinking about the LOCKU case some more: checking file permissions could
risk leaving us with unlockable locks if permissions change. User
permissions may be tough to use in general--locally it's OK to lock and
unlock as a different user, isn't it? So nfsd4_locku() may be right
just to give up and check nothing. With MACH_CRED we can at least check
that the client has the right to use the given lockowner. (Could we add
an nfsd4_match_creds() check to nfsd4_locku()?)
--b.
> > Signed-off-by: Andrew Elble <aweits@rit.edu>
> > ---
> > fs/nfsd/export.c | 4 ++++
> > fs/nfsd/nfs4proc.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++
> > fs/nfsd/nfs4state.c | 18 ++++++++++++++
> > fs/nfsd/nfs4xdr.c | 51 ++++++++++++++++++---------------------
> > fs/nfsd/nfsd.h | 5 ++++
> > fs/nfsd/state.h | 1 +
> > fs/nfsd/xdr4.h | 3 +++
> > 7 files changed, 123 insertions(+), 28 deletions(-)
> >
> > diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c
> > index b4d84b579f20..0395e3e8fc3e 100644
> > --- a/fs/nfsd/export.c
> > +++ b/fs/nfsd/export.c
> > @@ -954,6 +954,10 @@ __be32 check_nfsd_access(struct svc_export *exp, struct svc_rqst *rqstp)
> > rqstp->rq_cred.cr_flavor == RPC_AUTH_UNIX)
> > return 0;
> > }
> > +
> > + if (nfsd4_spo_must_allow(rqstp))
> > + return 0;
> > +
> > return nfserr_wrongsec;
> > }
> >
> > diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
> > index a9f096c7e99f..047d6662010b 100644
> > --- a/fs/nfsd/nfs4proc.c
> > +++ b/fs/nfsd/nfs4proc.c
> > @@ -2285,6 +2285,75 @@ static struct nfsd4_operation nfsd4_ops[] = {
> > },
> > };
> >
> > +/**
> > + * nfsd4_spo_must_allow - Determine if the compound op contains an
> > + * operation that is allowed to be sent with machine credentials
> > + *
> > + * @rqstp: a pointer to the struct svc_rqst
> > + *
> > + * nfsd4_spo_must_allow() allows check_nfsd_access() to succeed
> > + * when the operation and/or the FH+operation(s) is part of what the
> > + * client negotiated to be able to send with machine credentials.
> > + * We keep some state so that FH+operation(s) can succeed despite
> > + * check_nfsd_access() being called from fh_verify() as well as
> > + * nfsd4_proc_compound().
> > + */
> > +
> > +bool nfsd4_spo_must_allow(struct svc_rqst *rqstp)
> > +{
> > + struct nfsd4_compoundres *resp = rqstp->rq_resp;
> > + struct nfsd4_compoundargs *argp = rqstp->rq_argp;
> > + struct nfsd4_op *this = &argp->ops[resp->opcnt - 1];
> > + struct nfsd4_compound_state *cstate = &resp->cstate;
> > + struct nfsd4_operation *thisd;
> > + struct nfs4_op_map *allow = &cstate->clp->cl_spo_must_allow;
> > + u32 opiter;
> > +
> > + if (!cstate->minorversion)
> > + return false;
> > +
> > + thisd = OPDESC(this);
> > +
> > + if (!(thisd->op_flags & OP_IS_PUTFH_LIKE)) {
> > + if (cstate->spo_must_allowed == true)
> > + /*
> > + * a prior putfh + op has set
> > + * spo_must_allow conditions
> > + */
> > + return true;
> > + /* evaluate op against spo_must_allow with no prior putfh */
> > + if (test_bit(this->opnum, allow->u.longs) &&
> > + cstate->clp->cl_mach_cred &&
> > + nfsd4_mach_creds_match(cstate->clp, rqstp))
> > + return true;
> > + else
> > + return false;
> > + }
> > + /*
> > + * this->opnum has PUTFH ramifications
> > + * scan forward to next putfh or end of compound op
> > + */
> > + opiter = resp->opcnt;
> > + while (opiter < argp->opcnt) {
> > + this = &argp->ops[opiter++];
> > + thisd = OPDESC(this);
> > + if (thisd->op_flags & OP_IS_PUTFH_LIKE)
> > + break;
> > + if (test_bit(this->opnum, allow->u.longs) &&
> > + cstate->clp->cl_mach_cred &&
> > + nfsd4_mach_creds_match(cstate->clp, rqstp)) {
> > + /*
> > + * the op covered by the fh is a
> > + * spo_must_allow operation
> > + */
> > + cstate->spo_must_allowed = true;
> > + return true;
> > + }
> > + }
> > + cstate->spo_must_allowed = false;
> > + return false;
> > +}
> > +
> > int nfsd4_max_reply(struct svc_rqst *rqstp, struct nfsd4_op *op)
> > {
> > struct nfsd4_operation *opdesc;
> > diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
> > index 65efc900e97e..b28805519725 100644
> > --- a/fs/nfsd/nfs4state.c
> > +++ b/fs/nfsd/nfs4state.c
> > @@ -2367,6 +2367,22 @@ nfsd4_exchange_id(struct svc_rqst *rqstp,
> >
> > switch (exid->spa_how) {
> > case SP4_MACH_CRED:
> > + exid->spo_must_enforce[0] = 0;
> > + exid->spo_must_enforce[1] = (
> > + 1 << (OP_BIND_CONN_TO_SESSION - 32) |
> > + 1 << (OP_EXCHANGE_ID - 32) |
> > + 1 << (OP_CREATE_SESSION - 32) |
> > + 1 << (OP_DESTROY_SESSION - 32) |
> > + 1 << (OP_DESTROY_CLIENTID - 32));
> > +
> > + exid->spo_must_allow[0] &= (1 << (OP_CLOSE) |
> > + 1 << (OP_OPEN_DOWNGRADE) |
> > + 1 << (OP_LOCKU) |
> > + 1 << (OP_DELEGRETURN));
> > +
> > + exid->spo_must_allow[1] &= (
> > + 1 << (OP_TEST_STATEID - 32) |
> > + 1 << (OP_FREE_STATEID - 32));
> > if (!svc_rqst_integrity_protected(rqstp))
> > return nfserr_inval;
> > case SP4_NONE:
> > @@ -2443,6 +2459,8 @@ out_new:
> > }
> > new->cl_minorversion = cstate->minorversion;
> > new->cl_mach_cred = (exid->spa_how == SP4_MACH_CRED);
> > + new->cl_spo_must_allow.u.words[0] = exid->spo_must_allow[0];
> > + new->cl_spo_must_allow.u.words[1] = exid->spo_must_allow[1];
> >
> > gen_clid(new, nn);
> > add_to_unconfirmed(new);
> > diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
> > index 51c9e9ca39a4..e2043aa95e27 100644
> > --- a/fs/nfsd/nfs4xdr.c
> > +++ b/fs/nfsd/nfs4xdr.c
> > @@ -1297,16 +1297,14 @@ nfsd4_decode_exchange_id(struct nfsd4_compoundargs *argp,
> > break;
> > case SP4_MACH_CRED:
> > /* spo_must_enforce */
> > - READ_BUF(4);
> > - dummy = be32_to_cpup(p++);
> > - READ_BUF(dummy * 4);
> > - p += dummy;
> > -
> > + status = nfsd4_decode_bitmap(argp,
> > + exid->spo_must_enforce);
> > + if (status)
> > + goto out;
> > /* spo_must_allow */
> > - READ_BUF(4);
> > - dummy = be32_to_cpup(p++);
> > - READ_BUF(dummy * 4);
> > - p += dummy;
> > + status = nfsd4_decode_bitmap(argp, exid->spo_must_allow);
> > + if (status)
> > + goto out;
> > break;
> > case SP4_SSV:
> > /* ssp_ops */
> > @@ -3841,14 +3839,6 @@ nfsd4_encode_write(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_w
> > return nfserr;
> > }
> >
> > -static const u32 nfs4_minimal_spo_must_enforce[2] = {
> > - [1] = 1 << (OP_BIND_CONN_TO_SESSION - 32) |
> > - 1 << (OP_EXCHANGE_ID - 32) |
> > - 1 << (OP_CREATE_SESSION - 32) |
> > - 1 << (OP_DESTROY_SESSION - 32) |
> > - 1 << (OP_DESTROY_CLIENTID - 32)
> > -};
> > -
> > static __be32
> > nfsd4_encode_exchange_id(struct nfsd4_compoundres *resp, __be32 nfserr,
> > struct nfsd4_exchange_id *exid)
> > @@ -3859,6 +3849,7 @@ nfsd4_encode_exchange_id(struct nfsd4_compoundres *resp, __be32 nfserr,
> > char *server_scope;
> > int major_id_sz;
> > int server_scope_sz;
> > + int status = 0;
> > uint64_t minor_id = 0;
> >
> > if (nfserr)
> > @@ -3887,18 +3878,20 @@ nfsd4_encode_exchange_id(struct nfsd4_compoundres *resp, __be32 nfserr,
> > case SP4_NONE:
> > break;
> > case SP4_MACH_CRED:
> > - /* spo_must_enforce, spo_must_allow */
> > - p = xdr_reserve_space(xdr, 16);
> > - if (!p)
> > - return nfserr_resource;
> > -
> > /* spo_must_enforce bitmap: */
> > - *p++ = cpu_to_be32(2);
> > - *p++ = cpu_to_be32(nfs4_minimal_spo_must_enforce[0]);
> > - *p++ = cpu_to_be32(nfs4_minimal_spo_must_enforce[1]);
> > - /* empty spo_must_allow bitmap: */
> > - *p++ = cpu_to_be32(0);
> > -
> > + status = nfsd4_encode_bitmap(xdr,
> > + exid->spo_must_enforce[0],
> > + exid->spo_must_enforce[1],
> > + exid->spo_must_enforce[2]);
> > + if (status)
> > + goto out;
> > + /* spo_must_allow bitmap: */
> > + status = nfsd4_encode_bitmap(xdr,
> > + exid->spo_must_allow[0],
> > + exid->spo_must_allow[1],
> > + exid->spo_must_allow[2]);
> > + if (status)
> > + goto out;
> > break;
> > default:
> > WARN_ON_ONCE(1);
> > @@ -3925,6 +3918,8 @@ nfsd4_encode_exchange_id(struct nfsd4_compoundres *resp, __be32 nfserr,
> > /* Implementation id */
> > *p++ = cpu_to_be32(0); /* zero length nfs_impl_id4 array */
> > return 0;
> > +out:
> > + return status;
> > }
> >
> > static __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);
> > void nfs4_reset_lease(time_t leasetime);
> > int nfs4_reset_recoverydir(char *recdir);
> > char * nfs4_recoverydir(void);
> > +bool nfsd4_spo_must_allow(struct svc_rqst *rqstp);
> > #else
> > static inline int nfsd4_init_slabs(void) { return 0; }
> > static inline void nfsd4_free_slabs(void) { }
> > @@ -134,6 +135,10 @@ static inline void nfs4_state_shutdown_net(struct net *net) { }
> > static inline void nfs4_reset_lease(time_t leasetime) { }
> > static inline int nfs4_reset_recoverydir(char *recdir) { return 0; }
> > static inline char * nfs4_recoverydir(void) {return NULL; }
> > +static inline bool nfsd4_spo_must_allow(struct svc_rqst *rqstp)
> > +{
> > + return false;
> > +}
> > #endif
> >
> > /*
> > diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
> > index 77fdf4de91ba..2b59c74f098c 100644
> > --- a/fs/nfsd/state.h
> > +++ b/fs/nfsd/state.h
> > @@ -345,6 +345,7 @@ struct nfs4_client {
> > u32 cl_exchange_flags;
> > /* number of rpc's in progress over an associated session: */
> > atomic_t cl_refcount;
> > + struct nfs4_op_map cl_spo_must_allow;
> >
> > /* for nfs41 callbacks */
> > /* We currently support a single back channel with a single slot */
> > diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
> > index 25c9c79460f9..c88aca9c42d7 100644
> > --- a/fs/nfsd/xdr4.h
> > +++ b/fs/nfsd/xdr4.h
> > @@ -59,6 +59,7 @@ struct nfsd4_compound_state {
> > struct nfsd4_session *session;
> > struct nfsd4_slot *slot;
> > int data_offset;
> > + bool spo_must_allowed;
> > size_t iovlen;
> > u32 minorversion;
> > __be32 status;
> > @@ -403,6 +404,8 @@ struct nfsd4_exchange_id {
> > clientid_t clientid;
> > u32 seqid;
> > int spa_how;
> > + u32 spo_must_enforce[3];
> > + u32 spo_must_allow[3];
> > };
> >
> > struct nfsd4_sequence {
> > --
> > 2.6.3
next prev parent reply other threads:[~2016-01-21 16:07 UTC|newest]
Thread overview: 17+ messages / expand[flat|nested] mbox.gz Atom feed top
2016-01-18 20:08 [PATCH v2 0/3] Deal with lost delegations and EKEYEXPIRED Andrew Elble
2016-01-18 20:08 ` [PATCH v2 1/3] nfs/nfsd: Move useful bitfield ops to a commonly accessible place Andrew Elble
2016-01-18 20:08 ` [PATCH v2 2/3] nfsd: allow mach_creds_match to be used more broadly Andrew Elble
2016-01-18 20:08 ` [PATCH v2 3/3] nfsd: implement machine credential support for some operations Andrew Elble
2016-01-20 22:53 ` J. Bruce Fields
2016-01-21 16:07 ` J. Bruce Fields [this message]
2016-01-21 19:01 ` J. Bruce Fields
2016-01-21 19:30 ` Andrew W Elble
2016-01-21 19:50 ` J. Bruce Fields
2016-01-22 0:01 ` Andrew W Elble
2016-01-22 15:24 ` J. Bruce Fields
2016-01-22 16:06 ` Trond Myklebust
2016-01-22 15:40 ` J. Bruce Fields
2016-01-22 16:09 ` Andrew W Elble
2016-01-22 16:36 ` J. Bruce Fields
2016-01-20 22:34 ` [PATCH v2 0/3] Deal with lost delegations and EKEYEXPIRED J. Bruce Fields
-- strict thread matches above, loose matches on Subject: below --
2016-01-05 18:55 Andrew Elble
2016-01-05 18:55 ` [PATCH v2 3/3] nfsd: implement machine credential support for some operations Andrew Elble
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20160121160731.GA1793@fieldses.org \
--to=bfields@fieldses.org \
--cc=aweits@rit.edu \
--cc=dros@primarydata.com \
--cc=linux-nfs@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).