OM_uint32
gssEapExportSecContext(OM_uint32 *minor,
                       gss_ctx_id_t ctx,
                       gss_buffer_t token)
{
    OM_uint32 major, tmpMinor;
    size_t length;
    gss_buffer_desc initiatorName = GSS_C_EMPTY_BUFFER;
    gss_buffer_desc acceptorName = GSS_C_EMPTY_BUFFER;
    gss_buffer_desc partialCtx = GSS_C_EMPTY_BUFFER;
    gss_buffer_desc key;
    unsigned char *p;

    if ((CTX_IS_INITIATOR(ctx) && !CTX_IS_ESTABLISHED(ctx)) ||
        ctx->mechanismUsed == GSS_C_NO_OID) {
        *minor = GSSEAP_CONTEXT_INCOMPLETE;
        return GSS_S_NO_CONTEXT;
    }

    key.length = KRB_KEY_LENGTH(&ctx->rfc3961Key);
    key.value  = KRB_KEY_DATA(&ctx->rfc3961Key);

    if (ctx->initiatorName != GSS_C_NO_NAME) {
        major = gssEapExportNameInternal(minor, ctx->initiatorName,
                                         &initiatorName,
                                         EXPORT_NAME_FLAG_COMPOSITE);
        if (GSS_ERROR(major))
            goto cleanup;
    }

    if (ctx->acceptorName != GSS_C_NO_NAME) {
        major = gssEapExportNameInternal(minor, ctx->acceptorName,
                                         &acceptorName,
                                         EXPORT_NAME_FLAG_COMPOSITE);
        if (GSS_ERROR(major))
            goto cleanup;
    }

#ifdef GSSEAP_ENABLE_ACCEPTOR
    /*
     * The partial context is only transmitted for unestablished acceptor
     * contexts.
     */
    if (!CTX_IS_INITIATOR(ctx) && !CTX_IS_ESTABLISHED(ctx) &&
        (ctx->flags & CTX_FLAG_KRB_REAUTH) == 0) {
        major = gssEapExportPartialContext(minor, ctx, &partialCtx);
        if (GSS_ERROR(major))
            goto cleanup;
    }
#endif

    length  = 16;                               /* version, state, flags, */
    length += 4 + ctx->mechanismUsed->length;   /* mechanismUsed */
    length += 12 + key.length;                  /* rfc3961Key.value */
    length += 4 + initiatorName.length;         /* initiatorName.value */
    length += 4 + acceptorName.length;          /* acceptorName.value */
    length += 24 + sequenceSize(ctx->seqState); /* seqState */

    if (partialCtx.value != NULL)
        length += 4 + partialCtx.length;        /* partialCtx.value */

    token->value = GSSEAP_MALLOC(length);
    if (token->value == NULL) {
        major = GSS_S_FAILURE;
        *minor = ENOMEM;
        goto cleanup;
    }
    token->length = length;

    p = (unsigned char *)token->value;

    store_uint32_be(EAP_EXPORT_CONTEXT_V1, &p[0]);        /* version */
    store_uint32_be(GSSEAP_SM_STATE(ctx),  &p[4]);
    store_uint32_be(ctx->flags,            &p[8]);
    store_uint32_be(ctx->gssFlags,         &p[12]);
    p = store_oid(ctx->mechanismUsed,      &p[16]);

    store_uint32_be(ctx->checksumType,     &p[0]);
    store_uint32_be(ctx->encryptionType,   &p[4]);
    p = store_buffer(&key,                 &p[8], FALSE);

    p = store_buffer(&initiatorName,       p, FALSE);
    p = store_buffer(&acceptorName,        p, FALSE);

    store_uint64_be(ctx->expiryTime,       &p[0]);
    store_uint64_be(ctx->sendSeq,          &p[8]);
    store_uint64_be(ctx->recvSeq,          &p[16]);
    p += 24;

    major = sequenceExternalize(minor, ctx->seqState, &p, &length);
    if (GSS_ERROR(major))
        goto cleanup;

    if (partialCtx.value != NULL)
        p = store_buffer(&partialCtx, p, FALSE);

    GSSEAP_ASSERT(p == (unsigned char *)token->value + token->length);

    major = GSS_S_COMPLETE;
    *minor = 0;

cleanup:
    if (GSS_ERROR(major))
        gss_release_buffer(&tmpMinor, token);
    gss_release_buffer(&tmpMinor, &initiatorName);
    gss_release_buffer(&tmpMinor, &acceptorName);
    gss_release_buffer(&tmpMinor, &partialCtx);

    return major;
}
OM_uint32
gssEapImportContext(OM_uint32 *minor,
                    gss_buffer_t token,
                    gss_ctx_id_t ctx)
{
    OM_uint32 major;
    unsigned char *p = (unsigned char *)token->value;
    size_t remain = token->length;

    if (remain < 16) {
        *minor = GSSEAP_TOK_TRUNC;
        return GSS_S_DEFECTIVE_TOKEN;
    }
    if (load_uint32_be(&p[0]) != EAP_EXPORT_CONTEXT_V1) {
        *minor = GSSEAP_BAD_CONTEXT_TOKEN;
        return GSS_S_DEFECTIVE_TOKEN;
    }
    ctx->state      = load_uint32_be(&p[4]);
    ctx->flags      = load_uint32_be(&p[8]);
    ctx->gssFlags   = load_uint32_be(&p[12]);
    p      += 16;
    remain -= 16;

    /* Validate state */
    if (GSSEAP_SM_STATE(ctx) < GSSEAP_STATE_INITIAL ||
        GSSEAP_SM_STATE(ctx) > GSSEAP_STATE_ESTABLISHED)
        return GSS_S_DEFECTIVE_TOKEN;

    /* Only acceptor can export partial context tokens */
    if (CTX_IS_INITIATOR(ctx) && !CTX_IS_ESTABLISHED(ctx))
        return GSS_S_DEFECTIVE_TOKEN;

    major = importMechanismOid(minor, &p, &remain, &ctx->mechanismUsed);
    if (GSS_ERROR(major))
        return major;

    major = importKerberosKey(minor, &p, &remain,
                              &ctx->checksumType,
                              &ctx->encryptionType,
                              &ctx->rfc3961Key);
    if (GSS_ERROR(major))
        return major;

    /* Initiator name OID matches the context mechanism, so it's not encoded */
    major = importName(minor, ctx->mechanismUsed, &p, &remain, &ctx->initiatorName);
    if (GSS_ERROR(major))
        return major;

    major = importName(minor, GSS_C_NO_OID, &p, &remain, &ctx->acceptorName);
    if (GSS_ERROR(major))
        return major;

    /* Check that, if context is established, names are valid */
    if (CTX_IS_ESTABLISHED(ctx) &&
        (CTX_IS_INITIATOR(ctx) ? ctx->acceptorName == GSS_C_NO_NAME
                               : ctx->initiatorName == GSS_C_NO_NAME)) {
        return GSS_S_DEFECTIVE_TOKEN;
    }

    if (remain < 24 + sequenceSize(ctx->seqState)) {
        *minor = GSSEAP_TOK_TRUNC;
        return GSS_S_DEFECTIVE_TOKEN;
    }
    ctx->expiryTime = (time_t)load_uint64_be(&p[0]);
    ctx->sendSeq    = load_uint64_be(&p[8]);
    ctx->recvSeq    = load_uint64_be(&p[16]);
    p      += 24;
    remain -= 24;

    major = sequenceInternalize(minor, &ctx->seqState, &p, &remain);
    if (GSS_ERROR(major))
        return major;

#ifdef GSSEAP_ENABLE_ACCEPTOR
    /*
     * The partial context should only be expected for unestablished
     * acceptor contexts.
     */
    if (!CTX_IS_INITIATOR(ctx) && !CTX_IS_ESTABLISHED(ctx) &&
        (ctx->flags & CTX_FLAG_KRB_REAUTH) == 0) {
        major = gssEapImportPartialContext(minor, &p, &remain, ctx);
        if (GSS_ERROR(major))
            return major;
    }

#ifdef GSSEAP_DEBUG
    GSSEAP_ASSERT(remain == 0);
#endif
#endif /* GSSEAP_ENABLE_ACCEPTOR */

    major = GSS_S_COMPLETE;
    *minor = 0;

    return major;
}