[funini.com] -> [kei@sodan] -> Kernel Reading

root/fs/cifs/asn1.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. asn1_open
  2. asn1_octet_decode
  3. asn1_tag_decode
  4. asn1_id_decode
  5. asn1_length_decode
  6. asn1_header_decode
  7. asn1_eoc_decode
  8. asn1_subid_decode
  9. asn1_oid_decode
  10. compare_oid
  11. decode_negTokenInit

/*
 * The ASB.1/BER parsing code is derived from ip_nat_snmp_basic.c which was in
 * turn derived from the gxsnmp package by Gregory McLean & Jochen Friedrich
 *
 * Copyright (c) 2000 RP Internet (www.rpi.net.au).
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 */

#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include "cifspdu.h"
#include "cifsglob.h"
#include "cifs_debug.h"
#include "cifsproto.h"

/*****************************************************************************
 *
 * Basic ASN.1 decoding routines (gxsnmp author Dirk Wisse)
 *
 *****************************************************************************/

/* Class */
#define ASN1_UNI        0       /* Universal */
#define ASN1_APL        1       /* Application */
#define ASN1_CTX        2       /* Context */
#define ASN1_PRV        3       /* Private */

/* Tag */
#define ASN1_EOC        0       /* End Of Contents or N/A */
#define ASN1_BOL        1       /* Boolean */
#define ASN1_INT        2       /* Integer */
#define ASN1_BTS        3       /* Bit String */
#define ASN1_OTS        4       /* Octet String */
#define ASN1_NUL        5       /* Null */
#define ASN1_OJI        6       /* Object Identifier  */
#define ASN1_OJD        7       /* Object Description */
#define ASN1_EXT        8       /* External */
#define ASN1_SEQ        16      /* Sequence */
#define ASN1_SET        17      /* Set */
#define ASN1_NUMSTR     18      /* Numerical String */
#define ASN1_PRNSTR     19      /* Printable String */
#define ASN1_TEXSTR     20      /* Teletext String */
#define ASN1_VIDSTR     21      /* Video String */
#define ASN1_IA5STR     22      /* IA5 String */
#define ASN1_UNITIM     23      /* Universal Time */
#define ASN1_GENTIM     24      /* General Time */
#define ASN1_GRASTR     25      /* Graphical String */
#define ASN1_VISSTR     26      /* Visible String */
#define ASN1_GENSTR     27      /* General String */

/* Primitive / Constructed methods*/
#define ASN1_PRI        0       /* Primitive */
#define ASN1_CON        1       /* Constructed */

/*
 * Error codes.
 */
#define ASN1_ERR_NOERROR                0
#define ASN1_ERR_DEC_EMPTY              2
#define ASN1_ERR_DEC_EOC_MISMATCH       3
#define ASN1_ERR_DEC_LENGTH_MISMATCH    4
#define ASN1_ERR_DEC_BADVALUE           5

#define SPNEGO_OID_LEN 7
#define NTLMSSP_OID_LEN  10
#define KRB5_OID_LEN  7
#define MSKRB5_OID_LEN  7
static unsigned long SPNEGO_OID[7] = { 1, 3, 6, 1, 5, 5, 2 };
static unsigned long NTLMSSP_OID[10] = { 1, 3, 6, 1, 4, 1, 311, 2, 2, 10 };
static unsigned long KRB5_OID[7] = { 1, 2, 840, 113554, 1, 2, 2 };
static unsigned long MSKRB5_OID[7] = { 1, 2, 840, 48018, 1, 2, 2 };

/*
 * ASN.1 context.
 */
struct asn1_ctx {
        int error;              /* Error condition */
        unsigned char *pointer; /* Octet just to be decoded */
        unsigned char *begin;   /* First octet */
        unsigned char *end;     /* Octet after last octet */
};

/*
 * Octet string (not null terminated)
 */
struct asn1_octstr {
        unsigned char *data;
        unsigned int len;
};

