|
@@ -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:
|