]> git.hungrycats.org Git - linux/commitdiff
RPCSEC_GSS: Client-side only support for rpcsec_gss integrity
authorTrond Myklebust <trond.myklebust@fys.uio.no>
Sat, 7 Feb 2004 15:44:01 +0000 (16:44 +0100)
committerTrond Myklebust <trond.myklebust@fys.uio.no>
Sat, 7 Feb 2004 15:44:01 +0000 (16:44 +0100)
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_bufs instead of xdr_netobjs 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.

14 files changed:
include/linux/sunrpc/auth.h
include/linux/sunrpc/gss_api.h
include/linux/sunrpc/gss_krb5.h
include/linux/sunrpc/xdr.h
net/sunrpc/auth.c
net/sunrpc/auth_gss/auth_gss.c
net/sunrpc/auth_gss/gss_krb5_crypto.c
net/sunrpc/auth_gss/gss_krb5_mech.c
net/sunrpc/auth_gss/gss_krb5_seal.c
net/sunrpc/auth_gss/gss_krb5_unseal.c
net/sunrpc/auth_gss/gss_mech_switch.c
net/sunrpc/clnt.c
net/sunrpc/sunrpc_syms.c
net/sunrpc/xdr.c

index 70cc3360e608c7a1b882216efa28c25a176224c3..a68f18bf0a4647e04cd7f34483e0b74f5a176e05 100644 (file)
@@ -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 *);
index 35988e7bfb770484d5486014edcac337f89817d6..cbb60ac22fd483cfa7f053ed990d2acdb58487e9 100644 (file)
@@ -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)(
index aac2ad4f7d569ffeadea7ea4ce4c49d6b63cdf2b..9616746407f3963b8f4f33e6b3a63d1a7684b530 100644 (file)
@@ -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
index 2c6f76d1cc14d4eaafb72fb446ba6ffa3c7485bb..8082a00291007671ac595b3460604f2d9bbe0b5c 100644 (file)
@@ -141,6 +141,10 @@ void xdr_shift_iovec(struct iovec *, int, size_t);
 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.
index f1a73646e6ed554cc00bfc129c22166206cea8d6..a89b30cadff3b93fe73646f7f358f38bfee76c1e 100644 (file)
@@ -339,6 +339,35 @@ rpcauth_checkverf(struct rpc_task *task, u32 *p)
        return cred->cr_ops->crvalidate(task, p);
 }
 
+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)
 {
index 61618c6bed5e68e63bf4060df28310c1e7432308..1a4b9f504aa22001a98401cc70e9fc75a0084964 100644 (file)
@@ -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 *p, int ruid)
        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, int ruid)
        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 = {
index 9894d83ddf6f612bfa265825a295c5c8619d174f..5a5f859ad6280d8a778549aea57c66f4ba9096b9 100644 (file)
@@ -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 *ptr, int len) {
 /* 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 *header, char *body, int body_len,
        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:
index 61282b4d9c3b9faf9a478734b0c5ee4d6f35f21d..9913dac0f415d7367297d19beedc741aa7589e13 100644 (file)
@@ -183,7 +183,7 @@ gss_delete_sec_context_kerberos(void *internal_ctx) {
 
 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            *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(void)
                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);
 }
 
index eaf19d7c8e258cdc86b710d25c6fc62f15d472f5..52d4e78e117fea040d338831e91f49035d8bb7bd 100644 (file)
@@ -80,7 +80,7 @@ gss_krb5_padding(int blocksize, int length) {
 
 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, int qop_req,
                *(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;
        }
 
index 8b2795d701db112015dfbfd71a0edc627b640ff7..0e1c7f70f841f5b5af6a9e81a098da8bbbd3ac83 100644 (file)
  *   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;
 }
index b384cae37052e573185427d4cbdf39769b33bb1b..b360460defcd0aec4418fadc863af81d490c6f50 100644 (file)
@@ -196,7 +196,7 @@ gss_import_sec_context(struct xdr_netobj    *input_token,
 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_handle,
 
 u32
 gss_verify_mic(struct gss_ctx          *context_handle,
-              struct xdr_netobj        *message,
+              struct xdr_buf           *message,
               struct xdr_netobj        *mic_token,
               u32                      *qstate)
 {
index badaa121f29c409844a7d255471a792fd3dafcb4..cc4bfb201807d196f87701983841c7d3695b5dbb 100644 (file)
@@ -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;
index ff8d2bb7bb1865b7414561afd114787947d72791..f6bde71024e5bcdbd8e520fddae7a481c5dc7831 100644 (file)
@@ -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
index 078dad8a90e5c7668254446d3cd0f6bffa26ccda..00e0082704ba9b3ed1cf3a8d9081ad0d4010e1af 100644 (file)
@@ -538,7 +538,7 @@ _copy_to_pages(struct page **pages, size_t pgbase, const char *p, size_t len)
  * 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, unsigned int len)
        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;
+}