static void
asn1_open(struct asn1_ctx *ctx, unsigned char *buf, unsigned int len)
{
        ctx->begin = buf;
        ctx->end = buf + len;
        ctx->pointer = buf;
        ctx->error = ASN1_ERR_NOERROR;
}

static unsigned char
asn1_octet_decode(struct asn1_ctx *ctx, unsigned char *ch)
{
        if (ctx->pointer >= ctx->end) {
                ctx->error = ASN1_ERR_DEC_EMPTY;
                return 0;
        }
        *ch = *(ctx->pointer)++;
        return 1;
}

static unsigned char
asn1_tag_decode(struct asn1_ctx *ctx, unsigned int *tag)
{
        unsigned char ch;

        *tag = 0;

        do {
                if (!asn1_octet_decode(ctx, &ch))
                        return 0;
                *tag <<= 7;
                *tag |= ch & 0x7F;
        } while ((ch & 0x80) == 0x80);
        return 1;
}

static unsigned char
asn1_id_decode(struct asn1_ctx *ctx,
               unsigned int *cls, unsigned int *con, unsigned int *tag)
{
        unsigned char ch;

        if (!asn1_octet_decode(ctx, &ch))
                return 0;

        *cls = (ch & 0xC0) >> 6;
        *con = (ch & 0x20) >> 5;
        *tag = (ch & 0x1F);

        if (*tag == 0x1F) {
                if (!asn1_tag_decode(ctx, tag))
                        return 0;
        }
        return 1;
}

static unsigned char
asn1_length_decode(struct asn1_ctx *ctx, unsigned int *def, unsigned int *len)
{
        unsigned char ch, cnt;

        if (!asn1_octet_decode(ctx, &ch))
                return 0;

        if (ch == 0x80)
                *def = 0;
        else {
                *def = 1;

                if (ch < 0x80)
                        *len = ch;
                else {
                        cnt = (unsigned char) (ch & 0x7F);
                        *len = 0;

                        while (cnt > 0) {
                                if (!asn1_octet_decode(ctx, &ch))
                                        return 0;
                                *len <<= 8;
                                *len |= ch;
                                cnt--;
                        }
                }
        }

        /* don't trust len bigger than ctx buffer */
        if (*len > ctx->end - ctx->pointer)
                return 0;

        return 1;
}

static unsigned char
asn1_header_decode(struct asn1_ctx *ctx,
                   unsigned char **eoc,
                   unsigned int *cls, unsigned int *con, unsigned int *tag)
{
        unsigned int def = 0;
        unsigned int len = 0;

        if (!asn1_id_decode(ctx, cls, con, tag))
                return 0;

        if (!asn1_length_decode(ctx, &def, &len))
                return 0;

        /* primitive shall be definite, indefinite shall be constructed */
        if (*con == ASN1_PRI && !def)
                return 0;

        if (def)
                *eoc = ctx->pointer + len;
        else
                *eoc = NULL;
        return 1;
}

static unsigned char
asn1_eoc_decode(struct asn1_ctx *ctx, unsigned char *eoc)
{
        unsigned char ch;

        if (eoc == NULL) {
                if (!asn1_octet_decode(ctx, &ch))
                        return 0;

                if (ch != 0x00) {
                        ctx->error = ASN1_ERR_DEC_EOC_MISMATCH;
                        return 0;
                }

                if (!asn1_octet_decode(ctx, &ch))
                        return 0;

                if (ch != 0x00) {
                        ctx->error = ASN1_ERR_DEC_EOC_MISMATCH;
                        return 0;
                }
                return 1;
        } else {
                if (ctx->pointer != eoc) {
                        ctx->error = ASN1_ERR_DEC_LENGTH_MISMATCH;
                        return 0;
                }
                return 1;
        }
}

