Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2d2da60c authored by J. Bruce Fields's avatar J. Bruce Fields Committed by Trond Myklebust
Browse files

RPCSEC_GSS: client-side privacy support



 Add the code to the client side to handle privacy.  This is dead code until
 we actually add privacy support to krb5.

 Signed-off-by: default avatarJ. Bruce Fields <bfields@citi.umich.edu>
 Signed-off-by: default avatarTrond Myklebust <Trond.Myklebust@netapp.com>
parent 24b2605b
Loading
Loading
Loading
Loading
+148 −1
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/pagemap.h>
#include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/auth.h>
#include <linux/sunrpc/auth_gss.h>
@@ -975,6 +976,114 @@ gss_wrap_req_integ(struct rpc_cred *cred, struct gss_cl_ctx *ctx,
	return 0;
}

static void
priv_release_snd_buf(struct rpc_rqst *rqstp)
{
	int i;

	for (i=0; i < rqstp->rq_enc_pages_num; i++)
		__free_page(rqstp->rq_enc_pages[i]);
	kfree(rqstp->rq_enc_pages);
}

static int
alloc_enc_pages(struct rpc_rqst *rqstp)
{
	struct xdr_buf *snd_buf = &rqstp->rq_snd_buf;
	int first, last, i;

	if (snd_buf->page_len == 0) {
		rqstp->rq_enc_pages_num = 0;
		return 0;
	}

	first = snd_buf->page_base >> PAGE_CACHE_SHIFT;
	last = (snd_buf->page_base + snd_buf->page_len - 1) >> PAGE_CACHE_SHIFT;
	rqstp->rq_enc_pages_num = last - first + 1 + 1;
	rqstp->rq_enc_pages
		= kmalloc(rqstp->rq_enc_pages_num * sizeof(struct page *),
				GFP_NOFS);
	if (!rqstp->rq_enc_pages)
		goto out;
	for (i=0; i < rqstp->rq_enc_pages_num; i++) {
		rqstp->rq_enc_pages[i] = alloc_page(GFP_NOFS);
		if (rqstp->rq_enc_pages[i] == NULL)
			goto out_free;
	}
	rqstp->rq_release_snd_buf = priv_release_snd_buf;
	return 0;
out_free:
	for (i--; i >= 0; i--) {
		__free_page(rqstp->rq_enc_pages[i]);
	}
out:
	return -EAGAIN;
}

static inline int
gss_wrap_req_priv(struct rpc_cred *cred, struct gss_cl_ctx *ctx,
		kxdrproc_t encode, struct rpc_rqst *rqstp, u32 *p, void *obj)
{
	struct xdr_buf	*snd_buf = &rqstp->rq_snd_buf;
	u32		offset;
	u32             maj_stat;
	int		status;
	u32		*opaque_len;
	struct page	**inpages;
	int		first;
	int		pad;
	struct kvec	*iov;
	char		*tmp;

	opaque_len = p++;
	offset = (u8 *)p - (u8 *)snd_buf->head[0].iov_base;
	*p++ = htonl(rqstp->rq_seqno);

	status = encode(rqstp, p, obj);
	if (status)
		return status;

	status = alloc_enc_pages(rqstp);
	if (status)
		return status;
	first = snd_buf->page_base >> PAGE_CACHE_SHIFT;
	inpages = snd_buf->pages + first;
	snd_buf->pages = rqstp->rq_enc_pages;
	snd_buf->page_base -= first << PAGE_CACHE_SHIFT;
	/* Give the tail its own page, in case we need extra space in the
	 * head when wrapping: */
	if (snd_buf->page_len || snd_buf->tail[0].iov_len) {
		tmp = page_address(rqstp->rq_enc_pages[rqstp->rq_enc_pages_num - 1]);
		memcpy(tmp, snd_buf->tail[0].iov_base, snd_buf->tail[0].iov_len);
		snd_buf->tail[0].iov_base = tmp;
	}
	maj_stat = gss_wrap(ctx->gc_gss_ctx, GSS_C_QOP_DEFAULT, offset,
				snd_buf, inpages);
	/* RPC_SLACK_SPACE should prevent this ever happening: */
	BUG_ON(snd_buf->len > snd_buf->buflen);
        status = -EIO;
	/* We're assuming that when GSS_S_CONTEXT_EXPIRED, the encryption was
	 * done anyway, so it's safe to put the request on the wire: */
	if (maj_stat == GSS_S_CONTEXT_EXPIRED)
		cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE;
	else if (maj_stat)
		return status;

	*opaque_len = htonl(snd_buf->len - offset);
	/* 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;
	pad = 3 - ((snd_buf->len - offset - 1) & 3);
	memset(p, 0, pad);
	iov->iov_len += pad;
	snd_buf->len += pad;

	return 0;
}

static int
gss_wrap_req(struct rpc_task *task,
	     kxdrproc_t encode, void *rqstp, u32 *p, void *obj)
@@ -1002,6 +1111,8 @@ gss_wrap_req(struct rpc_task *task,
								rqstp, p, obj);
			break;
       		case RPC_GSS_SVC_PRIVACY:
			status = gss_wrap_req_priv(cred, ctx, encode,
					rqstp, p, obj);
			break;
	}
out:
@@ -1048,6 +1159,36 @@ gss_unwrap_resp_integ(struct rpc_cred *cred, struct gss_cl_ctx *ctx,
	return 0;
}

static inline int
gss_unwrap_resp_priv(struct rpc_cred *cred, struct gss_cl_ctx *ctx,
		struct rpc_rqst *rqstp, u32 **p)
{
	struct xdr_buf  *rcv_buf = &rqstp->rq_rcv_buf;
	u32 offset;
	u32 opaque_len;
	u32 maj_stat;
	int status = -EIO;

	opaque_len = ntohl(*(*p)++);
	offset = (u8 *)(*p) - (u8 *)rcv_buf->head[0].iov_base;
	if (offset + opaque_len > rcv_buf->len)
		return status;
	/* remove padding: */
	rcv_buf->len = offset + opaque_len;

	maj_stat = gss_unwrap(ctx->gc_gss_ctx, NULL,
			offset, rcv_buf);
	if (maj_stat == GSS_S_CONTEXT_EXPIRED)
		cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE;
	if (maj_stat != GSS_S_COMPLETE)
		return status;
	if (ntohl(*(*p)++) != rqstp->rq_seqno)
		return status;

	return 0;
}


static int
gss_unwrap_resp(struct rpc_task *task,
		kxdrproc_t decode, void *rqstp, u32 *p, void *obj)
@@ -1057,6 +1198,8 @@ gss_unwrap_resp(struct rpc_task *task,
			gc_base);
	struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred);
	u32		*savedp = p;
	struct kvec	*head = ((struct rpc_rqst *)rqstp)->rq_rcv_buf.head;
	int		savedlen = head->iov_len;
	int             status = -EIO;

	if (ctx->gc_proc != RPC_GSS_PROC_DATA)
@@ -1070,10 +1213,14 @@ gss_unwrap_resp(struct rpc_task *task,
				goto out;
			break;
       		case RPC_GSS_SVC_PRIVACY:
			status = gss_unwrap_resp_priv(cred, ctx, rqstp, &p);
			if (status)
				goto out;
			break;
	}
	/* take into account extra slack for integrity and privacy cases: */
	task->tk_auth->au_rslack = task->tk_auth->au_verfsize + (p - savedp);
	task->tk_auth->au_rslack = task->tk_auth->au_verfsize + (p - savedp)
						+ (savedlen - head->iov_len);
out_decode:
	status = decode(rqstp, p, obj);
out: