[pnfs] [PATCH 06/18] implementation-of-client-EXCHANGE_ID-operation
Olga Kornievskaia
aglo at citi.umich.edu
Sun Mar 1 22:25:04 EST 2009
This patch implements SSV portion of EXCHANGE_ID operation. For now we
hard-code the use of SSV as a state protection mechanism. In the future,
this should be specified by the mount (?). As per recommentations in the
nfs41 spec, we encode that OP_DELEGPURGE, OP_EXCHANGE_ID,
OP_CREATE_SESSION, OP_DESTROY_SESSION, OP_BIND_CONN_TO_SESSION,
and OP_DESTROY_CLIENTID operations should be included in the "must_enforce"
list and "OP_CLOSE" in "must_allow" list. Currentnly, OP_DELEGPURGE and
OP_BIND_CONN_TO_SESSION are not implemented. OP_EXCHANGE_ID and
OP_CREATE_SESSION are done once and SSV cannot be used for it. It leaves
OP_DESTROY_CLIENTID as the only implementation to be enforced which is
implemented in these patches (0013).
Signed-off-by: Olga Kornievskaia <aglo at citi.umich.edu>
---
fs/nfs/client.c | 16 ++++++
fs/nfs/nfs4proc.c | 125 +++++++++++++++++++++++++++++++++++++++++++++++++-
fs/nfs/nfs4xdr.c | 132 ++++++++++++++++++++++++++++++++++++++++++++++++++---
3 files changed, 265 insertions(+), 8 deletions(-)
diff --git a/fs/nfs/client.c b/fs/nfs/client.c
index 206376e..8d4d368 100644
--- a/fs/nfs/client.c
+++ b/fs/nfs/client.c
@@ -223,6 +223,8 @@ static void nfs_free_client(struct nfs_client *clp)
#ifdef CONFIG_NFS_V4_1
if (clp->cl_ex_cred != NULL)
put_rpccred(clp->cl_ex_cred);
+ if (!IS_ERR(clp->cl_rpcclient_i))
+ rpc_shutdown_client(clp->cl_rpcclient_i);
#endif /* CONFIG_NFS_V4_1 */
kfree(clp->cl_hostname);
@@ -533,6 +535,7 @@ static int nfs_create_rpc_client(struct nfs_client *clp,
.version = clp->rpc_ops->version,
.authflavor = flavor,
};
+ struct rpc_auth *auth;
if (discrtry)
args.flags |= RPC_CLNT_CREATE_DISCRTRY;
@@ -551,6 +554,19 @@ static int nfs_create_rpc_client(struct nfs_client *clp,
clp->cl_rpcclient = clnt;
clnt->cl_private = clp;
+
+ if (clp->cl_rpcclient->cl_auth->au_flavor < RPC_AUTH_GSS_KRB5)
+ return 0;
+
+ clp->cl_rpcclient_i = rpc_clone_client(clp->cl_rpcclient);
+ if (clp->cl_rpcclient->cl_auth->au_flavor != RPC_AUTH_GSS_KRB5I) {
+ auth = rpcauth_create(RPC_AUTH_GSS_KRB5I, clp->cl_rpcclient_i);
+ if (IS_ERR(auth)) {
+ dprintk("%s: couldn't create credcache!\n", __func__);
+ return PTR_ERR(auth);
+ }
+ }
+
return 0;
}
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index e9cfb15..745386c 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -51,6 +51,7 @@
#include <linux/module.h>
#include <linux/nfs41_session_recovery.h>
#include <linux/sunrpc/bc_xprt.h>
+#include <linux/crypto.h>
#include "nfs4_fs.h"
#include "delegation.h"
@@ -4221,6 +4222,28 @@ int nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name,
* NFS4ERR_BADSESSION in the sequence operation, and will therefore
* be in some phase of session reset.
*/
+
+struct nfs4_sec_oid nfs_hash_algs[] = {
+ {7, "\x06\x05\x2B\x0E\x03\x02\x1A",
+ "hmac(sha1)", 20},
+ {11, "\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x04",
+ "hmac(sha224)", 28},
+ {11, "\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01",
+ "hmac(sha256)", 32},
+ {11, "\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02",
+ "hmac(sha384)", 48},
+ {11, "\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03",
+ "hmac(sha512)", 64},
+ {0, NULL, NULL, 0},
+};
+
+struct nfs4_sec_oid nfs_encr_algs[] = {
+ {11, "\x06\x09\x60\x86\x48\x01\x65\x03\x04\x01\x02", "cbc(aes)", 16},
+ {11, "\x06\x09\x60\x86\x48\x01\x65\x03\x04\x01\x16", "cbc(aes)", 24},
+ {11, "\x06\x09\x60\x86\x48\x01\x65\x03\x04\x01\x2A", "cbc(aes)", 32},
+ {0, NULL, NULL, 0},
+};
+
static int nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred)
{
nfs4_verifier verifier;
@@ -4229,6 +4252,7 @@ static int nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred)
};
struct nfs41_exchange_id_res res = {
.client = clp,
+ .sp_type = -1,
};
int status;
int loop = 0;
@@ -4258,6 +4282,44 @@ static int nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred)
*p = htonl((u32)clp->cl_boot_time.tv_nsec);
args.verifier = &verifier;
+ /* fill out state protection portion of the EXCHANGE_ID */
+ args.sp_type = SP4_SSV;
+ clp->ssv_type = args.sp_type;
+
+ clp->sp_must_enforce[0] = (1UL << OP_DELEGPURGE);
+ clp->sp_must_enforce[1] = (1UL << (OP_EXCHANGE_ID-32));
+ clp->sp_must_enforce[1] |= (1UL << (OP_CREATE_SESSION-32));
+ clp->sp_must_enforce[1] |= (1UL << (OP_DESTROY_SESSION-32));
+ clp->sp_must_enforce[1] |= (1UL << (OP_BIND_CONN_TO_SESSION-32));
+ clp->sp_must_enforce[1] |= (1UL << (OP_DESTROY_CLIENTID-32));
+
+ clp->sp_must_allow[0] = (1UL << OP_CLOSE);
+ clp->sp_must_allow[1] = 0;
+
+ switch(args.sp_type) {
+ case SP4_MACH_CRED:
+ memcpy(args.u.mach_ops.must_enforce, clp->sp_must_enforce,
+ sizeof(clp->sp_must_enforce));
+ memcpy(args.u.mach_ops.must_allow, clp->sp_must_allow,
+ sizeof(clp->sp_must_allow));
+ break;
+ case SP4_SSV:
+ memcpy(args.u.ssv_parms.ops.must_enforce, clp->sp_must_enforce,
+ sizeof(clp->sp_must_enforce));
+ memcpy(args.u.ssv_parms.ops.must_allow, clp->sp_must_allow,
+ sizeof(clp->sp_must_allow));
+ args.u.ssv_parms.hash_algs = nfs_hash_algs;
+ args.u.ssv_parms.encr_algs = nfs_encr_algs;
+ args.u.ssv_parms.window = 1;
+ args.u.ssv_parms.num_gss_handles = 1;
+ break;
+ case SP4_NONE:
+ break;
+ default:
+ /*log error?*/
+ BUG_ON(1);
+ }
+
while (1) {
args.id_len = scnprintf(args.id, sizeof(args.id),
"%s/%s %u",
@@ -4266,7 +4328,7 @@ static int nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred)
RPC_DISPLAY_ADDR),
clp->cl_id_uniquifier);
- status = rpc_call_sync(clp->cl_rpcclient, &msg, 0);
+ status = rpc_call_sync(clp->cl_rpcclient_i, &msg, 0);
if (status != NFS4ERR_CLID_INUSE)
break;
@@ -4297,8 +4359,69 @@ static int nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred)
if (got_cred)
put_rpccred(cred);
+ /* process EXCHANGE_ID reply getting state management (SSV) setup */
+ if (!status && (res.sp_type != args.sp_type))
+ printk("server returned different sp_type %d than "
+ "client's requested %d\n", res.sp_type, args.sp_type);
+
+ if (!status && (clp->ssv == NULL)) {
+ struct hash_desc desc;
+ int ssv_len;
+ struct nfs4_sec_oid *hash_algs = args.u.ssv_parms.hash_algs;
+ struct nfs4_sec_oid *encr_algs = args.u.ssv_parms.encr_algs;
+ int spi_hash_alg = res.u.spr_ssv_info.spi_hash_alg;
+ int spi_encr_alg = res.u.spr_ssv_info.spi_encr_alg;
+
+ /* allocate/zero out memory for ssv structure */
+ clp->ssv = kzalloc(sizeof(struct nfs4_ssv), GFP_KERNEL);
+ if (clp->ssv == NULL) {
+ status = -ENOMEM;
+ goto out;
+ }
+
+ /* get hash/encr algo names and keylens for the selected
+ * integrity/privacy algorithms
+ */
+ strncpy(clp->ssv->mic_algo, hash_algs[spi_hash_alg].name,
+ MAX_ALGONAME_LEN);
+ strncpy(clp->ssv->seal_algo, encr_algs[spi_encr_alg].name,
+ MAX_ALGONAME_LEN);
+ clp->ssv->mic_keylen = encr_algs[spi_hash_alg].keylen;
+ clp->ssv->seal_keylen = encr_algs[spi_encr_alg].keylen;
+
+ /* check that returned SSV length is same as the selected
+ * selected digest's algorithm key size
+ */
+ desc.tfm = crypto_alloc_hash(clp->ssv->mic_algo, 0,
+ CRYPTO_ALG_ASYNC);
+ if (IS_ERR(desc.tfm))
+ goto out_free_ssv;
+ ssv_len = crypto_hash_digestsize(desc.tfm);
+ crypto_free_hash(desc.tfm);
+ if (res.u.spr_ssv_info.spi_ssv_len != ssv_len) {
+ printk("ERROR: server's ssv len %d, mic key len %d\n",
+ res.u.spr_ssv_info.spi_ssv_len, ssv_len);
+ status = NFS4ERR_INVAL;
+ goto out_free_ssv;
+ }
+
+ /* generate 1st ssv ALL ZEROs */
+ clp->ssv->ssv_len = res.u.spr_ssv_info.spi_ssv_len;
+ clp->ssv->version = 1;
+ clp->ssv->initiate = 1;
+ }
+ if (!status && clp->ssv) {
+ clp->ssv->gss_handle.len = res.u.spr_ssv_info.spi_handle.len;
+ memcpy(clp->ssv->gss_handle.data,
+ res.u.spr_ssv_info.spi_handle.data,
+ res.u.spr_ssv_info.spi_handle.len);
+ }
+out:
dprintk("<-- %s status= %d\n", __func__, status);
return status;
+out_free_ssv:
+ kfree(clp->ssv);
+ goto out;
}
struct nfs4_get_lease_time_data {
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index 8ce8abd..59ee3fa 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -52,6 +52,9 @@
#include <linux/nfs_fs.h>
#include <linux/nfs_idmap.h>
#include "nfs4_fs.h"
+#include <linux/crypto.h>
+#include <linux/random.h>
+#include <linux/nfs4.h>
#define NFSDBG_FACILITY NFSDBG_XDR
@@ -1549,7 +1552,8 @@ static void encode_exchange_id(struct xdr_stream *xdr,
struct nfs41_exchange_id_args *args,
struct compound_hdr *hdr)
{
- uint32_t *p;
+ __be32 *p;
+ int i = 0, len;
RESERVE_SPACE(4 + sizeof(args->verifier->data));
WRITE32(OP_EXCHANGE_ID);
@@ -1557,9 +1561,56 @@ static void encode_exchange_id(struct xdr_stream *xdr,
encode_string(xdr, args->id_len, args->id);
- RESERVE_SPACE(12);
+ RESERVE_SPACE(8);
WRITE32(args->flags);
- WRITE32(0); /* zero length state_protect4_a */
+ WRITE32(args->sp_type); /* zero length state_protect4_a */
+ switch (args->sp_type) {
+ case SP4_NONE:
+ break;
+ case SP4_MACH_CRED:
+ RESERVE_SPACE(24);
+ WRITE32(2);
+ WRITE32(args->u.mach_ops.must_enforce[0]);
+ WRITE32(args->u.mach_ops.must_enforce[1]);
+ WRITE32(2);
+ WRITE32(args->u.mach_ops.must_allow[0]);
+ WRITE32(args->u.mach_ops.must_allow[1]);
+ break;
+ case SP4_SSV:
+ RESERVE_SPACE(24);
+ WRITE32(2);
+ WRITE32(args->u.ssv_parms.ops.must_enforce[0]);
+ WRITE32(args->u.ssv_parms.ops.must_enforce[1]);
+ WRITE32(2);
+ WRITE32(args->u.ssv_parms.ops.must_allow[0]);
+ WRITE32(args->u.ssv_parms.ops.must_allow[1]);
+
+ RESERVE_SPACE(4);
+ WRITE32(5);
+ while(args->u.ssv_parms.hash_algs[i].data != NULL) {
+ len = args->u.ssv_parms.hash_algs[i].len;
+ RESERVE_SPACE(4 + len);
+ WRITE32(len);
+ WRITEMEM(args->u.ssv_parms.hash_algs[i].data, len);
+ i++;
+ }
+
+ RESERVE_SPACE(4);
+ WRITE32(3);
+ i=0;
+ while(args->u.ssv_parms.encr_algs[i].data != NULL) {
+ len = args->u.ssv_parms.encr_algs[i].len;
+ RESERVE_SPACE(4 + len);
+ WRITE32(len);
+ WRITEMEM(args->u.ssv_parms.encr_algs[i].data, len);
+ i++;
+ }
+ RESERVE_SPACE(8);
+ WRITE32(args->u.ssv_parms.window);
+ WRITE32(args->u.ssv_parms.num_gss_handles);
+ break;
+ }
+ RESERVE_SPACE(4);
WRITE32(0); /* zero length implementation id array */
hdr->nops++;
hdr->replen += decode_exchange_id_maxsz;
@@ -4152,12 +4203,27 @@ static int decode_delegreturn(struct xdr_stream *xdr)
}
#if defined(CONFIG_NFS_V4_1)
+static __be32 read_mach_ops(struct xdr_stream *xdr, u32 *bmap)
+{
+ __be32 *p;
+ int i, num;
+
+ READ_BUF(4);
+ READ32(num);
+ READ_BUF(num * 4);
+ bmap[0] = bmap[1] = 0;
+ for (i = 0; i < num; i++)
+ READ32(bmap[i]);
+ return 0;
+}
+
static int decode_exchange_id(struct xdr_stream *xdr,
struct nfs41_exchange_id_res *res)
{
uint32_t *p;
- int status, dummy;
struct nfs_client *clp = res->client;
+ int status, dummy;
+ u32 sp_must_enforce[2], sp_must_allow[2];
status = decode_op_hdr(xdr, OP_EXCHANGE_ID);
if (status)
@@ -4170,9 +4236,59 @@ static int decode_exchange_id(struct xdr_stream *xdr,
READ32(clp->cl_exchange_flags);
/* We ask for SP4_NONE */
- READ32(dummy);
- if (dummy != SP4_NONE)
+ READ32(res->sp_type);
+ /* check if received state protect differs from requested */
+ switch(res->sp_type) {
+ case SP4_NONE:
+ break;
+ case SP4_MACH_CRED:
+ /* spi_must_enforce */
+ status = read_mach_ops(xdr, sp_must_enforce);
+ if (status)
+ goto out;
+
+ /* spo_must_allow */
+ status = read_mach_ops(xdr, sp_must_allow);
+ if (status)
+ goto out;
+ break;
+ case SP4_SSV:
+ /* spi_must_enforce. ignoring reply */
+ status = read_mach_ops(xdr, sp_must_enforce);
+ if (status)
+ goto out;
+
+ /* spo_must_allow. ignoring reply */
+ status = read_mach_ops(xdr, sp_must_allow);
+ if (status)
+ goto out;
+
+ READ_BUF(4);
+ READ32(res->u.spr_ssv_info.spi_hash_alg);
+ READ_BUF(4);
+ READ32(res->u.spr_ssv_info.spi_encr_alg);
+ READ_BUF(4);
+ READ32(res->u.spr_ssv_info.spi_ssv_len);
+ READ_BUF(4);
+ READ32(res->u.spr_ssv_info.spi_window);
+
+ READ_BUF(4);
+ READ32(dummy);
+ if (!dummy)
+ return -EIO;
+ /* we always ask for 1 handle */
+ READ_BUF(4);
+ READ32(res->u.spr_ssv_info.spi_handle.len);
+ if (res->u.spr_ssv_info.spi_handle.len > MAX_GSSHANDLE_LEN)
+ return -EIO;
+
+ READ_BUF(res->u.spr_ssv_info.spi_handle.len);
+ COPYMEM(res->u.spr_ssv_info.spi_handle.data,
+ res->u.spr_ssv_info.spi_handle.len);
+ break;
+ default:
return -EIO;
+ }
/* minor_id */
READ_BUF(8);
@@ -4195,7 +4311,9 @@ static int decode_exchange_id(struct xdr_stream *xdr,
READ32(dummy);
p += XDR_QUADLEN(dummy);
- return 0;
+ status = 0;
+out:
+ return status;
}
#endif /* CONFIG_NFS_V4_1 */
--
1.6.0.6
More information about the pNFS
mailing list