/* static unsigned char asn1_null_decode(struct asn1_ctx *ctx,
                                      unsigned char *eoc)
{
        ctx->pointer = eoc;
        return 1;
}

static unsigned char asn1_long_decode(struct asn1_ctx *ctx,
                                      unsigned char *eoc, long *integer)
{
        unsigned char ch;
        unsigned int len;

        if (!asn1_octet_decode(ctx, &ch))
                return 0;

        *integer = (signed char) ch;
        len = 1;

        while (ctx->pointer < eoc) {
                if (++len > sizeof(long)) {
                        ctx->error = ASN1_ERR_DEC_BADVALUE;
                        return 0;
                }

                if (!asn1_octet_decode(ctx, &ch))
                        return 0;

                *integer <<= 8;
                *integer |= ch;
        }
        return 1;
}

static unsigned char asn1_uint_decode(struct asn1_ctx *ctx,
                                      unsigned char *eoc,
                                      unsigned int *integer)
{
        unsigned char ch;
        unsigned int len;

        if (!asn1_octet_decode(ctx, &ch))
                return 0;

        *integer = ch;
        if (ch == 0)
                len = 0;
        else
                len = 1;

        while (ctx->pointer < eoc) {
                if (++len > sizeof(unsigned int)) {
                        ctx->error = ASN1_ERR_DEC_BADVALUE;
                        return 0;
                }

                if (!asn1_octet_decode(ctx, &ch))
                        return 0;

                *integer <<= 8;
                *integer |= ch;
        }
        return 1;
}

static unsigned char asn1_ulong_decode(struct asn1_ctx *ctx,
                                       unsigned char *eoc,
                                       unsigned long *integer)
{
        unsigned char ch;
        unsigned int len;

        if (!asn1_octet_decode(ctx, &ch))
                return 0;

        *integer = ch;
        if (ch == 0)
                len = 0;
        else
                len = 1;

        while (ctx->pointer < eoc) {
                if (++len > sizeof(unsigned long)) {
                        ctx->error = ASN1_ERR_DEC_BADVALUE;
                        return 0;
                }

                if (!asn1_octet_decode(ctx, &ch))
                        return 0;

                *integer <<= 8;
                *integer |= ch;
        }
        return 1;
}

static unsigned char
asn1_octets_decode(struct asn1_ctx *ctx,
                   unsigned char *eoc,
                   unsigned char **octets, unsigned int *len)
{
        unsigned char *ptr;

        *len = 0;

        *octets = kmalloc(eoc - ctx->pointer, GFP_ATOMIC);
        if (*octets == NULL) {
                return 0;
        }

        ptr = *octets;
        while (ctx->pointer < eoc) {
                if (!asn1_octet_decode(ctx, (unsigned char *) ptr++)) {
                        kfree(*octets);
                        *octets = NULL;
                        return 0;
                }
                (*len)++;
        }
        return 1;
} */

static unsigned char
asn1_subid_decode(struct asn1_ctx *ctx, unsigned long *subid)
{
        unsigned char ch;

        *subid = 0;

        do {
                if (!asn1_octet_decode(ctx, &ch))
                        return 0;

                *subid <<= 7;
                *subid |= ch & 0x7F;
        } while ((ch & 0x80) == 0x80);
        return 1;
}

static int
asn1_oid_decode(struct asn1_ctx *ctx,
                unsigned char *eoc, unsigned long **oid, unsigned int *len)
{
        unsigned long subid;
        unsigned int size;
        unsigned long *optr;

        size = eoc - ctx->pointer + 1;

        /* first subid actually encodes first two subids */
        if (size < 2 || size > UINT_MAX/sizeof(unsigned long))
                return 0;

        *oid = kmalloc(size * sizeof(unsigned long), GFP_ATOMIC);
        if (*oid == NULL)
                return 0;

        optr = *oid;

        if (!asn1_subid_decode(ctx, &subid)) {
                kfree(*oid);
                *oid = NULL;
                return 0;
        }

        if (subid < 40) {
                optr[0] = 0;
                optr[1] = subid;
        } else if (subid < 80) {
                optr[0] = 1;
                optr[1] = subid - 40;
        } else {
                optr[0] = 2;
                optr[1] = subid - 80;
        }

        *len = 2;
        optr += 2;

        while (ctx->pointer < eoc) {
                if (++(*len) > size) {
                        ctx->error = ASN1_ERR_DEC_BADVALUE;
                        kfree(*oid);
                        *oid = NULL;
                        return 0;
                }

                if (!asn1_subid_decode(ctx, optr++)) {
                        kfree(*oid);
                        *oid = NULL;
                        return 0;
                }
        }
        return 1;
}

