[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