Commit message: Fix IAKERB context export/import [CVE-2015-2698]

The patches for CVE-2015-2696 contained a regression in the newly
added IAKERB iakerb_gss_export_sec_context() function, which could
cause it to corrupt memory.  Fix the regression by properly
dereferencing the context_handle pointer before casting it.

Also, the patches did not implement an IAKERB gss_import_sec_context()
function, under the erroneous belief that an exported IAKERB context
would be tagged as a krb5 context.  Implement it now to allow IAKERB
contexts to be successfully exported and imported after establishment.

CVE-2015-2698:

In any MIT krb5 release with the patches for CVE-2015-2696 applied, an
application which calls gss_export_sec_context() may experience memory
corruption if the context was established using the IAKERB mechanism.
Historically, some vulnerabilities of this nature can be translated
into remote code execution, though the necessary exploits must be
tailored to the individual application and are usually quite
complicated.

    CVSSv2 Vector: AV:N/AC:H/Au:S/C:C/I:C/A:C/E:POC/RL:OF/RC:C

ticket: 8273 (new)
target_version: 1.14
tags: pullup
CVE: CVE-2015-2698
CWE: CWE-119
Vulnerability description: The iakerb_gss_export_sec_context function in lib/gssapi/krb5/iakerb.c in MIT Kerberos 5 (aka krb5) 1.14 pre-release 2015-09-14 improperly accesses a certain pointer, which allows remote authenticated users to cause a denial of service (memory corruption) or possibly have unspecified other impact by interacting with an application that calls the gss_export_sec_context function.  NOTE: this vulnerability exists because of an incorrect fix for CVE-2015-2696.

Project: krb5
File: gssapi_krb5.c

void gss_krb5int_lib_fini(void)
{
#ifndef _GSS_STATIC_LINK
    if (!INITIALIZER_RAN(gss_krb5int_lib_init) || PROGRAM_EXITING()) {
# ifdef SHOW_INITFINI_FUNCS
        printf("gss_krb5int_lib_fini: skipping\n");
# endif
        return;
    }
#endif
#ifdef SHOW_INITFINI_FUNCS
    printf("gss_krb5int_lib_fini\n");
#endif
    remove_error_table(&et_k5g_error_table);

    k5_key_delete(K5_KEY_GSS_KRB5_SET_CCACHE_OLD_NAME);
    k5_key_delete(K5_KEY_GSS_KRB5_CCACHE_NAME);
    k5_key_delete(K5_KEY_GSS_KRB5_ERROR_MESSAGE);
    k5_mutex_destroy(&kg_vdb.mutex);
#ifndef _WIN32
    k5_mutex_destroy(&kg_kdc_flag_mutex);
#endif
#ifndef LEAN_CLIENT
    k5_mutex_destroy(&gssint_krb5_keytab_lock);
#endif /* LEAN_CLIENT */
}

Project: krb5
File: gssapi_krb5.c

int gss_krb5int_lib_init(void)
{
    int err;

#ifdef SHOW_INITFINI_FUNCS
    printf("gss_krb5int_lib_init\n");
#endif

    add_error_table(&et_k5g_error_table);

#ifndef LEAN_CLIENT
    err = k5_mutex_finish_init(&gssint_krb5_keytab_lock);
    if (err)
        return err;
#endif /* LEAN_CLIENT */
    err = k5_key_register(K5_KEY_GSS_KRB5_SET_CCACHE_OLD_NAME, free);
    if (err)
        return err;
    err = k5_key_register(K5_KEY_GSS_KRB5_CCACHE_NAME, free);
    if (err)
        return err;
    err = k5_key_register(K5_KEY_GSS_KRB5_ERROR_MESSAGE,
                          krb5_gss_delete_error_info);
    if (err)
        return err;
#ifndef _WIN32
    err = k5_mutex_finish_init(&kg_kdc_flag_mutex);
    if (err)
        return err;
    err = k5_mutex_finish_init(&kg_vdb.mutex);
    if (err)
        return err;
#endif
#ifdef _GSS_STATIC_LINK
    err = gss_krb5mechglue_init();
    if (err)
        return err;
    err = gss_iakerbmechglue_init();
    if (err)
        return err;
#endif

    return 0;
}

Project: krb5
File: gssapi_krb5.c

static int gss_krb5mechglue_init(void)
{
    struct gss_mech_config mech_krb5;

    memset(&mech_krb5, 0, sizeof(mech_krb5));
    mech_krb5.mech = &krb5_mechanism;

    mech_krb5.mechNameStr = "kerberos_v5";
    mech_krb5.mech_type = (gss_OID)gss_mech_krb5;
    gssint_register_mechinfo(&mech_krb5);

    mech_krb5.mechNameStr = "kerberos_v5_old";
    mech_krb5.mech_type = (gss_OID)gss_mech_krb5_old;
    gssint_register_mechinfo(&mech_krb5);

    mech_krb5.mechNameStr = "mskrb";
    mech_krb5.mech_type = (gss_OID)gss_mech_krb5_wrong;
    gssint_register_mechinfo(&mech_krb5);

    return 0;
}

Project: krb5
File: gssapi_krb5.c

gss_mech_initialize(void)
{
    return &krb5_mechanism;
}

Project: krb5
File: gssapi_krb5.c

kg_caller_provided_ccache_name (OM_uint32 *minor_status,
                                int *out_caller_provided_name)
{
    if (out_caller_provided_name) {
        *out_caller_provided_name =
            (k5_getspecific(K5_KEY_GSS_KRB5_CCACHE_NAME) != NULL);
    }

    *minor_status = 0;
    return GSS_S_COMPLETE;
}

Project: krb5
File: gssapi_krb5.c

kg_get_ccache_name (OM_uint32 *minor_status, const char **out_name)
{
    const char *name = NULL;
    OM_uint32 err = 0;
    char *kg_ccache_name;

    kg_ccache_name = k5_getspecific(K5_KEY_GSS_KRB5_CCACHE_NAME);

    if (kg_ccache_name != NULL) {
        name = strdup(kg_ccache_name);
        if (name == NULL)
            err = ENOMEM;
    } else {
        krb5_context context = NULL;

        /* Reset the context default ccache (see text above), and then
           retrieve it.  */
        err = krb5_gss_init_context(&context);
        if (!err)
            err = krb5_cc_set_default_name (context, NULL);
        if (!err) {
            name = krb5_cc_default_name(context);
            if (name) {
                name = strdup(name);
                if (name == NULL)
                    err = ENOMEM;
            }
        }
        if (err && context)
            save_error_info(err, context);
        if (context)
            krb5_free_context(context);
    }

    if (!err) {
        if (out_name) {
            *out_name = name;
        }
    }

    *minor_status = err;
    return (*minor_status == 0) ? GSS_S_COMPLETE : GSS_S_FAILURE;
}

