From: Trond Myklebust <trond.myklebust@fys.uio.no>


RPCSEC_GSS: Client-side only support for rpcsec_gss integrity protection. 
Since this requires checksumming an entire request, instead of just the
header, and since the request may include, for example, pages with write
data, we modify the gss_api routines to pass xdr_buf's instead of
xdr_netobj's where necessary.

We add rpcauth_wrap_req and rpcauth_unwrap_resp to rpcauth.c, wrappers for
the new rpc cred ops crwrap_req and crunwrap_req, which are called just
before encoding, and just after decoding, respectively.



---

 include/linux/sunrpc/auth.h           |    6 
 include/linux/sunrpc/gss_api.h        |    9 -
 include/linux/sunrpc/gss_krb5.h       |    6 
 include/linux/sunrpc/xdr.h            |    4 
 net/sunrpc/auth.c                     |   29 ++++
 net/sunrpc/auth_gss/auth_gss.c        |  231 ++++++++++++++++++++++++++++------
 net/sunrpc/auth_gss/gss_krb5_crypto.c |   51 ++++++-
 net/sunrpc/auth_gss/gss_krb5_mech.c   |    8 -
 net/sunrpc/auth_gss/gss_krb5_seal.c   |   23 ---
 net/sunrpc/auth_gss/gss_krb5_unseal.c |   58 +-------
 net/sunrpc/auth_gss/gss_mech_switch.c |    4 
 net/sunrpc/clnt.c                     |    6 
 net/sunrpc/sunrpc_syms.c              |    3 
 net/sunrpc/xdr.c                      |  144 +++++++++++++++++++++
 14 files changed, 450 insertions(+), 132 deletions(-)

diff -puN include/linux/sunrpc/auth.h~nfs-13-krb5_integ include/linux/sunrpc/auth.h
--- 25/include/linux/sunrpc/auth.h~nfs-13-krb5_integ	2004-01-09 22:16:15.000000000 -0800
+++ 25-akpm/include/linux/sunrpc/auth.h	2004-01-09 22:16:15.000000000 -0800
@@ -102,6 +102,10 @@ struct rpc_credops {
 	u32 *			(*crmarshal)(struct rpc_task *, u32 *, int);
 	int			(*crrefresh)(struct rpc_task *);
 	u32 *			(*crvalidate)(struct rpc_task *, u32 *);
+	int			(*crwrap_req)(struct rpc_task *, kxdrproc_t,
+						void *, u32 *, void *);
+	int			(*crunwrap_resp)(struct rpc_task *, kxdrproc_t,
+						void *, u32 *, void *);
 };
 
 extern struct rpc_authops	authunix_ops;
@@ -124,6 +128,8 @@ void			put_rpccred(struct rpc_cred *);
 void			rpcauth_unbindcred(struct rpc_task *);
 u32 *			rpcauth_marshcred(struct rpc_task *, u32 *);
 u32 *			rpcauth_checkverf(struct rpc_task *, u32 *);
+int			rpcauth_wrap_req(struct rpc_task *task, kxdrproc_t encode, void *rqstp, u32 *data, void *obj);
+int			rpcauth_unwrap_resp(struct rpc_task *task, kxdrproc_t decode, void *rqstp, u32 *data, void *obj);
 int			rpcauth_refreshcred(struct rpc_task *);
 void			rpcauth_invalcred(struct rpc_task *);
 int			rpcauth_uptodatecred(struct rpc_task *);
diff -puN include/linux/sunrpc/gss_api.h~nfs-13-krb5_integ include/linux/sunrpc/gss_api.h
--- 25/include/linux/sunrpc/gss_api.h~nfs-13-krb5_integ	2004-01-09 22:16:15.000000000 -0800
+++ 25-akpm/include/linux/sunrpc/gss_api.h	2004-01-09 22:16:15.000000000 -0800
@@ -16,6 +16,7 @@
 
 #ifdef __KERNEL__
 #include <linux/sunrpc/xdr.h>