static int
compare_oid(unsigned long *oid1, unsigned int oid1len,
            unsigned long *oid2, unsigned int oid2len)
{
        unsigned int i;

        if (oid1len != oid2len)
                return 0;
        else {
                for (i = 0; i < oid1len; i++) {
                        if (oid1[i] != oid2[i])
                                return 0;
                }
                return 1;
        }
}

        /* BB check for endian conversion issues here */

int
decode_negTokenInit(unsigned char *security_blob, int length,
                    enum securityEnum *secType)
{
        struct asn1_ctx ctx;
        unsigned char *end;
        unsigned char *sequence_end;
        unsigned long *oid = NULL;
        unsigned int cls, con, tag, oidlen, rc;
        bool use_ntlmssp = false;
        bool use_kerberos = false;
        bool use_mskerberos = false;

        *secType = NTLM; /* BB eventually make Kerberos or NLTMSSP the default*/

        /* cifs_dump_mem(" Received SecBlob ", security_blob, length); */

        asn1_open(&ctx, security_blob, length);

        /* GSSAPI header */
        if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) {
                cFYI(1, ("Error decoding negTokenInit header"));
                return 0;
        } else if ((cls != ASN1_APL) || (con != ASN1_CON)
                   || (tag != ASN1_EOC)) {
                cFYI(1, ("cls = %d con = %d tag = %d", cls, con, tag));
                return 0;
        }

        /* Check for SPNEGO OID -- remember to free obj->oid */
        rc = asn1_header_decode(&ctx, &end, &cls, &con, &tag);
        if (rc) {
                if ((tag == ASN1_OJI) && (con == ASN1_PRI) &&
                    (cls == ASN1_UNI)) {
                        rc = asn1_oid_decode(&ctx, end, &oid, &oidlen);
                        if (rc) {
                                rc = compare_oid(oid, oidlen, SPNEGO_OID,
                                                 SPNEGO_OID_LEN);
                                kfree(oid);
                        }
                } else
                        rc = 0;
        }

        /* SPNEGO OID not present or garbled -- bail out */
        if (!rc) {
                cFYI(1, ("Error decoding negTokenInit header"));
                return 0;
        }

        if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) {
                cFYI(1, ("Error decoding negTokenInit"));
                return 0;
        } else if ((cls != ASN1_CTX) || (con != ASN1_CON)
                   || (tag != ASN1_EOC)) {
                cFYI(1,
                     ("cls = %d con = %d tag = %d end = %p (%d) exit 0",
                      cls, con, tag, end, *end));
                return 0;
        }

        if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) {
                cFYI(1, ("Error decoding negTokenInit"));
                return 0;
        } else if ((cls != ASN1_UNI) || (con != ASN1_CON)
                   || (tag != ASN1_SEQ)) {
                cFYI(1,
                     ("cls = %d con = %d tag = %d end = %p (%d) exit 1",
                      cls, con, tag, end, *end));
                return 0;
        }

        if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) {
                cFYI(1, ("Error decoding 2nd part of negTokenInit"));
                return 0;
        } else if ((cls != ASN1_CTX) || (con != ASN1_CON)
                   || (tag != ASN1_EOC)) {
                cFYI(1,
                     ("cls = %d con = %d tag = %d end = %p (%d) exit 0",
                      cls, con, tag, end, *end));
                return 0;
        }

        if (asn1_header_decode
            (&ctx, &sequence_end, &cls, &con, &tag) == 0) {
                cFYI(1, ("Error decoding 2nd part of negTokenInit"));
                return 0;
        } else if ((cls != ASN1_UNI) || (con != ASN1_CON)
                   || (tag != ASN1_SEQ)) {
                cFYI(1,
                     ("cls = %d con = %d tag = %d end = %p (%d) exit 1",
                      cls, con, tag, end, *end));
                return 0;
        }

        while (!asn1_eoc_decode(&ctx, sequence_end)) {
                rc = asn1_header_decode(&ctx, &end, &cls, &con, &tag);
                if (!rc) {
                        cFYI(1,
                             ("Error decoding negTokenInit hdr exit2"));
                        return 0;
                }
                if ((tag == ASN1_OJI) && (con == ASN1_PRI)) {
                        if (asn1_oid_decode(&ctx, end, &oid, &oidlen)) {

                                cFYI(1, ("OID len = %d oid = 0x%lx 0x%lx "
                                         "0x%lx 0x%lx", oidlen, *oid,
                                         *(oid + 1), *(oid + 2), *(oid + 3)));

                                if (compare_oid(oid, oidlen, MSKRB5_OID,
                                                MSKRB5_OID_LEN) &&
                                                !use_kerberos)
                                        use_mskerberos = true;
                                else if (compare_oid(oid, oidlen, KRB5_OID,
                                                     KRB5_OID_LEN) &&
                                                     !use_mskerberos)
                                        use_kerberos = true;
                                else if (compare_oid(oid, oidlen, NTLMSSP_OID,
                                                     NTLMSSP_OID_LEN))
                                        use_ntlmssp = true;

                                kfree(oid);
                        }
                } else {
                        cFYI(1, ("Should be an oid what is going on?"));
                }
        }

        if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) {
                cFYI(1, ("Error decoding last part negTokenInit exit3"));
                return 0;
        } else if ((cls != ASN1_CTX) || (con != ASN1_CON)) {
                /* tag = 3 indicating mechListMIC */
                cFYI(1, ("Exit 4 cls = %d con = %d tag = %d end = %p (%d)",
                         cls, con, tag, end, *end));
                return 0;
        }
        if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) {
                cFYI(1, ("Error decoding last part negTokenInit exit5"));
                return 0;
        } else if ((cls != ASN1_UNI) || (con != ASN1_CON)
                   || (tag != ASN1_SEQ)) {
                cFYI(1, ("cls = %d con = %d tag = %d end = %p (%d)",
                        cls, con, tag, end, *end));
        }

        if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) {
                cFYI(1, ("Error decoding last part negTokenInit exit 7"));
                return 0;
        } else if ((cls != ASN1_CTX) || (con != ASN1_CON)) {
                cFYI(1, ("Exit 8 cls = %d con = %d tag = %d end = %p (%d)",
                         cls, con, tag, end, *end));
                return 0;
        }
        if (asn1_header_decode(&ctx, &end, &cls, &con, &tag) == 0) {
                cFYI(1, ("Error decoding last part negTokenInit exit9"));
                return 0;
        } else if ((cls != ASN1_UNI) || (con != ASN1_PRI)
                   || (tag != ASN1_GENSTR)) {
                cFYI(1, ("Exit10 cls = %d con = %d tag = %d end = %p (%d)",
                         cls, con, tag, end, *end));
                return 0;
        }
        cFYI(1, ("Need to call asn1_octets_decode() function for %s",
                 ctx.pointer)); /* is this UTF-8 or ASCII? */

        if (use_kerberos)
                *secType = Kerberos;
        else if (use_mskerberos)
                *secType = MSKerberos;
        else if (use_ntlmssp)
                *secType = NTLMSSP;

        return 1;
}

/* [<][>][^][v][top][bottom][index][help] */

[funini.com] -> [kei@sodan] -> Kernel Reading