Project: krb5
File: gssapi_krb5.c

kg_get_defcred(minor_status, cred)
    OM_uint32 *minor_status;
    gss_cred_id_t *cred;
{
    OM_uint32 major;

    if ((major = krb5_gss_acquire_cred(minor_status,
                                       (gss_name_t) NULL, GSS_C_INDEFINITE,
                                       GSS_C_NULL_OID_SET, GSS_C_INITIATE,
                                       cred, NULL, NULL)) && GSS_ERROR(major)) {
        return(major);
    }
    *minor_status = 0;
    return(GSS_S_COMPLETE);
}

Project: krb5
File: gssapi_krb5.c

kg_sync_ccache_name (krb5_context context, OM_uint32 *minor_status)
{
    OM_uint32 err = 0;

    /*
     * Sync up the context ccache name with the GSSAPI ccache name.
     * If kg_ccache_name is NULL -- normal unless someone has called
     * gss_krb5_ccache_name() -- then the system default ccache will
     * be picked up and used by resetting the context default ccache.
     * This is needed for platforms which support multiple ccaches.
     */

    if (!err) {
        /* if NULL, resets the context default ccache */
        err = krb5_cc_set_default_name(context,
                                       (char *) k5_getspecific(K5_KEY_GSS_KRB5_CCACHE_NAME));
    }

    *minor_status = err;
    return (*minor_status == 0) ? GSS_S_COMPLETE : GSS_S_FAILURE;
}

Project: krb5
File: gssapi_krb5.c

krb5_gss_authorize_localname(OM_uint32 *minor,
                             const gss_name_t pname,
                             gss_const_buffer_t local_user,
                             gss_const_OID name_type)
{
    krb5_context context;
    krb5_error_code code;
    krb5_gss_name_t kname;
    char *user;
    int user_ok;

    if (name_type != GSS_C_NO_OID &&
        !g_OID_equal(name_type, GSS_C_NT_USER_NAME)) {
        return GSS_S_BAD_NAMETYPE;
    }

    kname = (krb5_gss_name_t)pname;

    code = krb5_gss_init_context(&context);
    if (code != 0) {
        *minor = code;
        return GSS_S_FAILURE;
    }

    user = k5memdup0(local_user->value, local_user->length, &code);
    if (user == NULL) {
        *minor = code;
        krb5_free_context(context);
        return GSS_S_FAILURE;
    }

    user_ok = krb5_kuserok(context, kname->princ, user);

    free(user);
    krb5_free_context(context);

    *minor = 0;
    return user_ok ? GSS_S_COMPLETE : GSS_S_UNAUTHORIZED;
}

Project: krb5

krb5_gss_inquire_attrs_for_mech(OM_uint32 *minor_status,
                                gss_const_OID mech,
                                gss_OID_set *mech_attrs,
                                gss_OID_set *known_mech_attrs)
{
    OM_uint32 major, tmpMinor;

    if (mech_attrs == NULL) {
        *minor_status = 0;
        return GSS_S_COMPLETE;
    }

    major = gss_create_empty_oid_set(minor_status, mech_attrs);
    if (GSS_ERROR(major))
        goto cleanup;

#define MA_SUPPORTED(ma)    do {                                        \
        major = gss_add_oid_set_member(minor_status, (gss_OID)ma,       \
                                       mech_attrs);                     \
        if (GSS_ERROR(major))                                           \
            goto cleanup;                                               \
    } while (0)

    MA_SUPPORTED(GSS_C_MA_MECH_CONCRETE);
    MA_SUPPORTED(GSS_C_MA_ITOK_FRAMED);
    MA_SUPPORTED(GSS_C_MA_AUTH_INIT);
    MA_SUPPORTED(GSS_C_MA_AUTH_TARG);
    MA_SUPPORTED(GSS_C_MA_DELEG_CRED);
    MA_SUPPORTED(GSS_C_MA_INTEG_PROT);
    MA_SUPPORTED(GSS_C_MA_CONF_PROT);
    MA_SUPPORTED(GSS_C_MA_MIC);
    MA_SUPPORTED(GSS_C_MA_WRAP);
    MA_SUPPORTED(GSS_C_MA_PROT_READY);
    MA_SUPPORTED(GSS_C_MA_REPLAY_DET);
    MA_SUPPORTED(GSS_C_MA_OOS_DET);
    MA_SUPPORTED(GSS_C_MA_CBINDINGS);
    MA_SUPPORTED(GSS_C_MA_CTX_TRANS);

    if (g_OID_equal(mech, gss_mech_iakerb)) {
        MA_SUPPORTED(GSS_C_MA_AUTH_INIT_INIT);
        MA_SUPPORTED(GSS_C_MA_NOT_DFLT_MECH);
    } else if (!g_OID_equal(mech, gss_mech_krb5)) {
        MA_SUPPORTED(GSS_C_MA_DEPRECATED);
    }

cleanup:
    if (GSS_ERROR(major))
        gss_release_oid_set(&tmpMinor, mech_attrs);

    return major;
}

Project: krb5
File: gssapi_krb5.c