+#include <linux/uio.h>
 
 /* The mechanism-independent gss-api context: */
 struct gss_ctx {
@@ -39,11 +40,11 @@ u32 gss_import_sec_context(
 u32 gss_get_mic(
 		struct gss_ctx		*ctx_id,
 		u32			qop,
-		struct xdr_netobj	*message,
+		struct xdr_buf		*message,
 		struct xdr_netobj	*mic_token);
 u32 gss_verify_mic(
 		struct gss_ctx		*ctx_id,
-		struct xdr_netobj	*message,
+		struct xdr_buf		*message,
 		struct xdr_netobj	*mic_token,
 		u32			*qstate);
 u32 gss_delete_sec_context(
@@ -95,11 +96,11 @@ struct gss_api_ops {
 	u32 (*gss_get_mic)(
 			struct gss_ctx		*ctx_id,
 			u32			qop, 
-			struct xdr_netobj	*message,
+			struct xdr_buf		*message,
 			struct xdr_netobj	*mic_token);
 	u32 (*gss_verify_mic)(
 			struct gss_ctx		*ctx_id,
-			struct xdr_netobj	*message,
+			struct xdr_buf		*message,
 			struct xdr_netobj	*mic_token,
 			u32			*qstate);
 	void (*gss_delete_sec_context)(
diff -puN include/linux/sunrpc/gss_krb5.h~nfs-13-krb5_integ include/linux/sunrpc/gss_krb5.h
--- 25/include/linux/sunrpc/gss_krb5.h~nfs-13-krb5_integ	2004-01-09 22:16:15.000000000 -0800
+++ 25-akpm/include/linux/sunrpc/gss_krb5.h	2004-01-09 22:16:15.000000000 -0800
@@ -115,18 +115,18 @@ enum seal_alg {
 #define ENCTYPE_UNKNOWN         0x01ff
 
 s32
-krb5_make_checksum(s32 cksumtype, char *header, char *body, int body_len,
+krb5_make_checksum(s32 cksumtype, char *header, struct xdr_buf *body,
 		   struct xdr_netobj *cksum);
 
 u32
 krb5_make_token(struct krb5_ctx *context_handle, int qop_req,
-	struct xdr_netobj *input_message_buffer,
+	struct xdr_buf *input_message_buffer,
 	struct xdr_netobj *output_message_buffer, int toktype);
 
 u32
 krb5_read_token(struct krb5_ctx *context_handle,
 	  struct xdr_netobj *input_token_buffer,
-	  struct xdr_netobj *message_buffer,
+	  struct xdr_buf *message_buffer,
 	  int *qop_state, int toktype);
 
 u32
diff -puN include/linux/sunrpc/xdr.h~nfs-13-krb5_integ include/linux/sunrpc/xdr.h
--- 25/include/linux/sunrpc/xdr.h~nfs-13-krb5_integ	2004-01-09 22:16:15.000000000 -0800
+++ 25-akpm/include/linux/sunrpc/xdr.h	2004-01-09 22:16:15.000000000 -0800
@@ -141,6 +141,10 @@ void xdr_shift_iovec(struct iovec *, int
 extern int xdr_kmap(struct iovec *, struct xdr_buf *, size_t);
 extern void xdr_kunmap(struct xdr_buf *, size_t);
 extern void xdr_shift_buf(struct xdr_buf *, size_t);
+extern void _copy_from_pages(char *, struct page **, size_t, size_t);
+extern void xdr_buf_from_iov(struct iovec *, struct xdr_buf *);
+extern int xdr_buf_subsegment(struct xdr_buf *, struct xdr_buf *, int, int);
+extern int xdr_buf_read_netobj(struct xdr_buf *, struct xdr_netobj *, int);
 
 /*
  * Helper structure for copying from an sk_buff.
diff -puN net/sunrpc/auth.c~nfs-13-krb5_integ net/sunrpc/auth.c
--- 25/net/sunrpc/auth.c~nfs-13-krb5_integ	2004-01-09 22:16:15.000000000 -0800
+++ 25-akpm/net/sunrpc/auth.c	2004-01-09 22:16:15.000000000 -0800
@@ -340,6 +340,35 @@ rpcauth_checkverf(struct rpc_task *task,
 }
 
 int
+rpcauth_wrap_req(struct rpc_task *task, kxdrproc_t encode, void *rqstp,
+		u32 *data, void *obj)
+{
+	struct rpc_cred *cred = task->tk_msg.rpc_cred;
+
+	dprintk("RPC: %4d using %s cred %p to wrap rpc data\n",
+			task->tk_pid, cred->cr_auth->au_ops->au_name, cred);
+	if (cred->cr_ops->crwrap_req)
+		return cred->cr_ops->crwrap_req(task, encode, rqstp, data, obj);
+	/* By default, we encode the arguments normally. */
+	return encode(rqstp, data, obj);
+}
+
+int
+rpcauth_unwrap_resp(struct rpc_task *task, kxdrproc_t decode, void *rqstp,
+		u32 *data, void *obj)
+{
+	struct rpc_cred *cred = task->tk_msg.rpc_cred;
+
+	dprintk("RPC: %4d using %s cred %p to unwrap rpc data\n",
+			task->tk_pid, cred->cr_auth->au_ops->au_name, cred);
+	if (cred->cr_ops->crunwrap_resp)
+		return cred->cr_ops->crunwrap_resp(task, decode, rqstp,
+						   data, obj);
+	/* By default, we decode the arguments normally. */
+	return decode(rqstp, data, obj);
+}
+
+int
 rpcauth_refreshcred(struct rpc_task *task)
 {
 	struct rpc_auth	*auth = task->tk_auth;
diff -puN net/sunrpc/auth_gss/auth_gss.c~nfs-13-krb5_integ net/sunrpc/auth_gss/auth_gss.c
--- 25/net/sunrpc/auth_gss/auth_gss.c~nfs-13-krb5_integ	2004-01-09 22:16:15.000000000 -0800
+++ 25-akpm/net/sunrpc/auth_gss/auth_gss.c	2004-01-09 22:16:15.000000000 -0800
@@ -51,6 +51,7 @@
 #include <linux/sunrpc/gss_err.h>
 #include <linux/workqueue.h>
 #include <linux/sunrpc/rpc_pipe_fs.h>
+#include <linux/sunrpc/gss_api.h>
 #include <asm/uaccess.h>
 
 static struct rpc_authops authgss_ops;
@@ -65,7 +66,9 @@ static struct rpc_credops gss_credops;
 
 #define GSS_CRED_EXPIRE		(60 * HZ)	/* XXX: reasonable? */
 #define GSS_CRED_SLACK		1024		/* XXX: unused */
-#define GSS_VERF_SLACK		48		/* length of a krb5 verifier.*/
+/* length of a krb5 verifier (48), plus data added before arguments when
+ * using integrity (two 4-byte integers): */
+#define GSS_VERF_SLACK		56
 
 /* XXX this define must match the gssd define
 * as it is passed to gssd to signal the use of
@@ -669,21 +672,14 @@ gss_marshal(struct rpc_task *task, u32 *
 	struct gss_cl_ctx	*ctx = gss_cred_get_ctx(cred);
 	u32		*cred_len;
 	struct rpc_rqst *req = task->tk_rqstp;
-	struct rpc_clnt *clnt = task->tk_client;
-	struct rpc_xprt *xprt = clnt->cl_xprt;
-	u32             *verfbase = req->rq_svec[0].iov_base; 
 	u32             maj_stat = 0;
-	struct xdr_netobj bufin,bufout;
+	struct xdr_netobj mic;
+	struct iovec	iov;
+	struct xdr_buf	verf_buf;
 	u32		service;
 
 	dprintk("RPC: gss_marshal\n");
 
-	/* We compute the checksum for the verifier over the xdr-encoded bytes
-	 * starting with the xid (which verfbase points to) and ending at
-	 * the end of the credential. */
-	if (xprt->stream)
-		verfbase++; /* See clnt.c:call_header() */
-
 	*p++ = htonl(RPC_AUTH_GSS);
 	cred_len = p++;
 
@@ -704,24 +700,28 @@ gss_marshal(struct rpc_task *task, u32 *
 	p = xdr_encode_netobj(p, &ctx->gc_wire_ctx);
 	*cred_len = htonl((p - (cred_len + 1)) << 2);
 
-	/* Marshal verifier. */
-	bufin.data = (u8 *)verfbase;
-	bufin.len = (p - verfbase) << 2;
+	/* We compute the checksum for the verifier over the xdr-encoded bytes
+	 * starting with the xid and ending at the end of the credential: */
+	iov.iov_base = req->rq_snd_buf.head[0].iov_base;
+	if (task->tk_client->cl_xprt->stream)
+		/* See clnt.c:call_header() */
+		iov.iov_base += 4;
+	iov.iov_len = (u8 *)p - (u8 *)iov.iov_base;
+	xdr_buf_from_iov(&iov, &verf_buf);
 
 	/* set verifier flavor*/
 	*p++ = htonl(RPC_AUTH_GSS);
 
-	bufout.data = (u8 *)(p + 1);
+	mic.data = (u8 *)(p + 1);
 	maj_stat = gss_get_mic(ctx->gc_gss_ctx,
 			       GSS_C_QOP_DEFAULT, 
-			       &bufin, &bufout);
+			       &verf_buf, &mic);
 	if(maj_stat != 0){
-		printk("gss_marshal: gss_get_mic FAILED (%d)\n",
-		       maj_stat);
+		printk("gss_marshal: gss_get_mic FAILED (%d)\n", maj_stat);
 		goto out_put_ctx;
 	}
-	*p++ = htonl(bufout.len);
-	p += XDR_QUADLEN(bufout.len);
+	*p++ = htonl(mic.len);
+	p += XDR_QUADLEN(mic.len);
 	gss_put_ctx(ctx);
 	return p;
 out_put_ctx:
@@ -749,35 +749,45 @@ static u32 *
 gss_validate(struct rpc_task *task, u32 *p)
 {
 	struct rpc_cred *cred = task->tk_msg.rpc_cred;
+	struct gss_cred	*gss_cred = container_of(cred, struct gss_cred,
+						gc_base);
 	struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred);
 	u32		seq, qop_state;
-	struct xdr_netobj bufin;
-	struct xdr_netobj bufout;
+	struct iovec	iov;
+	struct xdr_buf	verf_buf;
+	struct xdr_netobj mic;
 	u32		flav,len;
+	u32		service;
 
 	dprintk("RPC: gss_validate\n");
 
 	flav = ntohl(*p++);
-	if ((len = ntohl(*p++)) > RPC_MAX_AUTH_SIZE) {
-                printk("RPC: giant verf size: %ld\n", (unsigned long) len);
+	if ((len = ntohl(*p++)) > RPC_MAX_AUTH_SIZE)
                 goto out_bad;
-	}
-	dprintk("RPC: gss_validate: verifier flavor %d, len %d\n", flav, len);
-
-	if (flav != RPC_AUTH_GSS) {
-		printk("RPC: bad verf flavor: %ld\n", (unsigned long)flav);
+	if (flav != RPC_AUTH_GSS)
 		goto out_bad;
-	}
 	seq = htonl(task->tk_gss_seqno);
-	bufin.data = (u8 *) &seq;
-	bufin.len = sizeof(seq);
-	bufout.data = (u8 *) p;
-	bufout.len = len;
-
-	if (gss_verify_mic(ctx->gc_gss_ctx, &bufin, &bufout, &qop_state) != 0)
-		goto out_bad;
-	task->tk_auth->au_rslack = XDR_QUADLEN(len) + 2;
-	dprintk("RPC: GSS gss_validate: gss_verify_mic succeeded.\n");
+	iov.iov_base = &seq;
+	iov.iov_len = sizeof(seq);
+	xdr_buf_from_iov(&iov, &verf_buf);
+	mic.data = (u8 *)p;
+	mic.len = len;
+
+	if (gss_verify_mic(ctx->gc_gss_ctx, &verf_buf, &mic, &qop_state))
+               goto out_bad;
+       service = gss_pseudoflavor_to_service(gss_cred->gc_flavor);
+       switch (service) {
+       case RPC_GSS_SVC_NONE:
+	       /* verifier data, flavor, length: */
+	       task->tk_auth->au_rslack = XDR_QUADLEN(len) + 2;
+	       break;
+       case RPC_GSS_SVC_INTEGRITY:
+	       /* verifier data, flavor, length, length, sequence number: */
+	       task->tk_auth->au_rslack = XDR_QUADLEN(len) + 4;
+	       break;
+       default:
+	       goto out_bad;
+       }
 	gss_put_ctx(ctx);
 	return p + XDR_QUADLEN(len);
 out_bad:
@@ -785,6 +795,147 @@ out_bad:
 	return NULL;
 }
 
+static int
+gss_wrap_req(struct rpc_task *task,
+	     kxdrproc_t encode, void *rqstp, u32 *p, void *obj)
+{
+	struct rpc_rqst	*req = (struct rpc_rqst *)rqstp;
+	struct xdr_buf	*snd_buf = &req->rq_snd_buf;
+	struct rpc_cred *cred = task->tk_msg.rpc_cred;
+	struct gss_cred	*gss_cred = container_of(cred, struct gss_cred,
+			gc_base);
+	struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred);
+	u32             *integ_len = NULL;
+	int             status = -EIO;
+	u32             maj_stat = 0;
+	struct xdr_buf	integ_buf;
+	struct xdr_netobj mic;
+	u32		service;
+	u32		offset, *q;
+	struct iovec	*iov;
+
+	dprintk("RPC: gss_wrap_body\n");
+	BUG_ON(!ctx);
+	if (ctx->gc_proc != RPC_GSS_PROC_DATA) {
+		/* The spec seems a little ambiguous here, but I think that not
+		 * wrapping context destruction requests makes the most sense.
+		 */
+		status = encode(rqstp, p, obj);
+		goto out;
+	}
+	service = gss_pseudoflavor_to_service(gss_cred->gc_flavor);
+	switch (service) {
+		case RPC_GSS_SVC_NONE:
+			status = encode(rqstp, p, obj);
+			goto out;
+		case RPC_GSS_SVC_INTEGRITY:
+
+			integ_len = p++;
+			offset = (u8 *)p - (u8 *)snd_buf->head[0].iov_base;
+			*p++ = htonl(task->tk_gss_seqno);
+
+			status = encode(rqstp, p, obj);
+			if (status)
+				goto out;
+
+			if (xdr_buf_subsegment(snd_buf, &integ_buf,
+						offset, snd_buf->len - offset))
+				goto out;
+			*integ_len = htonl(integ_buf.len);
+
+			/* guess whether we're in the head or the tail: */
+			if (snd_buf->page_len || snd_buf->tail[0].iov_len) 
+				iov = snd_buf->tail;
+			else
+				iov = snd_buf->head;
+			p = iov->iov_base + iov->iov_len;
+			mic.data = (u8 *)(p + 1);
+
+			maj_stat = gss_get_mic(ctx->gc_gss_ctx,
+					GSS_C_QOP_DEFAULT, &integ_buf, &mic);
+			status = -EIO; /* XXX? */
+			if (maj_stat)
+				goto out;
+			q = p;
+			*q++ = htonl(mic.len);
+			q += XDR_QUADLEN(mic.len);
+
+			offset = (u8 *)q - (u8 *)p;
+			iov->iov_len += offset;
+			snd_buf->len += offset;
+			break;
+		case RPC_GSS_SVC_PRIVACY:
+		default:
+			goto out;
+	}
+	status = 0;
+out:
+	gss_put_ctx(ctx);
+	dprintk("RPC: gss_wrap_req returning %d\n", status);
+	return status;
+}
+
+static int
+gss_unwrap_resp(struct rpc_task *task,
+		kxdrproc_t decode, void *rqstp, u32 *p, void *obj)
+{
+	struct rpc_rqst *req = (struct rpc_rqst *)rqstp;
+	struct xdr_buf	*rcv_buf = &req->rq_rcv_buf;
+	struct rpc_cred *cred = task->tk_msg.rpc_cred;
+	struct gss_cred *gss_cred = container_of(cred, struct gss_cred,
+			gc_base);
+	struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred);
+	struct xdr_buf	integ_buf;
+	struct xdr_netobj mic;
+	int             status = -EIO;
+	u32		maj_stat = 0;
+	u32		service;
+	u32		data_offset, mic_offset;
+	u32		integ_len;
+
+	BUG_ON(!ctx);
+
+	if (ctx->gc_proc != RPC_GSS_PROC_DATA)
+		goto out_decode;
+	service = gss_pseudoflavor_to_service(gss_cred->gc_flavor);
+	switch (service) {
+		case RPC_GSS_SVC_NONE:
+			goto out_decode;
+		case RPC_GSS_SVC_INTEGRITY:
+			integ_len = ntohl(*p++);
+			if (integ_len & 3)
+				goto out;
+			data_offset = (u8 *)p - (u8 *)rcv_buf->head[0].iov_base;
+			mic_offset = integ_len + data_offset;
+			if (mic_offset > rcv_buf->len)
+				goto out;
+			if (ntohl(*p++) != task->tk_gss_seqno)
+				goto out;
+
+			if (xdr_buf_subsegment(rcv_buf, &integ_buf, data_offset,
+						mic_offset - data_offset))
+				goto out;
+
+			if (xdr_buf_read_netobj(rcv_buf, &mic, mic_offset))
+				goto out;
+
+			maj_stat = gss_verify_mic(ctx->gc_gss_ctx, &integ_buf,
+					&mic, NULL);
+			if (maj_stat != GSS_S_COMPLETE)
+				goto out;
+			break;
+		case RPC_GSS_SVC_PRIVACY:
+		default:
+			goto out;
+	}
+out_decode:
+	status = decode(rqstp, p, obj);
+out:
+	gss_put_ctx(ctx);
+	dprintk("RPC: gss_unwrap_resp returning %d\n", status);
+	return status;
+}
+  
 static struct rpc_authops authgss_ops = {
 	.owner		= THIS_MODULE,
 	.au_flavor	= RPC_AUTH_GSS,
@@ -802,6 +953,8 @@ static struct rpc_credops gss_credops = 
 	.crmarshal	= gss_marshal,
 	.crrefresh	= gss_refresh,
 	.crvalidate	= gss_validate,
+	.crwrap_req	= gss_wrap_req,
+	.crunwrap_resp	= gss_unwrap_resp,
 };
 
 static struct rpc_pipe_ops gss_upcall_ops = {
diff -puN net/sunrpc/auth_gss/gss_krb5_crypto.c~nfs-13-krb5_integ net/sunrpc/auth_gss/gss_krb5_crypto.c
--- 25/net/sunrpc/auth_gss/gss_krb5_crypto.c~nfs-13-krb5_integ	2004-01-09 22:16:15.000000000 -0800
+++ 25-akpm/net/sunrpc/auth_gss/gss_krb5_crypto.c	2004-01-09 22:16:15.000000000 -0800
@@ -39,6 +39,7 @@
 #include <linux/slab.h>
 #include <asm/scatterlist.h>
 #include <linux/crypto.h>
+#include <linux/highmem.h>
 #include <linux/sunrpc/gss_krb5.h>
 
 #ifdef RPC_DEBUG
@@ -57,7 +58,7 @@ krb5_encrypt(
         struct scatterlist sg[1];
 	u8 local_iv[16] = {0};
 
-	dprintk("RPC: gss_k5encrypt: TOP in %p out %p\nin data:\n", out, in);
+	dprintk("RPC: krb5_encrypt: input data:\n");
 	print_hexl((u32 *)in, length, 0);
 
 	if (length % crypto_tfm_alg_blocksize(tfm) != 0)
@@ -79,8 +80,10 @@ krb5_encrypt(
 
 	ret = crypto_cipher_encrypt_iv(tfm, sg, sg, length, local_iv);
 
+	dprintk("RPC: krb5_encrypt: output data:\n");
+	print_hexl((u32 *)out, length, 0);
 out:
-	dprintk("gss_k5encrypt returns %d\n",ret);
+	dprintk("krb5_encrypt returns %d\n",ret);
 	return(ret);
 }
 
@@ -96,8 +99,8 @@ krb5_decrypt(
 	struct scatterlist sg[1];
 	u8 local_iv[16] = {0};
 
-	dprintk("RPC: gss_k5decrypt: TOP in %p out %p\nin data:\n", in, out);
-	print_hexl((u32 *)in,length,0);
+	dprintk("RPC: krb5_decrypt: input data:\n");
+	print_hexl((u32 *)in, length, 0);
 
 	if (length % crypto_tfm_alg_blocksize(tfm) != 0)
 		goto out;
@@ -117,6 +120,8 @@ krb5_decrypt(
 
 	ret = crypto_cipher_decrypt_iv(tfm, sg, sg, length, local_iv);
 
+	dprintk("RPC: krb5_decrypt: output_data:\n");
+	print_hexl((u32 *)out, length, 0);
 out:
 	dprintk("gss_k5decrypt returns %d\n",ret);
 	return(ret);
@@ -132,13 +137,15 @@ buf_to_sg(struct scatterlist *sg, char *
 /* checksum the plaintext data and the first 8 bytes of the krb5 token header,
  * as specified by the rfc: */
 s32
-krb5_make_checksum(s32 cksumtype, char *header, char *body, int body_len, 
+krb5_make_checksum(s32 cksumtype, char *header, struct xdr_buf *body,
 		   struct xdr_netobj *cksum)
 {
 	char                            *cksumname;
 	struct crypto_tfm               *tfm = NULL; /* XXX add to ctx? */
-	struct scatterlist              sg[2];
+	struct scatterlist              sg[1];
 	u32                             code = GSS_S_FAILURE;
+	int				len, thislen, offset;
+	int				i;
 
 	switch (cksumtype) {
 		case CKSUMTYPE_RSA_MD5:
@@ -155,10 +162,36 @@ krb5_make_checksum(s32 cksumtype, char *
 	if ((cksum->data = kmalloc(cksum->len, GFP_KERNEL)) == NULL)
 		goto out;
 
-	buf_to_sg(&sg[0], header, 8);
-	buf_to_sg(&sg[1], body, body_len);
 	crypto_digest_init(tfm);
-	crypto_digest_update(tfm, sg, 2);
+	buf_to_sg(sg, header, 8);
+	crypto_digest_update(tfm, sg, 1);
+	if (body->head[0].iov_len) {
+		buf_to_sg(sg, body->head[0].iov_base, body->head[0].iov_len);
+		crypto_digest_update(tfm, sg, 1);
+	}
+
+	len = body->page_len;
+	offset = body->page_base;
+	i = 0;
+	while (len) {
+		sg->page = body->pages[i];
+		sg->offset = offset;
+		offset = 0;
+		if (PAGE_SIZE > len)
+			thislen = len;
+		else
+			thislen = PAGE_SIZE;
+		sg->length = thislen;
+		kmap(sg->page); /* XXX kmap_atomic? */
+		crypto_digest_update(tfm, sg, 1);
+		kunmap(sg->page);
+		len -= thislen;
+		i++;
+	}
+	if (body->tail[0].iov_len) {
+		buf_to_sg(sg, body->tail[0].iov_base, body->tail[0].iov_len);
+		crypto_digest_update(tfm, sg, 1);
+	}
 	crypto_digest_final(tfm, cksum->data);
 	code = 0;
 out:
diff -puN net/sunrpc/auth_gss/gss_krb5_mech.c~nfs-13-krb5_integ net/sunrpc/auth_gss/gss_krb5_mech.c
--- 25/net/sunrpc/auth_gss/gss_krb5_mech.c~nfs-13-krb5_integ	2004-01-09 22:16:15.000000000 -0800
+++ 25-akpm/net/sunrpc/auth_gss/gss_krb5_mech.c	2004-01-09 22:16:15.000000000 -0800
@@ -183,7 +183,7 @@ gss_delete_sec_context_kerberos(void *in
 
 static u32
 gss_verify_mic_kerberos(struct gss_ctx		*ctx,
-			struct xdr_netobj	*message,
+			struct xdr_buf		*message,
 			struct xdr_netobj	*mic_token,
 			u32			*qstate) {
 	u32 maj_stat = 0;
@@ -202,13 +202,11 @@ gss_verify_mic_kerberos(struct gss_ctx		
 static u32
 gss_get_mic_kerberos(struct gss_ctx	*ctx,
 		     u32		qop,
-		     struct xdr_netobj	*message,
+		     struct xdr_buf 	*message,
 		     struct xdr_netobj	*mic_token) {
 	u32 err = 0;
 	struct krb5_ctx *kctx = ctx->internal_ctx_id;
 
-	if (!message->data) return GSS_S_FAILURE;
-
 	err = krb5_make_token(kctx, qop, message, mic_token, KG_TOK_MIC_MSG);
 
 	dprintk("RPC: gss_get_mic_kerberos returning %d\n",err);
@@ -233,12 +231,14 @@ static int __init init_kerberos_module(v
 		printk("Failed to register kerberos gss mechanism!\n");
 	gm = gss_mech_get_by_OID(&gss_mech_krb5_oid);
 	gss_register_triple(RPC_AUTH_GSS_KRB5 , gm, 0, RPC_GSS_SVC_NONE);
+	gss_register_triple(RPC_AUTH_GSS_KRB5I, gm, 0, RPC_GSS_SVC_INTEGRITY);
 	gss_mech_put(gm);
 	return 0;
 }
 
 static void __exit cleanup_kerberos_module(void)
 {
+	gss_unregister_triple(RPC_AUTH_GSS_KRB5I);
 	gss_unregister_triple(RPC_AUTH_GSS_KRB5);
 }
 
diff -puN net/sunrpc/auth_gss/gss_krb5_seal.c~nfs-13-krb5_integ net/sunrpc/auth_gss/gss_krb5_seal.c
--- 25/net/sunrpc/auth_gss/gss_krb5_seal.c~nfs-13-krb5_integ	2004-01-09 22:16:15.000000000 -0800
+++ 25-akpm/net/sunrpc/auth_gss/gss_krb5_seal.c	2004-01-09 22:16:15.000000000 -0800
@@ -80,7 +80,7 @@ gss_krb5_padding(int blocksize, int leng
 
 u32
 krb5_make_token(struct krb5_ctx *ctx, int qop_req,
-		   struct xdr_netobj * text, struct xdr_netobj * token,
+		   struct xdr_buf *text, struct xdr_netobj *token,
 		   int toktype)
 {
 	s32			checksum_type;
@@ -134,24 +134,11 @@ krb5_make_token(struct krb5_ctx *ctx, in
 		*(u16 *)(krb5_hdr + 4) = htons(ctx->sealalg);
 
 	if (toktype == KG_TOK_WRAP_MSG) {
-		unsigned char pad = gss_krb5_padding(blocksize, text->len);
-
-		get_random_bytes(msg_start, blocksize); /* "confounder" */
-		memcpy(msg_start + blocksize, text->data, text->len);
-
-		memset(msg_start + blocksize + text->len, pad, pad);
-
-		if (krb5_make_checksum(checksum_type, krb5_hdr, msg_start,
-				       tmsglen, &md5cksum))
-			goto out_err;
-
-		if (krb5_encrypt(ctx->enc, NULL, msg_start, msg_start,
-					tmsglen))
-			goto out_err;
-
+		/* XXX removing support for now */
+		goto out_err;
 	} else { /* Sign only.  */
-		if (krb5_make_checksum(checksum_type, krb5_hdr, text->data,
-				       text->len, &md5cksum))
+		if (krb5_make_checksum(checksum_type, krb5_hdr, text,
+				       &md5cksum))
 			goto out_err;
 	}
 
diff -puN net/sunrpc/auth_gss/gss_krb5_unseal.c~nfs-13-krb5_integ net/sunrpc/auth_gss/gss_krb5_unseal.c
--- 25/net/sunrpc/auth_gss/gss_krb5_unseal.c~nfs-13-krb5_integ	2004-01-09 22:16:15.000000000 -0800
+++ 25-akpm/net/sunrpc/auth_gss/gss_krb5_unseal.c	2004-01-09 22:16:15.000000000 -0800
@@ -75,20 +75,19 @@
  *   to return the decrypted data.
  */
 
+/* XXX will need to change prototype and/or just split into a separate function
+ * when we add privacy (because read_token will be in pages too). */
 u32
 krb5_read_token(struct krb5_ctx *ctx,
 		struct xdr_netobj *read_token,
-		struct xdr_netobj *message_buffer,
+		struct xdr_buf *message_buffer,
 		int *qop_state, int toktype)
 {
 	int			signalg;
 	int			sealalg;
-	struct xdr_netobj	token = {.len = 0, .data = NULL};
 	s32			checksum_type;
 	struct xdr_netobj	md5cksum = {.len = 0, .data = NULL};
 	s32			now;
-	unsigned char		*plain = NULL;
-	int			plainlen = 0;
 	int			direction;
 	s32			seqnum;
 	unsigned char		*ptr = (unsigned char *)read_token->data;
@@ -100,10 +99,11 @@ krb5_read_token(struct krb5_ctx *ctx,
 	if (g_verify_token_header(&ctx->mech_used, &bodysize, &ptr, toktype,
 					read_token->len))
 		goto out;
+	/* XXX sanity-check bodysize?? */
 
 	if (toktype == KG_TOK_WRAP_MSG) {
-		message_buffer->len = 0;
-		message_buffer->data = NULL;
+		/* XXX gone */
+		goto out;
 	}
 
 	/* get the sign and seal algorithms */
@@ -135,43 +135,6 @@ krb5_read_token(struct krb5_ctx *ctx,
 	     signalg != SGN_ALG_HMAC_SHA1_DES3_KD))
 		goto out;
 
-	if (toktype == KG_TOK_WRAP_MSG) {
-		int conflen = crypto_tfm_alg_blocksize(ctx->enc);
-		int padlen;
-
-		plainlen = bodysize - (14 + KRB5_CKSUM_LENGTH);
-		plain = ptr + 14 + KRB5_CKSUM_LENGTH;
-
-		ret = krb5_decrypt(ctx->enc, NULL, plain, plain, plainlen);
-		if (ret)
-			goto out;
-
-		ret = GSS_S_FAILURE;
-		padlen = plain[plainlen -1];
-		if ((padlen < 1) || (padlen > 8))
-			goto out;
-		token.len = plainlen - conflen - padlen;
-
-		if (token.len) {
-			token.data = kmalloc(token.len, GFP_KERNEL);
-			if (token.data == NULL)
-				goto out;
-			memcpy(token.data, plain + conflen, token.len);
-		}
-
-	} else if (toktype == KG_TOK_MIC_MSG) {
-		token = *message_buffer;
-		plain = token.data;
-		plainlen = token.len;
-	} else {
-		printk("RPC: bad toktype in krb5_read_token");
-		ret = GSS_S_FAILURE;
-		goto out;
-	}
-
-	dprintk("RPC krb5_read_token: token.len %d plainlen %d\n", token.len,
-		plainlen);
-
 	/* compute the checksum of the message */
 
 	/* initialize the the cksum */
@@ -186,8 +149,8 @@ krb5_read_token(struct krb5_ctx *ctx,
 
 	switch (signalg) {
 	case SGN_ALG_DES_MAC_MD5:
-		ret = krb5_make_checksum(checksum_type, ptr - 2, plain,
-					 plainlen, &md5cksum);
+		ret = krb5_make_checksum(checksum_type, ptr - 2,
+					 message_buffer, &md5cksum);
 		if (ret)
 			goto out;
 
@@ -208,9 +171,6 @@ krb5_read_token(struct krb5_ctx *ctx,
 
 	/* it got through unscathed.  Make sure the context is unexpired */
 
-	if (toktype == KG_TOK_WRAP_MSG)
-		*message_buffer = token;
-
 	if (qop_state)
 		*qop_state = GSS_C_QOP_DEFAULT;
 
@@ -234,7 +194,5 @@ krb5_read_token(struct krb5_ctx *ctx,
 	ret = GSS_S_COMPLETE;
 out:
 	if (md5cksum.data) kfree(md5cksum.data);
-	if ((toktype == KG_TOK_WRAP_MSG) && ret && token.data)
-		kfree(token.data);
 	return ret;
 }
diff -puN net/sunrpc/auth_gss/gss_mech_switch.c~nfs-13-krb5_integ net/sunrpc/auth_gss/gss_mech_switch.c
--- 25/net/sunrpc/auth_gss/gss_mech_switch.c~nfs-13-krb5_integ	2004-01-09 22:16:15.000000000 -0800
+++ 25-akpm/net/sunrpc/auth_gss/gss_mech_switch.c	2004-01-09 22:16:15.000000000 -0800
@@ -196,7 +196,7 @@ gss_import_sec_context(struct xdr_netobj
 u32
 gss_get_mic(struct gss_ctx	*context_handle,
 	    u32			qop,
-	    struct xdr_netobj	*message,
+	    struct xdr_buf	*message,
 	    struct xdr_netobj	*mic_token)
 {
 	 return context_handle->mech_type->gm_ops
@@ -210,7 +210,7 @@ gss_get_mic(struct gss_ctx	*context_hand
 
 u32
 gss_verify_mic(struct gss_ctx		*context_handle,
-	       struct xdr_netobj	*message,
+	       struct xdr_buf		*message,
 	       struct xdr_netobj	*mic_token,
 	       u32			*qstate)
 {
diff -puN net/sunrpc/clnt.c~nfs-13-krb5_integ net/sunrpc/clnt.c
--- 25/net/sunrpc/clnt.c~nfs-13-krb5_integ	2004-01-09 22:16:15.000000000 -0800
+++ 25-akpm/net/sunrpc/clnt.c	2004-01-09 22:16:15.000000000 -0800
@@ -568,7 +568,8 @@ call_encode(struct rpc_task *task)
 		rpc_exit(task, -EIO);
 		return;
 	}
-	if (encode && (status = encode(req, p, task->tk_msg.rpc_argp)) < 0) {
+	if (encode && (status = rpcauth_wrap_req(task, encode, req, p,
+						 task->tk_msg.rpc_argp)) < 0) {
 		printk(KERN_WARNING "%s: can't encode arguments: %d\n",
 				clnt->cl_protname, -status);
 		rpc_exit(task, status);
@@ -827,7 +828,8 @@ call_decode(struct rpc_task *task)
 	task->tk_action = NULL;
 
 	if (decode)
-		task->tk_status = decode(req, p, task->tk_msg.rpc_resp);
+		task->tk_status = rpcauth_unwrap_resp(task, decode, req, p,
+						      task->tk_msg.rpc_resp);
 	dprintk("RPC: %4d call_decode result %d\n", task->tk_pid,
 					task->tk_status);
 	return;
diff -puN net/sunrpc/sunrpc_syms.c~nfs-13-krb5_integ net/sunrpc/sunrpc_syms.c
--- 25/net/sunrpc/sunrpc_syms.c~nfs-13-krb5_integ	2004-01-09 22:16:15.000000000 -0800
+++ 25-akpm/net/sunrpc/sunrpc_syms.c	2004-01-09 22:16:15.000000000 -0800
@@ -126,6 +126,9 @@ EXPORT_SYMBOL(xdr_inline_pages);
 EXPORT_SYMBOL(xdr_shift_buf);
 EXPORT_SYMBOL(xdr_write_pages);
 EXPORT_SYMBOL(xdr_read_pages);
+EXPORT_SYMBOL(xdr_buf_from_iov);
+EXPORT_SYMBOL(xdr_buf_subsegment);
+EXPORT_SYMBOL(xdr_buf_read_netobj);
 
 /* Debugging symbols */
 #ifdef RPC_DEBUG
diff -puN net/sunrpc/xdr.c~nfs-13-krb5_integ net/sunrpc/xdr.c
--- 25/net/sunrpc/xdr.c~nfs-13-krb5_integ	2004-01-09 22:16:15.000000000 -0800
+++ 25-akpm/net/sunrpc/xdr.c	2004-01-09 22:16:15.000000000 -0800
@@ -538,7 +538,7 @@ _copy_to_pages(struct page **pages, size
  * Copies data into an arbitrary memory location from an array of pages
  * The copy is assumed to be non-overlapping.
  */
-static void
+void
 _copy_from_pages(char *p, struct page **pages, size_t pgbase, size_t len)
 {
 	struct page **pgfrom;
@@ -731,3 +731,145 @@ xdr_read_pages(struct xdr_stream *xdr, u
 	xdr->p = (uint32_t *)((char *)iov->iov_base + padding);
 	xdr->end = (uint32_t *)((char *)iov->iov_base + iov->iov_len);
 }
+
+static struct iovec empty_iov = {.iov_base = NULL, .iov_len = 0};
+
+void
+xdr_buf_from_iov(struct iovec *iov, struct xdr_buf *buf)
+{
+	buf->head[0] = *iov;
+	buf->tail[0] = empty_iov;
+	buf->page_len = 0;
+	buf->len = iov->iov_len;
+}
+
+/* Sets subiov to the intersection of iov with the buffer of length len
+ * starting base bytes after iov.  Indicates empty intersection by setting
+ * length of subiov to zero.  Decrements len by length of subiov, sets base
+ * to zero (or decrements it by length of iov if subiov is empty). */
+static void
+iov_subsegment(struct iovec *iov, struct iovec *subiov, int *base, int *len)
+{
+	if (*base > iov->iov_len) {
+		subiov->iov_base = NULL;
+		subiov->iov_len = 0;
+		*base -= iov->iov_len;
+	} else {
+		subiov->iov_base = iov->iov_base + *base;
+		subiov->iov_len = min(*len, (int)iov->iov_len - *base);
+		*base = 0;
+	}
+	*len -= subiov->iov_len; 
+}
+
+/* Sets subbuf to the portion of buf of length len beginning base bytes
+ * from the start of buf. Returns -1 if base of length are out of bounds. */
+int
+xdr_buf_subsegment(struct xdr_buf *buf, struct xdr_buf *subbuf,
+			int base, int len)
+{
+	int i;
+
+	subbuf->len = len;
+	iov_subsegment(buf->head, subbuf->head, &base, &len);
+
+	if (base < buf->page_len) {
+		i = (base + buf->page_base) >> PAGE_CACHE_SHIFT;
+		subbuf->pages = &buf->pages[i];
+		subbuf->page_base = (base + buf->page_base) & ~PAGE_CACHE_MASK;
+		subbuf->page_len = min((int)buf->page_len - base, len);
+		len -= subbuf->page_len;
+		base = 0;
+	} else {
+		base -= buf->page_len;
+		subbuf->page_len = 0;
+	}
+
+	iov_subsegment(buf->tail, subbuf->tail, &base, &len);
+	if (base || len)
+		return -1;
+	return 0;
+}
+
+/* obj is assumed to point to allocated memory of size at least len: */
+static int
+read_bytes_from_xdr_buf(struct xdr_buf *buf, int base, void *obj, int len)
+{
+	struct xdr_buf subbuf;
+	int this_len;
+	int status;
+
+	status = xdr_buf_subsegment(buf, &subbuf, base, len);
+	if (status)
+		goto out;
+	this_len = min(len, (int)subbuf.head[0].iov_len);
+	memcpy(obj, subbuf.head[0].iov_base, this_len);
+	len -= this_len;
+	obj += this_len;
+	this_len = min(len, (int)subbuf.page_len);
+	if (this_len)
+		_copy_from_pages(obj, subbuf.pages, subbuf.page_base, this_len);
+	len -= this_len;
+	obj += this_len;
+	this_len = min(len, (int)subbuf.tail[0].iov_len);
+	memcpy(obj, subbuf.tail[0].iov_base, this_len);
+out:
+	return status;
+}
+
+static int
+read_u32_from_xdr_buf(struct xdr_buf *buf, int base, u32 *obj)
+{
+	u32	raw;
+	int	status;
+
+	status = read_bytes_from_xdr_buf(buf, base, &raw, sizeof(*obj));
+	if (status)
+		return status;
+	*obj = ntohl(raw);
+	return 0;
+}
+
+/* If the netobj starting offset bytes from the start of xdr_buf is contained
+ * entirely in the head or the tail, set object to point to it; otherwise
+ * try to find space for it at the end of the tail, copy it there, and
+ * set obj to point to it. */
+int
+xdr_buf_read_netobj(struct xdr_buf *buf, struct xdr_netobj *obj, int offset)
+{
+	u32	tail_offset = buf->head[0].iov_len + buf->page_len;
+	u32	obj_end_offset;
+
+	if (read_u32_from_xdr_buf(buf, offset, &obj->len))
+		goto out;
+	obj_end_offset = offset + 4 + obj->len;
+
+	if (obj_end_offset <= buf->head[0].iov_len) {
+		/* The obj is contained entirely in the head: */
+		obj->data = buf->head[0].iov_base + offset + 4;
+	} else if (offset + 4 >= tail_offset) {
+		if (obj_end_offset - tail_offset
+				> buf->tail[0].iov_len)
+			goto out;
+		/* The obj is contained entirely in the tail: */
+		obj->data = buf->tail[0].iov_base
+			+ offset - tail_offset + 4;
+	} else {
+		/* use end of tail as storage for obj:
+		 * (We don't copy to the beginning because then we'd have
+		 * to worry about doing a potentially overlapping copy.
+		 * This assumes the object is at most half the length of the
+		 * tail.) */
+		if (obj->len > buf->tail[0].iov_len)
+			goto out;
+		obj->data = buf->tail[0].iov_base + buf->tail[0].iov_len - 
+				obj->len;
+		if (read_bytes_from_xdr_buf(buf, offset + 4,
+					obj->data, obj->len))
+			goto out;
+
+	}
+	return 0;
+out:
+	return -1;
+}

_