diff options
Diffstat (limited to 'src/lib/gssapi/generic/oid_ops.c')
| -rw-r--r-- | src/lib/gssapi/generic/oid_ops.c | 553 | 
1 files changed, 553 insertions, 0 deletions
| diff --git a/src/lib/gssapi/generic/oid_ops.c b/src/lib/gssapi/generic/oid_ops.c new file mode 100644 index 000000000000..6e294b9bcf94 --- /dev/null +++ b/src/lib/gssapi/generic/oid_ops.c @@ -0,0 +1,553 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/gssapi/generic/oid_ops.c */ +/* + * Copyright 1995 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + *   require a specific license from the United States Government. + *   It is the responsibility of any person or organization contemplating + *   export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission.  Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose.  It is provided "as is" without express + * or implied warranty. + */ +/* + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose.  It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* GSS-API V2 interfaces to manipulate OIDs */ + +#include "gssapiP_generic.h" +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <gssapi/gssapi_generic.h> +#include <errno.h> +#include <ctype.h> + +/* + * The functions for allocating and releasing individual OIDs use malloc and + * free instead of the gssalloc wrappers, because the mechglue currently mixes + * generic_gss_copy_oid() with hand-freeing of OIDs.  We do not need to free + * free OIDs allocated by mechanisms, so this should not be a problem. + */ + +OM_uint32 +generic_gss_release_oid(OM_uint32 *minor_status, gss_OID *oid) +{ +    if (minor_status) +        *minor_status = 0; + +    if (oid == NULL || *oid == GSS_C_NO_OID) +        return(GSS_S_COMPLETE); + +    /* +     * The V2 API says the following! +     * +     * gss_release_oid[()] will recognize any of the GSSAPI's own OID values, +     * and will silently ignore attempts to free these OIDs; for other OIDs +     * it will call the C free() routine for both the OID data and the +     * descriptor.  This allows applications to freely mix their own heap- +     * allocated OID values with OIDs returned by GSS-API. +     */ + +    /* +     * We use the official OID definitions instead of the unofficial OID +     * defintions. But we continue to support the unofficial OID +     * gss_nt_service_name just in case if some gss applications use +     * the old OID. +     */ + +    if ((*oid != GSS_C_NT_USER_NAME) && +        (*oid != GSS_C_NT_MACHINE_UID_NAME) && +        (*oid != GSS_C_NT_STRING_UID_NAME) && +        (*oid != GSS_C_NT_HOSTBASED_SERVICE) && +        (*oid != GSS_C_NT_ANONYMOUS) && +        (*oid != GSS_C_NT_EXPORT_NAME) && +        (*oid != GSS_C_NT_COMPOSITE_EXPORT) && +        (*oid != gss_nt_service_name)) { +        free((*oid)->elements); +        free(*oid); +    } +    *oid = GSS_C_NO_OID; +    return(GSS_S_COMPLETE); +} + +OM_uint32 +generic_gss_copy_oid(OM_uint32 *minor_status, +                     const gss_OID_desc * const oid, +                     gss_OID *new_oid) +{ +    gss_OID         p; + +    *minor_status = 0; + +    p = (gss_OID) malloc(sizeof(gss_OID_desc)); +    if (!p) { +        *minor_status = ENOMEM; +        return GSS_S_FAILURE; +    } +    p->length = oid->length; +    p->elements = malloc(p->length); +    if (!p->elements) { +        free(p); +        return GSS_S_FAILURE; +    } +    memcpy(p->elements, oid->elements, p->length); +    *new_oid = p; +    return(GSS_S_COMPLETE); +} + + +OM_uint32 +generic_gss_create_empty_oid_set(OM_uint32 *minor_status, gss_OID_set *oid_set) +{ +    *minor_status = 0; + +    if (oid_set == NULL) +        return GSS_S_CALL_INACCESSIBLE_WRITE; + +    if ((*oid_set = (gss_OID_set) gssalloc_malloc(sizeof(gss_OID_set_desc)))) { +        memset(*oid_set, 0, sizeof(gss_OID_set_desc)); +        return(GSS_S_COMPLETE); +    } +    else { +        *minor_status = ENOMEM; +        return(GSS_S_FAILURE); +    } +} + +OM_uint32 +generic_gss_add_oid_set_member(OM_uint32 *minor_status, +                               const gss_OID_desc * const member_oid, +                               gss_OID_set *oid_set) +{ +    gss_OID     elist; +    gss_OID     lastel; + +    *minor_status = 0; + +    if (member_oid == NULL || member_oid->length == 0 || +        member_oid->elements == NULL) +        return (GSS_S_CALL_INACCESSIBLE_READ); + +    if (oid_set == NULL) +        return GSS_S_CALL_INACCESSIBLE_WRITE; + +    elist = (*oid_set)->elements; +    /* Get an enlarged copy of the array */ +    if (((*oid_set)->elements = (gss_OID) gssalloc_malloc(((*oid_set)->count+1) * +                                                          sizeof(gss_OID_desc)))) { +        /* Copy in the old junk */ +        if (elist) +            memcpy((*oid_set)->elements, +                   elist, +                   ((*oid_set)->count * sizeof(gss_OID_desc))); + +        /* Duplicate the input element */ +        lastel = &(*oid_set)->elements[(*oid_set)->count]; +        if ((lastel->elements = +             (void *) gssalloc_malloc((size_t) member_oid->length))) { +            /* Success - copy elements */ +            memcpy(lastel->elements, member_oid->elements, +                   (size_t) member_oid->length); +            /* Set length */ +            lastel->length = member_oid->length; + +            /* Update count */ +            (*oid_set)->count++; +            if (elist) +                gssalloc_free(elist); +            *minor_status = 0; +            return(GSS_S_COMPLETE); +        } +        else +            gssalloc_free((*oid_set)->elements); +    } +    /* Failure - restore old contents of list */ +    (*oid_set)->elements = elist; +    *minor_status = ENOMEM; +    return(GSS_S_FAILURE); +} + +OM_uint32 +generic_gss_test_oid_set_member(OM_uint32 *minor_status, +                                const gss_OID_desc * const member, +                                gss_OID_set set, +                                int * present) +{ +    OM_uint32   i; +    int         result; + +    *minor_status = 0; + +    if (member == NULL || set == NULL) +        return (GSS_S_CALL_INACCESSIBLE_READ); + +    if (present == NULL) +        return (GSS_S_CALL_INACCESSIBLE_WRITE); + +    result = 0; +    for (i=0; i<set->count; i++) { +        if ((set->elements[i].length == member->length) && +            !memcmp(set->elements[i].elements, +                    member->elements, +                    (size_t) member->length)) { +            result = 1; +            break; +        } +    } +    *present = result; +    return(GSS_S_COMPLETE); +} + +OM_uint32 +generic_gss_oid_to_str(OM_uint32 *minor_status, +                       const gss_OID_desc * const oid, +                       gss_buffer_t oid_str) +{ +    unsigned long       number, n; +    OM_uint32           i; +    int                 first; +    unsigned char       *cp; +    struct k5buf        buf; + +    if (minor_status != NULL) +        *minor_status = 0; + +    if (oid_str != GSS_C_NO_BUFFER) { +        oid_str->length = 0; +        oid_str->value = NULL; +    } + +    if (oid == NULL || oid->length == 0 || oid->elements == NULL) +        return (GSS_S_CALL_INACCESSIBLE_READ); + +    if (oid_str == GSS_C_NO_BUFFER) +        return (GSS_S_CALL_INACCESSIBLE_WRITE); + +    /* Decoded according to krb5/gssapi_krb5.c */ + +    cp = (unsigned char *) oid->elements; +    number = (unsigned long) cp[0]; +    k5_buf_init_dynamic(&buf); +    k5_buf_add(&buf, "{ "); +    number = 0; +    cp = (unsigned char *) oid->elements; +    first = 1; +    for (i = 0; i < oid->length; i++) { +        number = (number << 7) | (cp[i] & 0x7f); +        if ((cp[i] & 0x80) == 0) { +            if (first) { +                n = (number < 40) ? 0 : (number < 80) ? 1 : 2; +                k5_buf_add_fmt(&buf, "%lu %lu ", n, number - (n * 40)); +                first = 0; +            } else { +                k5_buf_add_fmt(&buf, "%lu ", number); +            } +            number = 0; +        } +    } +    k5_buf_add_len(&buf, "}\0", 2); +    return k5buf_to_gss(minor_status, &buf, oid_str); +} + +/* Return the length of a DER OID subidentifier encoding. */ +static size_t +arc_encoded_length(unsigned long arc) +{ +    size_t len = 1; + +    for (arc >>= 7; arc; arc >>= 7) +        len++; +    return len; +} + +/* Encode a subidentifier into *bufp and advance it to the encoding's end. */ +static void +arc_encode(unsigned long arc, unsigned char **bufp) +{ +    unsigned char *p; + +    /* Advance to the end and encode backwards. */ +    p = *bufp = *bufp + arc_encoded_length(arc); +    *--p = arc & 0x7f; +    for (arc >>= 7; arc; arc >>= 7) +        *--p = (arc & 0x7f) | 0x80; +} + +/* Fetch an arc value from *bufp and advance past it and any following spaces + * or periods.  Return 1 on success, 0 if *bufp is not at a valid arc value. */ +static int +get_arc(const unsigned char **bufp, const unsigned char *end, +        unsigned long *arc_out) +{ +    const unsigned char *p = *bufp; +    unsigned long arc = 0, newval; + +    if (p == end || !isdigit(*p)) +        return 0; +    for (; p < end && isdigit(*p); p++) { +        newval = arc * 10 + (*p - '0'); +        if (newval < arc) +            return 0; +        arc = newval; +    } +    while (p < end && (isspace(*p) || *p == '.')) +        p++; +    *bufp = p; +    *arc_out = arc; +    return 1; +} + +/* + * Convert a sequence of two or more decimal arc values into a DER-encoded OID. + * The values may be separated by any combination of whitespace and period + * characters, and may be optionally surrounded with braces.  Leading + * whitespace and trailing garbage is allowed.  The first arc value must be 0, + * 1, or 2, and the second value must be less than 40 if the first value is not + * 2. + */ +OM_uint32 +generic_gss_str_to_oid(OM_uint32 *minor_status, +                       gss_buffer_t oid_str, +                       gss_OID *oid_out) +{ +    const unsigned char *p, *end, *arc3_start; +    unsigned char *out; +    unsigned long arc, arc1, arc2; +    size_t nbytes; +    int brace = 0; +    gss_OID oid; + +    if (minor_status != NULL) +        *minor_status = 0; + +    if (oid_out != NULL) +        *oid_out = GSS_C_NO_OID; + +    if (GSS_EMPTY_BUFFER(oid_str)) +        return (GSS_S_CALL_INACCESSIBLE_READ); + +    if (oid_out == NULL) +        return (GSS_S_CALL_INACCESSIBLE_WRITE); + +    /* Skip past initial spaces and, optionally, an open brace. */ +    brace = 0; +    p = oid_str->value; +    end = p + oid_str->length; +    while (p < end && isspace(*p)) +        p++; +    if (p < end && *p == '{') { +        brace = 1; +        p++; +    } +    while (p < end && isspace(*p)) +        p++; + +    /* Get the first two arc values, to be encoded as one subidentifier. */ +    if (!get_arc(&p, end, &arc1) || !get_arc(&p, end, &arc2)) +        return (GSS_S_FAILURE); +    if (arc1 > 2 || (arc1 < 2 && arc2 > 39) || arc2 > ULONG_MAX - 80) +        return (GSS_S_FAILURE); +    arc3_start = p; + +    /* Compute the total length of the encoding while checking syntax. */ +    nbytes = arc_encoded_length(arc1 * 40 + arc2); +    while (get_arc(&p, end, &arc)) +        nbytes += arc_encoded_length(arc); +    if (brace && (p == end || *p != '}')) +        return (GSS_S_FAILURE); + +    /* Allocate an oid structure. */ +    oid = malloc(sizeof(*oid)); +    if (oid == NULL) +        return (GSS_S_FAILURE); +    oid->elements = malloc(nbytes); +    if (oid->elements == NULL) { +        free(oid); +        return (GSS_S_FAILURE); +    } +    oid->length = nbytes; + +    out = oid->elements; +    arc_encode(arc1 * 40 + arc2, &out); +    p = arc3_start; +    while (get_arc(&p, end, &arc)) +        arc_encode(arc, &out); +    assert(out - nbytes == oid->elements); +    *oid_out = oid; +    return(GSS_S_COMPLETE); +} + +/* Compose an OID of a prefix and an integer suffix */ +OM_uint32 +generic_gss_oid_compose(OM_uint32 *minor_status, +                        const char *prefix, +                        size_t prefix_len, +                        int suffix, +                        gss_OID_desc *oid) +{ +    int osuffix, i; +    size_t nbytes; +    unsigned char *op; + +    if (oid == GSS_C_NO_OID) { +        *minor_status = EINVAL; +        return GSS_S_FAILURE; +    } +    if (oid->length < prefix_len) { +        *minor_status = ERANGE; +        return GSS_S_FAILURE; +    } + +    memcpy(oid->elements, prefix, prefix_len); + +    nbytes = 0; +    osuffix = suffix; +    while (suffix) { +        nbytes++; +        suffix >>= 7; +    } +    suffix = osuffix; + +    if (oid->length < prefix_len + nbytes) { +        *minor_status = ERANGE; +        return GSS_S_FAILURE; +    } + +    op = (unsigned char *) oid->elements + prefix_len + nbytes; +    i = -1; +    while (suffix) { +        op[i] = (unsigned char)suffix & 0x7f; +        if (i != -1) +            op[i] |= 0x80; +        i--; +        suffix >>= 7; +    } + +    oid->length = prefix_len + nbytes; + +    *minor_status = 0; +    return GSS_S_COMPLETE; +} + +OM_uint32 +generic_gss_oid_decompose(OM_uint32 *minor_status, +                          const char *prefix, +                          size_t prefix_len, +                          gss_OID_desc *oid, +                          int *suffix) +{ +    size_t i, slen; +    unsigned char *op; + +    if (oid->length < prefix_len || +        memcmp(oid->elements, prefix, prefix_len) != 0) { +        return GSS_S_BAD_MECH; +    } + +    op = (unsigned char *) oid->elements + prefix_len; + +    *suffix = 0; + +    slen = oid->length - prefix_len; + +    for (i = 0; i < slen; i++) { +        *suffix = (*suffix << 7) | (op[i] & 0x7f); +        if (i + 1 != slen && (op[i] & 0x80) == 0) { +            *minor_status = EINVAL; +            return GSS_S_FAILURE; +        } +    } + +    return GSS_S_COMPLETE; +} + +OM_uint32 +generic_gss_copy_oid_set(OM_uint32 *minor_status, +                         const gss_OID_set_desc * const oidset, +                         gss_OID_set *new_oidset) +{ +    gss_OID_set_desc *copy; +    OM_uint32 minor = 0; +    OM_uint32 major = GSS_S_COMPLETE; +    OM_uint32 i; + +    if (minor_status != NULL) +        *minor_status = 0; + +    if (new_oidset != NULL) +        *new_oidset = GSS_C_NO_OID_SET; + +    if (oidset == GSS_C_NO_OID_SET) +        return (GSS_S_CALL_INACCESSIBLE_READ); + +    if (new_oidset == NULL) +        return (GSS_S_CALL_INACCESSIBLE_WRITE); + +    if ((copy = (gss_OID_set_desc *) gssalloc_calloc(1, sizeof (*copy))) == NULL) { +        major = GSS_S_FAILURE; +        goto done; +    } + +    if ((copy->elements = (gss_OID_desc *) +         gssalloc_calloc(oidset->count, sizeof (*copy->elements))) == NULL) { +        major = GSS_S_FAILURE; +        goto done; +    } +    copy->count = oidset->count; + +    for (i = 0; i < copy->count; i++) { +        gss_OID_desc *out = ©->elements[i]; +        gss_OID_desc *in = &oidset->elements[i]; + +        if ((out->elements = (void *) gssalloc_malloc(in->length)) == NULL) { +            major = GSS_S_FAILURE; +            goto done; +        } +        (void) memcpy(out->elements, in->elements, in->length); +        out->length = in->length; +    } + +    *new_oidset = copy; +done: +    if (major != GSS_S_COMPLETE) { +        (void) generic_gss_release_oid_set(&minor, ©); +    } + +    return (major); +} | 