krb5_gss_inquire_cred_by_oid(OM_uint32 *minor_status,
                             const gss_cred_id_t cred_handle,
                             const gss_OID desired_object,
                             gss_buffer_set_t *data_set)
{
    OM_uint32 major_status = GSS_S_FAILURE;
#if 0
    size_t i;
#endif

    if (minor_status == NULL)
        return GSS_S_CALL_INACCESSIBLE_WRITE;

    *minor_status = 0;

    if (desired_object == GSS_C_NO_OID)
        return GSS_S_CALL_INACCESSIBLE_READ;

    if (data_set == NULL)
        return GSS_S_CALL_INACCESSIBLE_WRITE;

    *data_set = GSS_C_NO_BUFFER_SET;
    if (cred_handle == GSS_C_NO_CREDENTIAL) {
        *minor_status = (OM_uint32)KRB5_NOCREDS_SUPPLIED;
        return GSS_S_NO_CRED;
    }

    major_status = krb5_gss_validate_cred(minor_status, cred_handle);
    if (GSS_ERROR(major_status))
        return major_status;

#if 0
    for (i = 0; i < sizeof(krb5_gss_inquire_cred_by_oid_ops)/
             sizeof(krb5_gss_inquire_cred_by_oid_ops[0]); i++) {
        if (g_OID_prefix_equal(desired_object, &krb5_gss_inquire_cred_by_oid_ops[i].oid)) {
            return (*krb5_gss_inquire_cred_by_oid_ops[i].func)(minor_status,
                                                               cred_handle,
                                                               desired_object,
                                                               data_set);
        }
    }
#endif

    *minor_status = EINVAL;

    return GSS_S_UNAVAILABLE;
}

Project: krb5
File: gssapi_krb5.c

krb5_gss_inquire_mech_for_saslname(OM_uint32 *minor_status,
                                   const gss_buffer_t sasl_mech_name,
                                   gss_OID *mech_type)
{
    *minor_status = 0;

    if (sasl_mech_name->length == GS2_KRB5_SASL_NAME_LEN &&
        memcmp(sasl_mech_name->value,
               GS2_KRB5_SASL_NAME, GS2_KRB5_SASL_NAME_LEN) == 0) {
        if (mech_type != NULL)
            *mech_type = (gss_OID)gss_mech_krb5;
        return GSS_S_COMPLETE;
    } else if (sasl_mech_name->length == GS2_IAKERB_SASL_NAME_LEN &&
               memcmp(sasl_mech_name->value,
                      GS2_IAKERB_SASL_NAME, GS2_IAKERB_SASL_NAME_LEN) == 0) {
        if (mech_type != NULL)
            *mech_type = (gss_OID)gss_mech_iakerb;
        return GSS_S_COMPLETE;
    }

    return GSS_S_BAD_MECH;
}

Project: krb5
File: gssapi_krb5.c

krb5_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)
{
    if (g_OID_equal(desired_mech, gss_mech_iakerb)) {
        if (!g_make_string_buffer(GS2_IAKERB_SASL_NAME, sasl_mech_name) ||
            !g_make_string_buffer("iakerb", mech_name) ||
            !g_make_string_buffer("Initial and Pass Through Authentication "
                                  "Kerberos Mechanism (IAKERB)",
                                  mech_description))
            goto fail;
    } else {
        if (!g_make_string_buffer(GS2_KRB5_SASL_NAME, sasl_mech_name) ||
            !g_make_string_buffer("krb5", mech_name) ||
            !g_make_string_buffer("Kerberos 5 GSS-API Mechanism",
                                  mech_description))
            goto fail;
    }

    *minor_status = 0;
    return GSS_S_COMPLETE;

fail:
    *minor_status = ENOMEM;
    return GSS_S_FAILURE;
}

Project: krb5
File: gssapi_krb5.c

krb5_gss_inquire_sec_context_by_oid (OM_uint32 *minor_status,
                                     const gss_ctx_id_t context_handle,
                                     const gss_OID desired_object,
                                     gss_buffer_set_t *data_set)
{
    krb5_gss_ctx_id_rec *ctx;
    size_t i;

    if (minor_status == NULL)
        return GSS_S_CALL_INACCESSIBLE_WRITE;

    *minor_status = 0;

    if (desired_object == GSS_C_NO_OID)
        return GSS_S_CALL_INACCESSIBLE_READ;

    if (data_set == NULL)
        return GSS_S_CALL_INACCESSIBLE_WRITE;

    *data_set = GSS_C_NO_BUFFER_SET;

    ctx = (krb5_gss_ctx_id_rec *) context_handle;

    if (ctx->terminated || !ctx->established)
        return GSS_S_NO_CONTEXT;

    for (i = 0; i < sizeof(krb5_gss_inquire_sec_context_by_oid_ops)/
             sizeof(krb5_gss_inquire_sec_context_by_oid_ops[0]); i++) {
        if (g_OID_prefix_equal(desired_object, &krb5_gss_inquire_sec_context_by_oid_ops[i].oid)) {
            return (*krb5_gss_inquire_sec_context_by_oid_ops[i].func)(minor_status,
                                                                      context_handle,
                                                                      desired_object,
                                                                      data_set);
        }
    }

    *minor_status = EINVAL;

    return GSS_S_UNAVAILABLE;
}

Project: krb5
File: gssapi_krb5.c

krb5_gss_set_sec_context_option (OM_uint32 *minor_status,
                                 gss_ctx_id_t *context_handle,
                                 const gss_OID desired_object,
                                 const gss_buffer_t value)
{
#if 0
    size_t i;
#endif

    if (minor_status == NULL)
        return GSS_S_CALL_INACCESSIBLE_WRITE;

    *minor_status = 0;

    if (context_handle == NULL)
        return GSS_S_CALL_INACCESSIBLE_READ;

    if (desired_object == GSS_C_NO_OID)
        return GSS_S_CALL_INACCESSIBLE_READ;

#if 0
    for (i = 0; i < sizeof(krb5_gss_set_sec_context_option_ops)/
             sizeof(krb5_gss_set_sec_context_option_ops[0]); i++) {
        if (g_OID_prefix_equal(desired_object, &krb5_gss_set_sec_context_option_ops[i].oid)) {
            return (*krb5_gss_set_sec_context_option_ops[i].func)(minor_status,
                                                                  context_handle,
                                                                  desired_object,
                                                                  value);
        }
    }
#endif

    *minor_status = EINVAL;

    return GSS_S_UNAVAILABLE;
}

Project: krb5
File: gssapi_krb5.c

krb5_gssspi_mech_invoke (OM_uint32 *minor_status,
                         const gss_OID desired_mech,
                         const gss_OID desired_object,
                         gss_buffer_t value)
{
    size_t i;

    if (minor_status == NULL)
        return GSS_S_CALL_INACCESSIBLE_WRITE;

    *minor_status = 0;

    if (desired_mech == GSS_C_NO_OID)
        return GSS_S_BAD_MECH;

    if (desired_object == GSS_C_NO_OID)
        return GSS_S_CALL_INACCESSIBLE_READ;

    for (i = 0; i < sizeof(krb5_gssspi_mech_invoke_ops)/
             sizeof(krb5_gssspi_mech_invoke_ops[0]); i++) {
        if (g_OID_prefix_equal(desired_object, &krb5_gssspi_mech_invoke_ops[i].oid)) {
            return (*krb5_gssspi_mech_invoke_ops[i].func)(minor_status,
                                                          desired_mech,
                                                          desired_object,
                                                          value);
        }
    }

    *minor_status = EINVAL;

    return GSS_S_UNAVAILABLE;
}

Project: krb5

no_ci_flags(OM_uint32 *minor_status,
            gss_cred_id_t *cred_handle,
            const gss_OID desired_oid,
            const gss_buffer_t value)
{
    krb5_gss_cred_id_t cred;

    cred = (krb5_gss_cred_id_t) *cred_handle;
    cred->suppress_ci_flags = 1;

    *minor_status = 0;
    return GSS_S_COMPLETE;
}

Project: krb5
File: iakerb.c

iakerb_acceptor_step(iakerb_ctx_id_t ctx,
                     int initialContextToken,
                     const gss_buffer_t input_token,
                     gss_buffer_t output_token)
{
    krb5_error_code code;
    krb5_data request = empty_data(), reply = empty_data();
    krb5_data realm = empty_data();
    OM_uint32 tmp;
    int tcp_only, use_master;
    krb5_ui_4 kdc_code;

    output_token->length = 0;
    output_token->value = NULL;

    if (ctx->count >= IAKERB_MAX_HOPS) {
        code = KRB5_KDC_UNREACH;
        goto cleanup;
    }

    code = iakerb_parse_token(ctx, initialContextToken, input_token, &realm,
                              NULL, &request);
    if (code != 0)
        goto cleanup;

    if (realm.length == 0 || request.length == 0) {
        code = KRB5_BAD_MSIZE;
        goto cleanup;
    }

    code = iakerb_save_token(ctx, input_token);
    if (code != 0)
        goto cleanup;

    for (tcp_only = 0; tcp_only <= 1; tcp_only++) {
        use_master = 0;
        code = krb5_sendto_kdc(ctx->k5c, &request, &realm,
                               &reply, &use_master, tcp_only);
        if (code == 0 && krb5_is_krb_error(&reply)) {
            krb5_error *error;

            code = decode_krb5_error(&reply, &error);
            if (code != 0)
                goto cleanup;
            kdc_code = error->error;
            krb5_free_error(ctx->k5c, error);
            if (kdc_code == KRB_ERR_RESPONSE_TOO_BIG) {
                krb5_free_data_contents(ctx->k5c, &reply);
                reply = empty_data();
                continue;
            }
        }
        break;
    }

    if (code == KRB5_KDC_UNREACH || code == KRB5_REALM_UNKNOWN) {
        krb5_error error;

        memset(&error, 0, sizeof(error));
        if (code == KRB5_KDC_UNREACH)
            error.error = KRB_AP_ERR_IAKERB_KDC_NO_RESPONSE;
        else if (code == KRB5_REALM_UNKNOWN)
            error.error = KRB_AP_ERR_IAKERB_KDC_NOT_FOUND;

        code = krb5_mk_error(ctx->k5c, &error, &reply);
        if (code != 0)
            goto cleanup;
    } else if (code != 0)
        goto cleanup;

    code = iakerb_make_token(ctx, &realm, NULL, &reply, 0, output_token);
    if (code != 0)
        goto cleanup;

    code = iakerb_save_token(ctx, output_token);
    if (code != 0)
        goto cleanup;

    ctx->count++;

cleanup:
    if (code != 0)
        gss_release_buffer(&tmp, output_token);
    /* request is a pointer into input_token, no need to free */
    krb5_free_data_contents(ctx->k5c, &realm);
    krb5_free_data_contents(ctx->k5c, &reply);

    return code;
}

Project: krb5
File: iakerb.c

iakerb_alloc_context(iakerb_ctx_id_t *pctx, int initiate)
{
    iakerb_ctx_id_t ctx;
    krb5_error_code code;

    *pctx = NULL;

    ctx = k5alloc(sizeof(*ctx), &code);
    if (ctx == NULL)
        goto cleanup;
    ctx->defcred = GSS_C_NO_CREDENTIAL;
    ctx->magic = KG_IAKERB_CONTEXT;
    ctx->state = IAKERB_AS_REQ;
    ctx->count = 0;
    ctx->initiate = initiate;
    ctx->established = 0;

    code = krb5_gss_init_context(&ctx->k5c);
    if (code != 0)
        goto cleanup;

    *pctx = ctx;

cleanup:
    if (code != 0)
        iakerb_release_context(ctx);

    return code;
}

Project: krb5
File: iakerb.c

iakerb_gss_accept_sec_context(OM_uint32 *minor_status,
                              gss_ctx_id_t *context_handle,
                              gss_cred_id_t verifier_cred_handle,
                              gss_buffer_t input_token,
                              gss_channel_bindings_t input_chan_bindings,
                              gss_name_t *src_name,
                              gss_OID *mech_type,
                              gss_buffer_t output_token,
                              OM_uint32 *ret_flags,
                              OM_uint32 *time_rec,
                              gss_cred_id_t *delegated_cred_handle)
{
    OM_uint32 major_status = GSS_S_FAILURE;
    OM_uint32 code;
    iakerb_ctx_id_t ctx;
    int initialContextToken = (*context_handle == GSS_C_NO_CONTEXT);

    if (initialContextToken) {
        code = iakerb_alloc_context(&ctx, 0);
        if (code != 0)
            goto cleanup;

    } else
        ctx = (iakerb_ctx_id_t)*context_handle;

    if (iakerb_is_iakerb_token(input_token)) {
        if (ctx->gssc != GSS_C_NO_CONTEXT) {
            /* We shouldn't get an IAKERB token now. */
            code = G_WRONG_TOKID;
            major_status = GSS_S_DEFECTIVE_TOKEN;
            goto cleanup;
        }
        code = iakerb_acceptor_step(ctx, initialContextToken,
                                    input_token, output_token);
        if (code == (OM_uint32)KRB5_BAD_MSIZE)
            major_status = GSS_S_DEFECTIVE_TOKEN;
        if (code != 0)
            goto cleanup;
        if (initialContextToken) {
            *context_handle = (gss_ctx_id_t)ctx;
            ctx = NULL;
        }
        if (src_name != NULL)
            *src_name = GSS_C_NO_NAME;
        if (mech_type != NULL)
            *mech_type = (gss_OID)gss_mech_iakerb;
        if (ret_flags != NULL)
            *ret_flags = 0;
        if (time_rec != NULL)
            *time_rec = 0;
        if (delegated_cred_handle != NULL)
            *delegated_cred_handle = GSS_C_NO_CREDENTIAL;
        major_status = GSS_S_CONTINUE_NEEDED;
    } else {
        krb5_gss_ctx_ext_rec exts;

        iakerb_make_exts(ctx, &exts);

        major_status = krb5_gss_accept_sec_context_ext(&code,
                                                       &ctx->gssc,
                                                       verifier_cred_handle,
                                                       input_token,
                                                       input_chan_bindings,
                                                       src_name,
                                                       NULL,
                                                       output_token,
                                                       ret_flags,
                                                       time_rec,
                                                       delegated_cred_handle,
                                                       &exts);
        if (major_status == GSS_S_COMPLETE)
            ctx->established = 1;
        if (mech_type != NULL)
            *mech_type = (gss_OID)gss_mech_krb5;
    }

cleanup:
    if (initialContextToken && GSS_ERROR(major_status)) {
        iakerb_release_context(ctx);
        *context_handle = GSS_C_NO_CONTEXT;
    }

    *minor_status = code;
    return major_status;
}

