From: "J. Bruce Fields" <bfields@fieldses.org>
To: Kevin Coffman <kwc@umich.edu>
Cc: NFS list <linux-nfs@vger.kernel.org>, Steve Dickson <SteveD@redhat.com>
Subject: Re: RFC: Handling GSS rotated data
Date: Tue, 10 Apr 2012 19:02:03 -0400 [thread overview]
Message-ID: <20120410230202.GM18465@fieldses.org> (raw)
In-Reply-To: <20120410215633.GL18465@fieldses.org>
On Tue, Apr 10, 2012 at 05:56:33PM -0400, bfields wrote:
> On Wed, Apr 04, 2012 at 06:28:47PM -0400, Kevin Coffman wrote:
> > This bug points out a deficiency in the GSS code previously added to the kernel:
> >
> > https://bugzilla.redhat.com/show_bug.cgi?id=796992
> >
> > The spec (RFC 4121 section 4.2.5) says, "The receiver MUST be able to
> > interpret all possible rotation count values, including rotation
> > counts greater than the length of the token." Note that an
> > implementation is never required to send rotated data. However it is
> > required to be able to handle receiving rotated data. Windows is the
> > only implementation that I am aware of that currently sends tokens
> > with rotated data.
> >
> > Attached is a patch (with way too much debugging) to handle the
> > rotated data we have seen from Microsoft clients. Admittedly, it does
> > not handle all cases, which is required to be fully compliant with the
> > spec. I will not have the time to devote to making it fully
> > compliant. I submit this patch as an RFC and for someone else to
> > complete! Also note that I may have over-complicating things!!
>
> Thanks!
>
> But I'm not going to agree to implementing another subset of the
> cases--we already fell into that trap once, let's not do it again.
>
> Even if it means we have to do something slow and stupid as the first
> pass, I'd rather have something complete....
By the way, did the cases you saw form microsoft all have a small
rotation value?
So, one approach xdr_buf_subsegment() can calculate a new xdr_buf that
has the "correct" head, tail, and page lengths (making
realhead/realpage/realtail unnecessary).
And read_bytes_from_xdr_buf()/write_bytes_from_xdr_buf() already know
how to do scatter/gather copies from/to an xdr_buf.
And then we can use those to repeatedly shift by up to LOCAL_BUF_LEN
bytes until we've rotated the right amount. Assuming the typical case
is a small (less than LOCAL_BUF_LEN) rotation value, that will only
require one pass. So maybe it's not too horrible.
So, something like:
rrc = rrc % buf->len;
xdr_buf_subsegment(buf, subbuf, offset, buf->len);
while (shifted < rrc) {
this_shift = min(rrc, LOCAL_BUF_LEN);
shift_buf_a_little(subbuf, this_shift);
shifted += this_shift;
}
void shift_buf_a_little(buf, shift)
{
char head[LOCAL_BUF_LEN];
BUG_ON(shift > LOCAL_BUF_LEN);
read_bytes_from_xdr_buf(buf, 0, head, shift);
for (i=0; i + shift < buf->len; i += shift) {
char tmp[LOCAL_BUF_LEN];
this_len = min(shift, buf->len - i);
read_bytes_from_xdr_buf(buf, i+shift, tmp, this_len);
write_bytes_to_xdr_buf(buf, i, tmp, this_len);
}
write_bytes_to_xdr_buf(buf, buf->len - shift, head, shift);
}
?
Uh, but I'm not certain of that second function.
--b.
>
> --b.
>
> diff --git a/net/sunrpc/auth_gss/gss_krb5_wrap.c b/net/sunrpc/auth_gss/gss_krb5_wrap.c
> index 2763e3e..b824067 100644
> --- a/net/sunrpc/auth_gss/gss_krb5_wrap.c
> +++ b/net/sunrpc/auth_gss/gss_krb5_wrap.c
> @@ -384,7 +384,6 @@ gss_unwrap_kerberos_v1(struct krb5_ctx *kctx, int offset, struct xdr_buf *buf)
> * We cannot currently handle tokens with rotated data. We need a
> * generalized routine to rotate the data in place. It is anticipated
> * that we won't encounter rotated data in the general case.
> - */
> static u32
> rotate_left(struct krb5_ctx *kctx, u32 offset, struct xdr_buf *buf, u16 rrc)
> {
> @@ -397,6 +396,139 @@ rotate_left(struct krb5_ctx *kctx, u32 offset, struct xdr_buf *buf, u16 rrc)
> "rrc %u, realrrc %u\n", __func__, rrc, realrrc);
> return 1;
> }
> + */
> +
> +#include <linux/ctype.h>
> +void print_xdrbuf(const char *msg, struct xdr_buf *x);
> +void print_xdrbuf_data(const char *msg, struct xdr_buf *x, unsigned int offset);
> +
> +#define LOCAL_BUF_LEN 32
> +
> +static u32
> +rotate_left(struct krb5_ctx *kctx, u32 offset, struct xdr_buf *buf, u16 rrc)
> +{
> + unsigned int realhead, realpage, realtail;
> + unsigned int remainder;
> + unsigned int rot_len = buf->len - offset - 16;
> + unsigned int realrrc = rrc % rot_len;
> + struct kvec *head, *tail;
> + void *headstart; /* start of data past rpc header */
> + u32 err;
> +
> + printk("%s: rrc %u, buf->len %u, offset %u, rot_len %u, realrrc %u\n",
> + __func__, rrc, buf->len, offset, rot_len, realrrc);
> + print_xdrbuf("in rotate_left", buf);
> + print_xdrbuf_data("BEFORE: ", buf, offset);
> +
> + if (realrrc == 0)
> + return 0;
> +
> + /*
> + * At the time this function is called, the only length
> + * values we can absolutely trust are buf->len (the length
> + * of data actually read off the network) and buf->buflen
> + * (the total available buffer space).
> + *
> + * The head, tail, and page lengths are the available
> + * buffer space for each, not how much data is actually
> + * available in each. We know that data is read to fill
> + * up the head, then page data, and then tail; so we can
> + * calculate the *real* lengths of each.
> + */
> + realhead = realpage = realtail = 0;
> + remainder = buf->len;
> + head = &buf->head[0];
> + tail = &buf->tail[0];
> +
> + if (remainder) {
> + realhead = min_t(unsigned int, buf->len, head->iov_len);
> + remainder -= realhead;
> + }
> + if (remainder) {
> + realpage = min_t(unsigned int, remainder, buf->page_len);
> + remainder -= realpage;
> + }
> + if (remainder) {
> + realtail = min_t(unsigned int, remainder, tail->iov_len);
> + remainder -= realtail;
> + }
> + BUG_ON(remainder);
> + printk("%s: realhead %u, realpage %u, realtail %u\n",
> + __func__, realhead, realpage, realtail);
> +
> + headstart = head->iov_base + offset + 16;
> +
> + if (realpage == 0 && realtail == 0) {
> + /*
> + * All the data is in the head. Therefore all
> + * the data to be moved is in the head
> + */
> + char localbuf[LOCAL_BUF_LEN];
> + unsigned int amtleft = realrrc;
> + unsigned int thismove;
> + printk("%s: We are lucky! (moving %u within head)\n",
> + __func__, realrrc);
> + while (amtleft != 0) {
> + thismove = min_t(unsigned int, amtleft, LOCAL_BUF_LEN);
> + memcpy(localbuf, headstart, thismove);
> + memmove(headstart, headstart + thismove,
> + realhead - offset - 16 - thismove);
> + memcpy(head->iov_base + realhead - thismove,
> + localbuf, thismove);
> + amtleft -= thismove;
> + }
> + } else if (realhead - offset - 16 < realrrc) {
> + /*
> + * The part to be moved is split between the head and
> + * page data or tail
> + */
> + printk("%s: We are not lucky. All the realrrc data (%u) "
> + " is not in the head!\n", __func__, realrrc);
> + return 1;
> + } else if (realtail != 0 && tail->iov_len - realtail >= realrrc) {
> + /*
> + * We are lucky enough to simply move rrc bytes
> + * from the head to the end of the tail
> + */
> + printk("%s: We are lucky! (moving %u from head to tail)\n",
> + __func__, realrrc);
> + memcpy(tail->iov_base + realtail,
> + head->iov_base + offset + 16,
> + realrrc);
> + tail->iov_len = realtail + realrrc;
> + memmove(headstart, headstart + realrrc,
> + realhead - offset - 16 - realrrc);
> + head->iov_len -= realrrc;
> + } else if (realpage != 0 && realtail == 0) {
> + /*
> + * There is page data and no existing tail
> + */
> + printk("%s: We are lucky! (moving %u from head to pages)\n",
> + __func__, realrrc);
> + buf->page_len += realrrc;
> + err = write_bytes_to_xdr_buf(buf, realhead + realpage,
> + headstart, realrrc);
> + if (err) {
> + printk("%s: We are not lucky getting err %d while "
> + "writing %u bytes to xdr_buf\n",
> + __func__, err, realrrc);
> + return 1;
> + }
> + memmove(headstart, headstart + realrrc,
> + realhead - offset - 16 - realrrc);
> + head->iov_len -= realrrc;
> + } else {
> + /*
> + * None of the quick fixes works. Do it the "hard way".
> + */
> + printk("%s: We are not lucky with realrrc %u\n",
> + __func__, realrrc);
> + return 1;
> + }
> +
> + print_xdrbuf_data("AFTER : ", buf, offset);
> + return 0;
> +}
>
> static u32
> gss_wrap_kerberos_v2(struct krb5_ctx *kctx, u32 offset,
> @@ -585,3 +717,74 @@ gss_unwrap_kerberos(struct gss_ctx *gctx, int offset, struct xdr_buf *buf)
> }
> }
>
> +void
> +print_hexl(const char *title, u8 *p, u_int length, u_int offset)
> +{
> + u_int i, j, jm;
> + u8 c, *cp;
> +
> + printk("RPC: %s: length %d: %p\n", title, length, p);
> + cp = (u8 *) p;
> +
> + for (i = 0; i < length; i += 0x10) {
> + printk(" %04x: ", (u_int)(i + offset));
> + jm = length - i;
> + jm = jm > 16 ? 16 : jm;
> +
> + for (j = 0; j < jm; j++) {
> + if ((j % 2) == 1)
> + printk("%02x ", (u_int)cp[i+j]);
> + else
> + printk("%02x", (u_int)cp[i+j]);
> + }
> + for (; j < 16; j++) {
> + if ((j % 2) == 1)
> + printk(" ");
> + else
> + printk(" ");
> + }
> + printk(" ");
> +
> + for (j = 0; j < jm; j++) {
> + c = cp[i+j];
> + c = isprint(c) ? c : '.';
> + printk("%c", c);
> + }
> + printk("\n");
> + }
> + printk("\n");
> +}
> +EXPORT_SYMBOL(print_hexl);
> +
> +void print_xdrbuf(const char *msg, struct xdr_buf *x)
> +{
> + printk("---------- xdr_buf at %p: %s ----------\n", x, msg);
> + printk(" head[0].iov_base %p, iov_len %zu\n",
> + x->head[0].iov_base, x->head[0].iov_len);
> + printk(" tail[0].iov_base %p, iov_len %zu\n",
> + x->tail[0].iov_base, x->tail[0].iov_len);
> + printk(" pages %p\n", x->pages);
> + printk(" page_base %u, page_len %u, flags %u\n",
> + x->page_base, x->page_len, x->flags);
> + printk(" buflen %u, len %u\n", x->buflen, x->len);
> +}
> +EXPORT_SYMBOL(print_xdrbuf);
> +
> +//extern void print_hexl(const char *title, u8 *p, u_int length, u_int offset);
> +void print_xdrbuf_data(const char *msg, struct xdr_buf *x, unsigned int offset)
> +{
> + printk("========== xdr_buf %p offset %u: %s ==========\n",
> + x, offset, msg);
> + if (offset < x->head[0].iov_len)
> + print_hexl("head data", x->head[0].iov_base + offset,
> + x->head[0].iov_len - offset, 0);
> + if (x->page_len) {
> + int plen = (x->page_len > 512) ? 512 : x->page_len;
> + print_hexl("page data",
> + page_address(x->pages[0]) + x->page_base, plen, 0);
> + }
> + if (x->tail[0].iov_base != NULL && x->tail[0].iov_len != 0)
> + print_hexl("tail data", x->tail[0].iov_base,
> + x->tail[0].iov_len, 0);
> +}
> +EXPORT_SYMBOL(print_xdrbuf_data);
next prev parent reply other threads:[~2012-04-10 23:02 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2012-04-04 22:28 RFC: Handling GSS rotated data Kevin Coffman
2012-04-10 21:56 ` J. Bruce Fields
2012-04-10 23:02 ` J. Bruce Fields [this message]
2012-04-11 0:56 ` Kevin Coffman
2012-04-12 0:12 ` J. Bruce Fields
2012-04-12 0:19 ` J. Bruce Fields
2012-05-01 23:52 ` J. Bruce Fields
2012-05-02 22:08 ` J. Bruce Fields
2012-04-12 1:06 ` Jim Rees
2012-04-12 16:46 ` J. Bruce Fields
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20120410230202.GM18465@fieldses.org \
--to=bfields@fieldses.org \
--cc=SteveD@redhat.com \
--cc=kwc@umich.edu \
--cc=linux-nfs@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).