Fix several cases where a padded len isn't checked in ticket decode in kernel-net-rxrpc CVE-2017-7482

Tracked by Jolla (Rejected)

asked 2017-12-20 00:04:20 +0200

this post is marked as community wiki

This post is a wiki. Anyone with karma >75 is welcome to improve it.

updated 2017-12-20 00:04:20 +0200

lpr gravatar image

When a kerberos 5 ticket is being decoded so that it can be loaded into an rxrpc-type key, there are several places in which the length of a variable-length field is checked to make sure that it's not going to overrun the available data - but the data is padded to the nearest four-byte boundary and the code doesn't check for this extra. This could lead to the size-remaining variable wrapping and the data pointer going over the end of the buffer.

Fix this by making the various variable-length data checks use the padded length.

Kernel-3.2 Patch is available [bwh: Backported to 3.2: adjust filename, context]

File affected: kernel-adaptation-sbj-3.4.108.20161101.1/net/rxrpc/ar-key.c

So the whole Patch should look like:

@@ -213,7 +213,7 @@ static int rxrpc_krb5_decode_principal(struct krb5_principal *princ,
                   unsigned *_toklen)
 {
const __be32 *xdr = *_xdr;
-   unsigned toklen = *_toklen, n_parts, loop, tmp;
+   unsigned int toklen = *_toklen, n_parts, loop, tmp, paddedlen;

/* there must be at least one name, and at least #names+1 length
 * words */
@@ -243,16 +243,16 @@ static int rxrpc_krb5_decode_principal(struct krb5_principal *princ,
    toklen -= 4;
    if (tmp <= 0 || tmp > AFSTOKEN_STRING_MAX)
        return -EINVAL;
-       if (tmp > toklen)
+       paddedlen = (tmp + 3) & ~3;
+       if (paddedlen > toklen)
        return -EINVAL;
    princ->name_parts[loop] = kmalloc(tmp + 1, GFP_KERNEL);
    if (!princ->name_parts[loop])
        return -ENOMEM;
    memcpy(princ->name_parts[loop], xdr, tmp);
    princ->name_parts[loop][tmp] = 0;
-       tmp = (tmp + 3) & ~3;
-       toklen -= tmp;
-       xdr += tmp >> 2;
+       toklen -= paddedlen;
+       xdr += paddedlen >> 2;
}

if (toklen < 4)
@@ -261,16 +261,16 @@ static int rxrpc_krb5_decode_principal(struct krb5_principal *princ,
toklen -= 4;
if (tmp <= 0 || tmp > AFSTOKEN_K5_REALM_MAX)
    return -EINVAL;
-   if (tmp > toklen)
+   paddedlen = (tmp + 3) & ~3;
+   if (paddedlen > toklen)
    return -EINVAL;
princ->realm = kmalloc(tmp + 1, GFP_KERNEL);
if (!princ->realm)
    return -ENOMEM;
memcpy(princ->realm, xdr, tmp);
princ->realm[tmp] = 0;
-   tmp = (tmp + 3) & ~3;
-   toklen -= tmp;
-   xdr += tmp >> 2;
+   toklen -= paddedlen;
+   xdr += paddedlen >> 2;

_debug("%s/...@%s", princ->name_parts[0], princ->realm);

@@ -289,7 +289,7 @@ static int rxrpc_krb5_decode_tagged_data(struct krb5_tagged_data *td,
                 unsigned *_toklen)
 {
const __be32 *xdr = *_xdr;
-   unsigned toklen = *_toklen, len;
+   unsigned int toklen = *_toklen, len, paddedlen;

/* there must be at least one tag and one length word */
if (toklen <= 8)
@@ -303,6 +303,9 @@ static int rxrpc_krb5_decode_tagged_data(struct krb5_tagged_data *td,
toklen -= 8;
if (len > max_data_size)
    return -EINVAL;
+   paddedlen = (len + 3) & ~3;
+   if (paddedlen > toklen)
+       return -EINVAL;
td->data_len = len;

if (len > 0) {
@@ -310,9 +313,8 @@ static int rxrpc_krb5_decode_tagged_data(struct krb5_tagged_data *td,
    if (!td->data)
        return -ENOMEM;
    memcpy(td->data, xdr, len);
-       len = (len + 3) & ~3;
-       toklen -= len;
-       xdr += len >> 2;
+       toklen -= paddedlen;
+       xdr += paddedlen >> 2;
}

_debug("tag %x len %x", td->tag, td->data_len);
@@ -383,7 +385,7 @@ static int rxrpc_krb5_decode_ticket(u8 **_ticket, u16 *_tktlen,
                const __be32 **_xdr, unsigned *_toklen)
 {
const __be32 *xdr = *_xdr;
-   unsigned toklen = *_toklen, len;
+   unsigned int toklen = *_toklen, len, paddedlen;

/* there must be at least one length word */
if (toklen <= 4)
@@ -395,6 +397,9 @@ static int rxrpc_krb5_decode_ticket(u8 **_ticket, u16 *_tktlen,
toklen -= 4;
if (len > AFSTOKEN_K5_TIX_MAX)
    return -EINVAL;
+   paddedlen = (len + 3) & ~3;
+   if (paddedlen > toklen)
+       return -EINVAL;
*_tktlen = len;

_debug("ticket len %u", len);
@@ -404,9 +409,8 @@ static int rxrpc_krb5_decode_ticket(u8 **_ticket, u16 *_tktlen,
    if (!*_ticket)
        return -ENOMEM;
    memcpy(*_ticket, xdr, len);
-       len = (len + 3) & ~3;
-       toklen -= len;
-       xdr += len >> 2;
+       toklen -= paddedlen;
+       xdr += paddedlen >> 2;
}

*_xdr = xdr;
@@ -549,7 +553,7 @@ static int rxrpc_instantiate_xdr(struct key *key, const void *data, size_t datal
 {
const __be32 *xdr = data, *token;
const char *cp;
-   unsigned len, tmp, loop, ntoken, toklen, sec_ix;
+   unsigned int len, paddedlen, loop, ntoken, toklen, sec_ix;
int ret;

_enter(",{%x,%x,%x,%x},%zu",
@@ -574,22 +578,21 @@ static int rxrpc_instantiate_xdr(struct key *key, const void *data, size_t datal
if (len < 1 || len > AFSTOKEN_CELL_MAX)
    goto not_xdr;
datalen -= 4;
-   tmp = (len + 3) & ~3;
-   if (tmp > datalen)
+   paddedlen = (len + 3) & ~3;
+   if (paddedlen > datalen)
    goto not_xdr;

cp = (const char *) xdr;
for (loop = 0; loop < len; loop++)
    if (!isprint(cp[loop]))
        goto not_xdr;
-   if (len < tmp)
-       for (; loop < tmp; loop++)
-           if (cp[loop])
-               goto not_xdr;
+   for (; loop < paddedlen; loop++)
+       if (cp[loop])
+           goto not_xdr;
_debug("cellname: [%u/%u] '%*.*s'",
-          len, tmp, len, len, (const char *) xdr);
-   datalen -= tmp;
-   xdr += tmp >> 2;
+          len, paddedlen, len, len, (const char *) xdr);
+   datalen -= paddedlen;
+   xdr += paddedlen >> 2;

/* get the token count */
if (datalen < 12)
@@ -610,10 +613,11 @@ static int rxrpc_instantiate_xdr(struct key *key, const void *data, size_t datal
    sec_ix = ntohl(*xdr);
    datalen -= 4;
    _debug("token: [%x/%zx] %x", toklen, datalen, sec_ix);
-       if (toklen < 20 || toklen > datalen)
+       paddedlen = (toklen + 3) & ~3;
+       if (toklen < 20 || toklen > datalen || paddedlen > datalen)
        goto not_xdr;
-       datalen -= (toklen + 3) & ~3;
-       xdr += (toklen + 3) >> 2;
+       datalen -= paddedlen;
+       xdr += paddedlen >> 2;

} while (--loop > 0);
edit retag flag offensive close delete