Project: krb5
File: iakerb.c

iakerb_gss_context_time(OM_uint32 *minor_status, gss_ctx_id_t context_handle,
                        OM_uint32 *time_rec)
{
    iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle;

    if (ctx->gssc == GSS_C_NO_CONTEXT)
        return GSS_S_NO_CONTEXT;

    return krb5_gss_context_time(minor_status, ctx->gssc, time_rec);
}

Project: krb5
File: iakerb.c

iakerb_gss_delete_sec_context(OM_uint32 *minor_status,
                              gss_ctx_id_t *context_handle,
                              gss_buffer_t output_token)
{
    iakerb_ctx_id_t iakerb_ctx = (iakerb_ctx_id_t)*context_handle;

    if (output_token != GSS_C_NO_BUFFER) {
        output_token->length = 0;
        output_token->value = NULL;
    }

    *minor_status = 0;
    *context_handle = GSS_C_NO_CONTEXT;
    iakerb_release_context(iakerb_ctx);

    return GSS_S_COMPLETE;
}

Project: krb5
File: iakerb.c

iakerb_gss_get_mic(OM_uint32 *minor_status, gss_ctx_id_t context_handle,
                   gss_qop_t qop_req, gss_buffer_t message_buffer,
                   gss_buffer_t message_token)
{
    iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle;

    if (ctx->gssc == GSS_C_NO_CONTEXT)
        return GSS_S_NO_CONTEXT;

    return krb5_gss_get_mic(minor_status, ctx->gssc, qop_req, message_buffer,
                            message_token);
}

Project: krb5
File: iakerb.c

iakerb_gss_get_mic_iov(OM_uint32 *minor_status, gss_ctx_id_t context_handle,
                       gss_qop_t qop_req, gss_iov_buffer_desc *iov,
                       int iov_count)
{
    iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle;

    if (ctx->gssc == GSS_C_NO_CONTEXT)
        return GSS_S_NO_CONTEXT;

    return krb5_gss_get_mic_iov(minor_status, ctx->gssc, qop_req, iov,
                                iov_count);
}

Project: krb5
File: iakerb.c

iakerb_gss_get_mic_iov_length(OM_uint32 *minor_status,
                              gss_ctx_id_t context_handle, gss_qop_t qop_req,
                              gss_iov_buffer_desc *iov, int iov_count)
{
    iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle;

    if (ctx->gssc == GSS_C_NO_CONTEXT)
        return GSS_S_NO_CONTEXT;

    return krb5_gss_get_mic_iov_length(minor_status, ctx->gssc, qop_req, iov,
                                       iov_count);
}

Project: krb5
File: iakerb.c

iakerb_gss_init_sec_context(OM_uint32 *minor_status,
                            gss_cred_id_t claimant_cred_handle,
                            gss_ctx_id_t *context_handle,
                            gss_name_t target_name,
                            gss_OID mech_type,
                            OM_uint32 req_flags,
                            OM_uint32 time_req,
                            gss_channel_bindings_t input_chan_bindings,
                            gss_buffer_t input_token,
                            gss_OID *actual_mech_type,
                            gss_buffer_t output_token,
                            OM_uint32 *ret_flags,
                            OM_uint32 *time_rec)
{
    OM_uint32 major_status = GSS_S_FAILURE;
    krb5_error_code code;
    iakerb_ctx_id_t ctx;
    krb5_gss_cred_id_t kcred;
    krb5_gss_name_t kname;
    krb5_boolean cred_locked = FALSE;
    int initialContextToken = (*context_handle == GSS_C_NO_CONTEXT);

    if (initialContextToken) {
        code = iakerb_alloc_context(&ctx, 1);
        if (code != 0) {
            *minor_status = code;
            goto cleanup;
        }
        if (claimant_cred_handle == GSS_C_NO_CREDENTIAL) {
            major_status = iakerb_gss_acquire_cred(minor_status, NULL,
                                                   GSS_C_INDEFINITE,
                                                   GSS_C_NULL_OID_SET,
                                                   GSS_C_INITIATE,
                                                   &ctx->defcred, NULL, NULL);
            if (GSS_ERROR(major_status))
                goto cleanup;
            claimant_cred_handle = ctx->defcred;
        }
    } else {
        ctx = (iakerb_ctx_id_t)*context_handle;
        if (claimant_cred_handle == GSS_C_NO_CREDENTIAL)
            claimant_cred_handle = ctx->defcred;
    }

    kname = (krb5_gss_name_t)target_name;

    major_status = kg_cred_resolve(minor_status, ctx->k5c,
                                   claimant_cred_handle, target_name);
    if (GSS_ERROR(major_status))
        goto cleanup;
    cred_locked = TRUE;
    kcred = (krb5_gss_cred_id_t)claimant_cred_handle;

    major_status = GSS_S_FAILURE;

    if (initialContextToken) {
        code = iakerb_get_initial_state(ctx, kcred, kname, time_req,
                                        &ctx->state);
        if (code != 0) {
            *minor_status = code;
            goto cleanup;
        }
        *context_handle = (gss_ctx_id_t)ctx;
    }

    if (ctx->state != IAKERB_AP_REQ) {
        /* We need to do IAKERB. */
        code = iakerb_initiator_step(ctx,
                                     kcred,
                                     kname,
                                     time_req,
                                     input_token,
                                     output_token);
        if (code == KRB5_BAD_MSIZE)
            major_status = GSS_S_DEFECTIVE_TOKEN;
        if (code != 0) {
            *minor_status = code;
            goto cleanup;
        }
    }

    if (ctx->state == IAKERB_AP_REQ) {
        krb5_gss_ctx_ext_rec exts;

        if (cred_locked) {
            k5_mutex_unlock(&kcred->lock);
            cred_locked = FALSE;
        }

        iakerb_make_exts(ctx, &exts);

        if (ctx->gssc == GSS_C_NO_CONTEXT)
            input_token = GSS_C_NO_BUFFER;

        /* IAKERB is finished, or we skipped to Kerberos directly. */
        major_status = krb5_gss_init_sec_context_ext(minor_status,
                                                     (gss_cred_id_t) kcred,
                                                     &ctx->gssc,
                                                     target_name,
                                                     (gss_OID)gss_mech_iakerb,
                                                     req_flags,
                                                     time_req,
                                                     input_chan_bindings,
                                                     input_token,
                                                     NULL,
                                                     output_token,
                                                     ret_flags,
                                                     time_rec,
                                                     &exts);
        if (major_status == GSS_S_COMPLETE)
            ctx->established = 1;
        if (actual_mech_type != NULL)
            *actual_mech_type = (gss_OID)gss_mech_krb5;
    } else {
        if (actual_mech_type != NULL)
            *actual_mech_type = (gss_OID)gss_mech_iakerb;
        if (ret_flags != NULL)
            *ret_flags = 0;
        if (time_rec != NULL)
            *time_rec = 0;
        major_status = GSS_S_CONTINUE_NEEDED;
    }

cleanup:
    if (cred_locked)
        k5_mutex_unlock(&kcred->lock);
    if (initialContextToken && GSS_ERROR(major_status)) {
        iakerb_release_context(ctx);
        *context_handle = GSS_C_NO_CONTEXT;
    }

    return major_status;
}

