diff options
| author | Stanislav Sedov <stas@FreeBSD.org> | 2011-10-05 07:23:29 +0000 | 
|---|---|---|
| committer | Stanislav Sedov <stas@FreeBSD.org> | 2011-10-05 07:23:29 +0000 | 
| commit | 7c450da7b446c557e05f34a100b597800967d987 (patch) | |
| tree | 57a48e7e9b592f2d5b713e80a4455820625c2b7b /lib/gssapi/mech/gss_mo.c | |
| parent | b4e3a10e9339a8400197298021d6ca9b8e3aa039 (diff) | |
Diffstat (limited to 'lib/gssapi/mech/gss_mo.c')
| -rw-r--r-- | lib/gssapi/mech/gss_mo.c | 635 | 
1 files changed, 635 insertions, 0 deletions
diff --git a/lib/gssapi/mech/gss_mo.c b/lib/gssapi/mech/gss_mo.c new file mode 100644 index 000000000000..ad74d9237a2d --- /dev/null +++ b/lib/gssapi/mech/gss_mo.c @@ -0,0 +1,635 @@ +/* + * Copyright (c) 2010 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Portions Copyright (c) 2010 Apple Inc. All rights reserved. + * Portions Copyright (c) 2010 PADL Software Pty Ltd. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + *    may be used to endorse or promote products derived from this software + *    without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "mech_locl.h" + +#include <crypto-headers.h> + +static int +get_option_def(int def, gss_const_OID mech, gss_mo_desc *mo, gss_buffer_t value) +{ +    return def; +} + +int +_gss_mo_get_option_1(gss_const_OID mech, gss_mo_desc *mo, gss_buffer_t value) +{ +    return get_option_def(1, mech, mo, value); +} + +int +_gss_mo_get_option_0(gss_const_OID mech, gss_mo_desc *mo, gss_buffer_t value) +{ +    return get_option_def(0, mech, mo, value); +} + +int +_gss_mo_get_ctx_as_string(gss_const_OID mech, gss_mo_desc *mo, gss_buffer_t value) +{ +    if (value) { +	value->value = strdup((char *)mo->ctx); +	if (value->value == NULL) +	    return GSS_S_FAILURE; +	value->length = strlen((char *)mo->ctx); +    } +    return GSS_S_COMPLETE; +} + +GSSAPI_LIB_FUNCTION int GSSAPI_LIB_CALL +gss_mo_set(gss_const_OID mech, gss_const_OID option, +	   int enable, gss_buffer_t value) +{ +    gssapi_mech_interface m; +    size_t n; + +    if ((m = __gss_get_mechanism(mech)) == NULL) +	return GSS_S_BAD_MECH; + +    for (n = 0; n < m->gm_mo_num; n++) +	if (gss_oid_equal(option, m->gm_mo[n].option) && m->gm_mo[n].set) +	    return m->gm_mo[n].set(mech, &m->gm_mo[n], enable, value); + +    return GSS_S_UNAVAILABLE; +} + +GSSAPI_LIB_FUNCTION int GSSAPI_LIB_CALL +gss_mo_get(gss_const_OID mech, gss_const_OID option, gss_buffer_t value) +{ +    gssapi_mech_interface m; +    size_t n; + +    _mg_buffer_zero(value); + +    if ((m = __gss_get_mechanism(mech)) == NULL) +	return GSS_S_BAD_MECH; + +    for (n = 0; n < m->gm_mo_num; n++) +	if (gss_oid_equal(option, m->gm_mo[n].option) && m->gm_mo[n].get) +	    return m->gm_mo[n].get(mech, &m->gm_mo[n], value); + +    return GSS_S_UNAVAILABLE; +} + +static void +add_all_mo(gssapi_mech_interface m, gss_OID_set *options, OM_uint32 mask) +{ +    OM_uint32 minor; +    size_t n; + +    for (n = 0; n < m->gm_mo_num; n++) +	if ((m->gm_mo[n].flags & mask) == mask) +	    gss_add_oid_set_member(&minor, m->gm_mo[n].option, options); +} + +GSSAPI_LIB_FUNCTION void GSSAPI_LIB_CALL +gss_mo_list(gss_const_OID mech, gss_OID_set *options) +{ +    gssapi_mech_interface m; +    OM_uint32 major, minor; + +    if (options == NULL) +	return; + +    *options = GSS_C_NO_OID_SET; + +    if ((m = __gss_get_mechanism(mech)) == NULL) +	return; + +    major = gss_create_empty_oid_set(&minor, options); +    if (major != GSS_S_COMPLETE) +	return; + +    add_all_mo(m, options, 0); +} + +GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL +gss_mo_name(gss_const_OID mech, gss_const_OID option, gss_buffer_t name) +{ +    gssapi_mech_interface m; +    size_t n; + +    if (name == NULL) +	return GSS_S_BAD_NAME; + +    if ((m = __gss_get_mechanism(mech)) == NULL) +	return GSS_S_BAD_MECH; + +    for (n = 0; n < m->gm_mo_num; n++) { +	if (gss_oid_equal(option, m->gm_mo[n].option)) { +	    /* +	     * If there is no name, its because its a GSS_C_MA and +	     * there is already a table for that. +	     */ +	    if (m->gm_mo[n].name) { +		name->value = strdup(m->gm_mo[n].name); +		if (name->value == NULL) +		    return GSS_S_BAD_NAME; +		name->length = strlen(m->gm_mo[n].name); +		return GSS_S_COMPLETE; +	    } else { +		OM_uint32 junk; +		return gss_display_mech_attr(&junk, option, +					     NULL, name, NULL); +	    } +	} +    } +    return GSS_S_BAD_NAME; +} + +/* + * Helper function to allow NULL name + */ + +static OM_uint32 +mo_value(const gss_const_OID mech, gss_const_OID option, gss_buffer_t name) +{ +    if (name == NULL) +	return GSS_S_COMPLETE; + +    return gss_mo_get(mech, option, name); +} + +/* code derived from draft-ietf-cat-sasl-gssapi-01 */ +static char basis_32[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; + +static OM_uint32 +make_sasl_name(OM_uint32 *minor, const gss_OID mech, char sasl_name[16]) +{ +    EVP_MD_CTX *ctx; +    char *p = sasl_name; +    u_char hdr[2], hash[20], *h = hash; + +    if (mech->length > 127) +        return GSS_S_BAD_MECH; + +    hdr[0] = 0x06; +    hdr[1] = mech->length; + +    ctx = EVP_MD_CTX_create(); +    EVP_DigestInit_ex(ctx, EVP_sha1(), NULL); +    EVP_DigestUpdate(ctx, hdr, 2); +    EVP_DigestUpdate(ctx, mech->elements, mech->length); +    EVP_DigestFinal_ex(ctx, hash, NULL); + +    memcpy(p, "GS2-", 4); +    p += 4; + +    *p++ = basis_32[(h[0] >> 3)]; +    *p++ = basis_32[((h[0] & 7) << 2) | (h[1] >> 6)]; +    *p++ = basis_32[(h[1] & 0x3f) >> 1]; +    *p++ = basis_32[((h[1] & 1) << 4) | (h[2] >> 4)]; +    *p++ = basis_32[((h[2] & 0xf) << 1) | (h[3] >> 7)]; +    *p++ = basis_32[(h[3] & 0x7f) >> 2]; +    *p++ = basis_32[((h[3] & 3) << 3) | (h[4] >> 5)]; +    *p++ = basis_32[(h[4] & 0x1f)]; +    *p++ = basis_32[(h[5] >> 3)]; +    *p++ = basis_32[((h[5] & 7) << 2) | (h[6] >> 6)]; +    *p++ = basis_32[(h[6] & 0x3f) >> 1]; + +    *p = '\0'; + +    return GSS_S_COMPLETE; +} + +/* + * gss_inquire_saslname_for_mech() wrapper that uses MIT SPI + */ +static OM_uint32 +inquire_saslname_for_mech_compat(OM_uint32 *minor, +                                 const gss_OID desired_mech, +                                 gss_buffer_t sasl_mech_name, +                                 gss_buffer_t mech_name, +                                 gss_buffer_t mech_description) +{ +    struct gss_mech_compat_desc_struct *gmc; +    gssapi_mech_interface m; +    OM_uint32 major; + +    m = __gss_get_mechanism(desired_mech); +    if (m == NULL) +        return GSS_S_BAD_MECH; + +    gmc = m->gm_compat; + +    if (gmc != NULL && gmc->gmc_inquire_saslname_for_mech != NULL) { +        major = gmc->gmc_inquire_saslname_for_mech(minor, +                                                   desired_mech, +                                                   sasl_mech_name, +                                                   mech_name, +                                                   mech_description); +    } else { +        major = GSS_S_UNAVAILABLE; +    } + +    return major; +} + +/** + * Returns different protocol names and description of the mechanism. + * + * @param minor_status minor status code + * @param desired_mech mech list query + * @param sasl_mech_name SASL GS2 protocol name + * @param mech_name gssapi protocol name + * @param mech_description description of gssapi mech + * + * @return returns GSS_S_COMPLETE or a error code. + * + * @ingroup gssapi + */ + +GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL +gss_inquire_saslname_for_mech(OM_uint32 *minor_status, +			      const gss_OID desired_mech, +			      gss_buffer_t sasl_mech_name, +			      gss_buffer_t mech_name, +			      gss_buffer_t mech_description) +{ +    OM_uint32 major; + +    _mg_buffer_zero(sasl_mech_name); +    _mg_buffer_zero(mech_name); +    _mg_buffer_zero(mech_description); + +    if (minor_status) +	*minor_status = 0; + +    if (desired_mech == NULL) +	return GSS_S_BAD_MECH; + +    major = mo_value(desired_mech, GSS_C_MA_SASL_MECH_NAME, sasl_mech_name); +    if (major == GSS_S_COMPLETE) { +        /* Native SPI */ +        major = mo_value(desired_mech, GSS_C_MA_MECH_NAME, mech_name); +        if (GSS_ERROR(major)) +            return major; + +        major = mo_value(desired_mech, GSS_C_MA_MECH_DESCRIPTION, mech_description); +        if (GSS_ERROR(major)) +            return major; +    } + +    if (GSS_ERROR(major)) { +        /* API-as-SPI compatibility */ +        major = inquire_saslname_for_mech_compat(minor_status, +                                                 desired_mech, +                                                 sasl_mech_name, +                                                 mech_name, +                                                 mech_description); +    } + +    if (GSS_ERROR(major)) { +        /* Algorithmically dervied SASL mechanism name */ +        char buf[16]; +        gss_buffer_desc tmp = { sizeof(buf) - 1, buf }; + +        major = make_sasl_name(minor_status, desired_mech, buf); +        if (GSS_ERROR(major)) +            return major; + +        major = _gss_copy_buffer(minor_status, &tmp, sasl_mech_name); +        if (GSS_ERROR(major)) +            return major; +    } + +    return major; +} + +/** + * Find a mech for a sasl name + * + * @param minor_status minor status code + * @param sasl_mech_name + * @param mech_type + * + * @return returns GSS_S_COMPLETE or an error code. + */ + +GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL +gss_inquire_mech_for_saslname(OM_uint32 *minor_status, +			      const gss_buffer_t sasl_mech_name, +			      gss_OID *mech_type) +{ +    struct _gss_mech_switch *m; +    gss_buffer_desc name; +    OM_uint32 major, junk; +    char buf[16]; + +    _gss_load_mech(); + +    *mech_type = NULL; + +    HEIM_SLIST_FOREACH(m, &_gss_mechs, gm_link) { +        struct gss_mech_compat_desc_struct *gmc; + +        /* Native SPI */ +        major = mo_value(&m->gm_mech_oid, GSS_C_MA_SASL_MECH_NAME, &name); +        if (major == GSS_S_COMPLETE && +            name.length == sasl_mech_name->length && +            memcmp(name.value, sasl_mech_name->value, name.length) == 0) { +                gss_release_buffer(&junk, &name); +                *mech_type = &m->gm_mech_oid; +                return GSS_S_COMPLETE; +	} +	gss_release_buffer(&junk, &name); + +        if (GSS_ERROR(major)) { +            /* API-as-SPI compatibility */ +            gmc = m->gm_mech.gm_compat; +            if (gmc && gmc->gmc_inquire_mech_for_saslname) { +                major = gmc->gmc_inquire_mech_for_saslname(minor_status, +                                                           sasl_mech_name, +                                                           mech_type); +                if (major == GSS_S_COMPLETE) +                    return GSS_S_COMPLETE; +            } +        } + +        if (GSS_ERROR(major)) { +            /* Algorithmically dervied SASL mechanism name */ +            if (sasl_mech_name->length == 16 && +                make_sasl_name(minor_status, &m->gm_mech_oid, buf) == GSS_S_COMPLETE && +                memcmp(buf, sasl_mech_name->value, 16) == 0) { +                    *mech_type = &m->gm_mech_oid; +                    return GSS_S_COMPLETE; +            } +        } +    } + +    return GSS_S_BAD_MECH; +} + +/* + * Test mechanism against indicated attributes using both Heimdal and + * MIT SPIs. + */ +static int +test_mech_attrs(gssapi_mech_interface mi, +                gss_const_OID_set mech_attrs, +                gss_const_OID_set against_attrs, +                int except) +{ +    size_t n, m; +    int eq = 0; + +    if (against_attrs == GSS_C_NO_OID_SET) +        return 1; + +    for (n = 0; n < against_attrs->count; n++) { +        for (m = 0; m < mi->gm_mo_num; m++) { +            eq = gss_oid_equal(mi->gm_mo[m].option, +                               &against_attrs->elements[n]); +            if (eq) +                break; +        } +        if (mech_attrs != GSS_C_NO_OID_SET) { +            for (m = 0; m < mech_attrs->count; m++) { +                eq = gss_oid_equal(&mech_attrs->elements[m], +                                   &against_attrs->elements[n]); +                if (eq) +                    break; +            } +        } +        if (!eq ^ except) +            return 0; +    } + +    return 1; +} + +/** + * Return set of mechanism that fullfill the criteria + * + * @param minor_status minor status code + * @param desired_mech_attrs + * @param except_mech_attrs + * @param critical_mech_attrs + * @param mechs returned mechs, free with gss_release_oid_set(). + * + * @return returns GSS_S_COMPLETE or an error code. + */ + +GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL +gss_indicate_mechs_by_attrs(OM_uint32 * minor_status, +			    gss_const_OID_set desired_mech_attrs, +			    gss_const_OID_set except_mech_attrs, +			    gss_const_OID_set critical_mech_attrs, +			    gss_OID_set *mechs) +{ +    struct _gss_mech_switch *ms; +    gss_OID_set mech_attrs = GSS_C_NO_OID_SET; +    gss_OID_set known_mech_attrs = GSS_C_NO_OID_SET; +    OM_uint32 major; + +    major = gss_create_empty_oid_set(minor_status, mechs); +    if (GSS_ERROR(major)) +	return major; + +    _gss_load_mech(); + +    HEIM_SLIST_FOREACH(ms, &_gss_mechs, gm_link) { +	gssapi_mech_interface mi = &ms->gm_mech; +        struct gss_mech_compat_desc_struct *gmc = mi->gm_compat; +        OM_uint32 tmp; + +        if (gmc && gmc->gmc_inquire_attrs_for_mech) { +            major = gmc->gmc_inquire_attrs_for_mech(minor_status, +                                                    &mi->gm_mech_oid, +                                                    &mech_attrs, +                                                    &known_mech_attrs); +            if (GSS_ERROR(major)) +                continue; +        } + +        /* +         * Test mechanism supports all of desired_mech_attrs; +         * none of except_mech_attrs; +         * and knows of all critical_mech_attrs. +         */ +        if (test_mech_attrs(mi, mech_attrs,       desired_mech_attrs,  0) && +            test_mech_attrs(mi, mech_attrs,       except_mech_attrs,   1) && +            test_mech_attrs(mi, known_mech_attrs, critical_mech_attrs, 0)) { +            major = gss_add_oid_set_member(minor_status, &mi->gm_mech_oid, mechs); +        } + +        gss_release_oid_set(&tmp, &mech_attrs); +        gss_release_oid_set(&tmp, &known_mech_attrs); + +        if (GSS_ERROR(major)) +            break; +    } + +    return major; +} + +/** + * List support attributes for a mech and/or all mechanisms. + * + * @param minor_status minor status code + * @param mech given together with mech_attr will return the list of + *        attributes for mechanism, can optionally be GSS_C_NO_OID. + * @param mech_attr see mech parameter, can optionally be NULL, + *        release with gss_release_oid_set(). + * @param known_mech_attrs all attributes for mechanisms supported, + *        release with gss_release_oid_set(). + * + * @ingroup gssapi + */ + +GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL +gss_inquire_attrs_for_mech(OM_uint32 * minor_status, +			   gss_const_OID mech, +			   gss_OID_set *mech_attr, +			   gss_OID_set *known_mech_attrs) +{ +    OM_uint32 major, junk; + +    if (known_mech_attrs) +        *known_mech_attrs = GSS_C_NO_OID_SET; + +    if (mech_attr && mech) { +	gssapi_mech_interface m; +        struct gss_mech_compat_desc_struct *gmc; + +	if ((m = __gss_get_mechanism(mech)) == NULL) { +	    *minor_status = 0; +	    return GSS_S_BAD_MECH; +	} + +        gmc = m->gm_compat; + +        if (gmc && gmc->gmc_inquire_attrs_for_mech) { +            major = gmc->gmc_inquire_attrs_for_mech(minor_status, +                                                    mech, +                                                    mech_attr, +                                                    known_mech_attrs); +        } else { +	    major = gss_create_empty_oid_set(minor_status, mech_attr); +            if (major == GSS_S_COMPLETE) +	        add_all_mo(m, mech_attr, GSS_MO_MA); +        } +	if (GSS_ERROR(major)) +	    return major; +    } + +    if (known_mech_attrs) { +	struct _gss_mech_switch *m; + +        if (*known_mech_attrs == GSS_C_NO_OID_SET) { +	    major = gss_create_empty_oid_set(minor_status, known_mech_attrs); +	    if (GSS_ERROR(major)) { +	        if (mech_attr) +		    gss_release_oid_set(&junk, mech_attr); +	        return major; +	    } +        } + +	_gss_load_mech(); + +	HEIM_SLIST_FOREACH(m, &_gss_mechs, gm_link) +	    add_all_mo(&m->gm_mech, known_mech_attrs, GSS_MO_MA); +    } + + +    return GSS_S_COMPLETE; +} + +/** + * Return names and descriptions of mech attributes + * + * @param minor_status minor status code + * @param mech_attr + * @param name + * @param short_desc + * @param long_desc + * + * @return returns GSS_S_COMPLETE or an error code. + */ + +GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL +gss_display_mech_attr(OM_uint32 * minor_status, +		      gss_const_OID mech_attr, +		      gss_buffer_t name, +		      gss_buffer_t short_desc, +		      gss_buffer_t long_desc) +{ +    struct _gss_oid_name_table *ma = NULL; +    OM_uint32 major; +    size_t n; + +    _mg_buffer_zero(name); +    _mg_buffer_zero(short_desc); +    _mg_buffer_zero(long_desc); + +    if (minor_status) +	*minor_status = 0; + +    for (n = 0; ma == NULL && _gss_ont_ma[n].oid; n++) +	if (gss_oid_equal(mech_attr, _gss_ont_ma[n].oid)) +	    ma = &_gss_ont_ma[n]; + +    if (ma == NULL) +	return GSS_S_BAD_MECH_ATTR; + +    if (name) { +	gss_buffer_desc bd; +	bd.value = rk_UNCONST(ma->name); +	bd.length = strlen(ma->name); +	major = _gss_copy_buffer(minor_status, &bd, name); +	if (major != GSS_S_COMPLETE) +	    return major; +    } + +    if (short_desc) { +	gss_buffer_desc bd; +	bd.value = rk_UNCONST(ma->short_desc); +	bd.length = strlen(ma->short_desc); +	major = _gss_copy_buffer(minor_status, &bd, short_desc); +	if (major != GSS_S_COMPLETE) +	    return major; +    } + +    if (long_desc) { +	gss_buffer_desc bd; +	bd.value = rk_UNCONST(ma->long_desc); +	bd.length = strlen(ma->long_desc); +	major = _gss_copy_buffer(minor_status, &bd, long_desc); +	if (major != GSS_S_COMPLETE) +	    return major; +    } + +    return GSS_S_COMPLETE; +}  | 