Project: krb5
File: iakerb.c

iakerb_gss_inquire_context(OM_uint32 *minor_status,
                           gss_ctx_id_t context_handle, gss_name_t *src_name,
                           gss_name_t *targ_name, OM_uint32 *lifetime_rec,
                           gss_OID *mech_type, OM_uint32 *ctx_flags,
                           int *initiate, int *opened)
{
    OM_uint32 ret;
    iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle;

    if (src_name != NULL)
        *src_name = GSS_C_NO_NAME;
    if (targ_name != NULL)
        *targ_name = GSS_C_NO_NAME;
    if (lifetime_rec != NULL)
        *lifetime_rec = 0;
    if (mech_type != NULL)
        *mech_type = (gss_OID)gss_mech_iakerb;
    if (ctx_flags != NULL)
        *ctx_flags = 0;
    if (initiate != NULL)
        *initiate = ctx->initiate;
    if (opened != NULL)
        *opened = ctx->established;

    if (ctx->gssc == GSS_C_NO_CONTEXT)
        return GSS_S_COMPLETE;

    ret = krb5_gss_inquire_context(minor_status, ctx->gssc, src_name,
                                   targ_name, lifetime_rec, mech_type,
                                   ctx_flags, initiate, opened);

    if (!ctx->established) {
        /* Report IAKERB as the mech OID until the context is established. */
        if (mech_type != NULL)
            *mech_type = (gss_OID)gss_mech_iakerb;

        /* We don't support exporting partially-established contexts. */
        if (ctx_flags != NULL)
            *ctx_flags &= ~GSS_C_TRANS_FLAG;
    }

    return ret;
}

Project: krb5
File: iakerb.c

iakerb_gss_process_context_token(OM_uint32 *minor_status,
                                 const gss_ctx_id_t context_handle,
                                 const gss_buffer_t token_buffer)
{
    iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle;

    if (ctx->gssc == GSS_C_NO_CONTEXT)
        return GSS_S_DEFECTIVE_TOKEN;

    return krb5_gss_process_context_token(minor_status, ctx->gssc,
                                          token_buffer);
}

Project: krb5
File: iakerb.c

iakerb_gss_pseudo_random(OM_uint32 *minor_status, gss_ctx_id_t context_handle,
                         int prf_key, const gss_buffer_t prf_in,
                         ssize_t desired_output_len, gss_buffer_t prf_out)
{
    iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle;

    if (ctx->gssc == GSS_C_NO_CONTEXT)
        return GSS_S_NO_CONTEXT;

    return krb5_gss_pseudo_random(minor_status, ctx->gssc, prf_key, prf_in,
                                  desired_output_len, prf_out);
}

Project: krb5
File: iakerb.c

iakerb_gss_set_sec_context_option(OM_uint32 *minor_status,
                                  gss_ctx_id_t *context_handle,
                                  const gss_OID desired_object,
                                  const gss_buffer_t value)
{
    iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)*context_handle;

    if (ctx == NULL || ctx->gssc == GSS_C_NO_CONTEXT)
        return GSS_S_UNAVAILABLE;

    return krb5_gss_set_sec_context_option(minor_status, &ctx->gssc,
                                           desired_object, value);
}

Project: krb5
File: iakerb.c

iakerb_gss_unwrap(OM_uint32 *minor_status, gss_ctx_id_t context_handle,
                  gss_buffer_t input_message_buffer,
                  gss_buffer_t output_message_buffer, int *conf_state,
                  gss_qop_t *qop_state)
{
    iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle;

    if (ctx->gssc == GSS_C_NO_CONTEXT)
        return GSS_S_NO_CONTEXT;

    return krb5_gss_unwrap(minor_status, ctx->gssc, input_message_buffer,
                           output_message_buffer, conf_state, qop_state);
}

Project: krb5
File: iakerb.c

iakerb_gss_unwrap_iov(OM_uint32 *minor_status, gss_ctx_id_t context_handle,
                      int *conf_state, gss_qop_t *qop_state,
                      gss_iov_buffer_desc *iov, int iov_count)
{
    iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle;

    if (ctx->gssc == GSS_C_NO_CONTEXT)
        return GSS_S_NO_CONTEXT;

    return krb5_gss_unwrap_iov(minor_status, ctx->gssc, conf_state, qop_state,
                               iov, iov_count);
}

Project: krb5
File: iakerb.c

iakerb_gss_verify_mic(OM_uint32 *minor_status, gss_ctx_id_t context_handle,
                      gss_buffer_t msg_buffer, gss_buffer_t token_buffer,
                      gss_qop_t *qop_state)
{
    iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle;

    if (ctx->gssc == GSS_C_NO_CONTEXT)
        return GSS_S_NO_CONTEXT;

    return krb5_gss_verify_mic(minor_status, ctx->gssc, msg_buffer,
                               token_buffer, qop_state);
}

Project: krb5
File: iakerb.c

iakerb_gss_wrap(OM_uint32 *minor_status, gss_ctx_id_t context_handle,
                int conf_req_flag, gss_qop_t qop_req,
                gss_buffer_t input_message_buffer, int *conf_state,
                gss_buffer_t output_message_buffer)
{
    iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle;

    if (ctx->gssc == GSS_C_NO_CONTEXT)
        return GSS_S_NO_CONTEXT;

    return krb5_gss_wrap(minor_status, ctx->gssc, conf_req_flag, qop_req,
                         input_message_buffer, conf_state,
                         output_message_buffer);
}

Project: krb5
File: iakerb.c

iakerb_gss_wrap_iov(OM_uint32 *minor_status, gss_ctx_id_t context_handle,
                    int conf_req_flag, gss_qop_t qop_req, int *conf_state,
                    gss_iov_buffer_desc *iov, int iov_count)
{
    iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle;

    if (ctx->gssc == GSS_C_NO_CONTEXT)
        return GSS_S_NO_CONTEXT;

    return krb5_gss_wrap_iov(minor_status, ctx->gssc, conf_req_flag, qop_req,
                             conf_state, iov, iov_count);
}

Project: krb5
File: iakerb.c

iakerb_gss_wrap_iov_length(OM_uint32 *minor_status,
                           gss_ctx_id_t context_handle, int conf_req_flag,
                           gss_qop_t qop_req, int *conf_state,
                           gss_iov_buffer_desc *iov, int iov_count)
{
    iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle;

    if (ctx->gssc == GSS_C_NO_CONTEXT)
        return GSS_S_NO_CONTEXT;

    return krb5_gss_wrap_iov_length(minor_status, ctx->gssc, conf_req_flag,
                                    qop_req, conf_state, iov, iov_count);
}

Project: krb5
File: iakerb.c

iakerb_gss_wrap_size_limit(OM_uint32 *minor_status,
                           gss_ctx_id_t context_handle, int conf_req_flag,
                           gss_qop_t qop_req, OM_uint32 req_output_size,
                           OM_uint32 *max_input_size)
{
    iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle;

    if (ctx->gssc == GSS_C_NO_CONTEXT)
        return GSS_S_NO_CONTEXT;

    return krb5_gss_wrap_size_limit(minor_status, ctx->gssc, conf_req_flag,
                                    qop_req, req_output_size, max_input_size);
}

Project: krb5
File: iakerb.c

iakerb_init_creds_ctx(iakerb_ctx_id_t ctx,
                      krb5_gss_cred_id_t cred,
                      OM_uint32 time_req)
{
    krb5_error_code code;

    if (cred->iakerb_mech == 0) {
        code = EINVAL;
        goto cleanup;
    }

    assert(cred->name != NULL);
    assert(cred->name->princ != NULL);

    code = krb5_get_init_creds_opt_alloc(ctx->k5c, &ctx->gic_opts);
    if (code != 0)
        goto cleanup;

    if (time_req != 0 && time_req != GSS_C_INDEFINITE)
        krb5_get_init_creds_opt_set_tkt_life(ctx->gic_opts, time_req);

    code = krb5_get_init_creds_opt_set_out_ccache(ctx->k5c, ctx->gic_opts,
                                                  cred->ccache);
    if (code != 0)
        goto cleanup;

    code = krb5_init_creds_init(ctx->k5c,
                                cred->name->princ,
                                NULL,   /* prompter */
                                NULL,   /* data */
                                0,      /* start_time */
                                ctx->gic_opts,
                                &ctx->icc);
    if (code != 0)
        goto cleanup;

    if (cred->password != NULL) {
        code = krb5_init_creds_set_password(ctx->k5c, ctx->icc,
                                            cred->password);
    } else {
        code = krb5_init_creds_set_keytab(ctx->k5c, ctx->icc,
                                          cred->client_keytab);
    }
    if (code != 0)
        goto cleanup;

cleanup:
    return code;
}

Project: krb5
File: iakerb.c

iakerb_is_iakerb_token(const gss_buffer_t token)
{
    krb5_error_code code;
    unsigned int bodysize = token->length;
    unsigned char *ptr = token->value;

    code = g_verify_token_header(gss_mech_iakerb,
                                 &bodysize, &ptr,
                                 IAKERB_TOK_PROXY,
                                 token->length, 0);

    return (code == 0);
}

Project: krb5
File: iakerb.c

iakerb_make_exts(iakerb_ctx_id_t ctx, krb5_gss_ctx_ext_rec *exts)
{
    memset(exts, 0, sizeof(*exts));

    if (ctx->conv.length != 0)
        exts->iakerb.conv = &ctx->conv;
}

Project: krb5
File: iakerb.c

iakerb_make_token(iakerb_ctx_id_t ctx,
                  krb5_data *realm,
                  krb5_data *cookie,
                  krb5_data *request,
                  int initialContextToken,
                  gss_buffer_t token)
{
    krb5_error_code code;
    krb5_iakerb_header iah;
    krb5_data *data = NULL;
    char *p;
    unsigned int tokenSize;
    unsigned char *q;

    token->value = NULL;
    token->length = 0;

    /*
     * Assemble the IAKERB-HEADER from the realm and cookie
     */
    memset(&iah, 0, sizeof(iah));
    iah.target_realm = *realm;
    iah.cookie = cookie;

    code = encode_krb5_iakerb_header(&iah, &data);
    if (code != 0)
        goto cleanup;

    /*
     * Concatenate Kerberos request.
     */
    p = realloc(data->data, data->length + request->length);
    if (p == NULL) {
        code = ENOMEM;
        goto cleanup;
    }
    data->data = p;

    if (request->length > 0)
        memcpy(data->data + data->length, request->data, request->length);
    data->length += request->length;

    if (initialContextToken)
        tokenSize = g_token_size(gss_mech_iakerb, data->length);
    else
        tokenSize = 2 + data->length;

    token->value = q = gssalloc_malloc(tokenSize);
    if (q == NULL) {
        code = ENOMEM;
        goto cleanup;
    }
    token->length = tokenSize;

    if (initialContextToken) {
        g_make_token_header(gss_mech_iakerb, data->length, &q,
                            IAKERB_TOK_PROXY);
    } else {
        store_16_be(IAKERB_TOK_PROXY, q);
        q += 2;
    }
    memcpy(q, data->data, data->length);
    q += data->length;

    assert(q == (unsigned char *)token->value + token->length);

cleanup:
    krb5_free_data(ctx->k5c, data);

    return code;
}

Project: krb5
File: iakerb.c

iakerb_parse_token(iakerb_ctx_id_t ctx,
                   int initialContextToken,
                   const gss_buffer_t token,
                   krb5_data *realm,
                   krb5_data **cookie,
                   krb5_data *request)
{
    krb5_error_code code;
    krb5_iakerb_header *iah = NULL;
    unsigned int bodysize, lenlen;
    int length;
    unsigned char *ptr;
    int flags = 0;
    krb5_data data;

    if (token == GSS_C_NO_BUFFER || token->length == 0) {
        code = KRB5_BAD_MSIZE;
        goto cleanup;
    }

    if (initialContextToken)
        flags |= G_VFY_TOKEN_HDR_WRAPPER_REQUIRED;

    ptr = token->value;

    code = g_verify_token_header(gss_mech_iakerb,
                                 &bodysize, &ptr,
                                 IAKERB_TOK_PROXY,
                                 token->length, flags);
    if (code != 0)
        goto cleanup;

    data.data = (char *)ptr;

    if (bodysize-- == 0 || *ptr++ != 0x30 /* SEQUENCE */) {
        code = ASN1_BAD_ID;
        goto cleanup;
    }

    length = gssint_get_der_length(&ptr, bodysize, &lenlen);
    if (length < 0 || bodysize - lenlen < (unsigned int)length) {
        code = KRB5_BAD_MSIZE;
        goto cleanup;
    }
    data.length = 1 /* SEQUENCE */ + lenlen + length;

    ptr += length;
    bodysize -= (lenlen + length);

    code = decode_krb5_iakerb_header(&data, &iah);
    if (code != 0)
        goto cleanup;

    if (realm != NULL) {
        *realm = iah->target_realm;
        iah->target_realm.data = NULL;
    }

    if (cookie != NULL) {
        *cookie = iah->cookie;
        iah->cookie = NULL;
    }

    request->data = (char *)ptr;
    request->length = bodysize;

    assert(request->data + request->length ==
           (char *)token->value + token->length);

cleanup:
    krb5_free_iakerb_header(ctx->k5c, iah);

    return code;
}

Project: krb5
File: iakerb.c

iakerb_release_context(iakerb_ctx_id_t ctx)
{
    OM_uint32 tmp;

    if (ctx == NULL)
        return;

    krb5_gss_release_cred(&tmp, &ctx->defcred);
    krb5_init_creds_free(ctx->k5c, ctx->icc);
    krb5_tkt_creds_free(ctx->k5c, ctx->tcc);
    krb5_gss_delete_sec_context(&tmp, &ctx->gssc, NULL);
    krb5_free_data_contents(ctx->k5c, &ctx->conv);
    krb5_get_init_creds_opt_free(ctx->k5c, ctx->gic_opts);
    krb5_free_context(ctx->k5c);
    free(ctx);
}

Project: krb5
File: iakerb.c

iakerb_save_token(iakerb_ctx_id_t ctx, const gss_buffer_t token)
{
    char *p;

    p = realloc(ctx->conv.data, ctx->conv.length + token->length);
    if (p == NULL)
        return ENOMEM;

    memcpy(p + ctx->conv.length, token->value, token->length);
    ctx->conv.data = p;
    ctx->conv.length += token->length;

    return 0;
}

Project: krb5
File: iakerb.c

iakerb_tkt_creds_ctx(iakerb_ctx_id_t ctx,
                     krb5_gss_cred_id_t cred,
                     krb5_gss_name_t name,
                     OM_uint32 time_req)

{
    krb5_error_code code;
    krb5_creds creds;
    krb5_timestamp now;

    assert(cred->name != NULL);
    assert(cred->name->princ != NULL);

    memset(&creds, 0, sizeof(creds));

    creds.client = cred->name->princ;
    creds.server = name->princ;

    if (time_req != 0 && time_req != GSS_C_INDEFINITE) {
        code = krb5_timeofday(ctx->k5c, &now);
        if (code != 0)
            goto cleanup;

        creds.times.endtime = now + time_req;
    }

    if (cred->name->ad_context != NULL) {
        code = krb5_authdata_export_authdata(ctx->k5c,
                                             cred->name->ad_context,
                                             AD_USAGE_TGS_REQ,
                                             &creds.authdata);
        if (code != 0)
            goto cleanup;
    }

    code = krb5_tkt_creds_init(ctx->k5c, cred->ccache, &creds, 0, &ctx->tcc);
    if (code != 0)
        goto cleanup;

cleanup:
    krb5_free_authdata(ctx->k5c, creds.authdata);

    return code;
}

Project: krb5
File: iakerb.c

iakerb_verify_finished(krb5_context context,
                       krb5_key key,
                       const krb5_data *conv,
                       const krb5_data *finished)
{
    krb5_error_code code;
    krb5_iakerb_finished *iaf;
    krb5_boolean valid = FALSE;

    if (key == NULL)
        return KRB5KDC_ERR_NULL_KEY;

    code = decode_krb5_iakerb_finished(finished, &iaf);
    if (code != 0)
        return code;

    code = krb5_k_verify_checksum(context, key, KRB5_KEYUSAGE_IAKERB_FINISHED,
                                  conv, &iaf->checksum, &valid);
    if (code == 0 && valid == FALSE)
        code = KRB5KRB_AP_ERR_BAD_INTEGRITY;

    krb5_free_iakerb_finished(context, iaf);

    return code;
}