static OM_uint32
inquireNegoExKey(OM_uint32 *minor,
                  const gss_ctx_id_t ctx,
                  const gss_OID desired_object,
                  gss_buffer_set_t *dataSet)
{
    OM_uint32 major, tmpMinor;
    int bInitiatorKey;
    gss_buffer_desc salt;
    gss_buffer_desc key = GSS_C_EMPTY_BUFFER;
    size_t keySize;

    bInitiatorKey = CTX_IS_INITIATOR(ctx);

    major = GSS_S_UNAVAILABLE;
    *minor = GSSEAP_KEY_UNAVAILABLE;

    if (key.value != NULL) {
        memset(key.value, 0, key.length);
        gss_release_buffer(&tmpMinor, &key);
    }
    if (GSS_ERROR(major))
        zeroAndReleaseBufferSet(dataSet);

    return major;
}
static void
peerSetInt(void *data, enum eapol_int_var variable,
           unsigned int value)
{
    gss_ctx_id_t ctx = data;

    if (ctx == GSS_C_NO_CONTEXT)
        return;

    GSSEAP_ASSERT(CTX_IS_INITIATOR(ctx));

    switch (variable) {
    case EAPOL_idleWhile:
        ctx->initiatorCtx.idleWhile = value;
        break;
    }
}
static unsigned int
peerGetInt(void *data, enum eapol_int_var variable)
{
    gss_ctx_id_t ctx = data;

    if (ctx == GSS_C_NO_CONTEXT)
        return FALSE;

    GSSEAP_ASSERT(CTX_IS_INITIATOR(ctx));

    switch (variable) {
    case EAPOL_idleWhile:
        return ctx->initiatorCtx.idleWhile;
        break;
    }

    return 0;
}
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;
}
Example #5
0
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;
}
Example #6
0
OM_uint32 GSSAPI_CALLCONV
gss_inquire_context(OM_uint32 *minor,
#ifdef HAVE_HEIMDAL_VERSION
                    gss_const_ctx_id_t ctx,
#else
                    gss_ctx_id_t ctx,
#endif
                    gss_name_t *src_name,
                    gss_name_t *targ_name,
                    OM_uint32 *lifetime_rec,
                    gss_OID *mech_type,
                    OM_uint32 *ctx_flags,
                    int *locally_initiated,
                    int *open)
{
    OM_uint32 major, tmpMinor;

    if (ctx == GSS_C_NO_CONTEXT) {
        *minor = EINVAL;
        return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CONTEXT;
    }

    GSSEAP_MUTEX_LOCK(&((gss_ctx_id_t)ctx)->mutex);

    if (src_name != NULL) {
        if (ctx->initiatorName != GSS_C_NO_NAME) {
            major = gssEapDuplicateName(minor, ctx->initiatorName, src_name);
            if (GSS_ERROR(major))
                goto cleanup;
        } else
            *src_name = GSS_C_NO_NAME;
    }

    if (targ_name != NULL) {
        if (ctx->acceptorName != GSS_C_NO_NAME) {
            major = gssEapDuplicateName(minor, ctx->acceptorName, targ_name);
            if (GSS_ERROR(major))
                goto cleanup;
        } else
            *targ_name = GSS_C_NO_NAME;
    }

    if (lifetime_rec != NULL)
        gssEapContextTime(&tmpMinor, ctx, lifetime_rec);

    if (mech_type != NULL) {
        major = gssEapCanonicalizeOid(minor, ctx->mechanismUsed, 0, mech_type);
        if (GSS_ERROR(major))
            goto cleanup;
    }

    if (ctx_flags != NULL) {
        *ctx_flags = ctx->gssFlags;
    }

    if (locally_initiated != NULL) {
        *locally_initiated = CTX_IS_INITIATOR(ctx);
    }

    if (open != NULL) {
        *open = CTX_IS_ESTABLISHED(ctx);
    }

    major = GSS_S_COMPLETE;
    *minor = 0;

cleanup:
    GSSEAP_MUTEX_UNLOCK(&((gss_ctx_id_t)ctx)->mutex);

    if (GSS_ERROR(major)) {
        gssEapReleaseName(&tmpMinor, src_name);
        gssEapReleaseName(&tmpMinor, targ_name);
    }

    return major;
}
Example #7
0
static OM_uint32
makeErrorToken(OM_uint32 *minor,
               OM_uint32 majorStatus,
               OM_uint32 minorStatus,
               struct gss_eap_token_buffer_set *token)
{
    OM_uint32 major, tmpMinor;
    unsigned char errorData[8];
    gss_buffer_desc errorBuffer;

    GSSEAP_ASSERT(GSS_ERROR(majorStatus));

    /*
     * Only return error codes that the initiator could have caused,
     * to avoid information leakage.
     */
#if MECH_EAP
    if (IS_RADIUS_ERROR(minorStatus)) {
        /* Squash RADIUS error codes */
        minorStatus = GSSEAP_RADIUS_PROT_FAILURE;
    } else if (!IS_WIRE_ERROR(minorStatus)) {
#else
    if (!IS_WIRE_ERROR(minorStatus)) {
#endif
        /* Don't return non-wire error codes */
        return GSS_S_COMPLETE;
    }

    minorStatus -= ERROR_TABLE_BASE_eapg;

    store_uint32_be(majorStatus, &errorData[0]);
    store_uint32_be(minorStatus, &errorData[4]);

    major = gssEapAllocInnerTokens(&tmpMinor, 1, token);
    if (GSS_ERROR(major)) {
        *minor = tmpMinor;
        return major;
    }

    errorBuffer.length = sizeof(errorData);
    errorBuffer.value = errorData;

    major = duplicateBuffer(&tmpMinor, &errorBuffer, &token->buffers.elements[0]);
    if (GSS_ERROR(major)) {
        gssEapReleaseInnerTokens(&tmpMinor, token, 1);
        *minor = tmpMinor;
        return major;
    }

    token->buffers.count = 1;
    token->types[0] = ITOK_TYPE_CONTEXT_ERR | ITOK_FLAG_CRITICAL;

    *minor = 0;
    return GSS_S_COMPLETE;
}

OM_uint32
gssEapSmStep(OM_uint32 *minor,
             gss_cred_id_t cred,
             gss_ctx_id_t ctx,
             gss_name_t target,
             gss_OID mech,
             OM_uint32 reqFlags,
             OM_uint32 timeReq,
             gss_channel_bindings_t chanBindings,
             gss_buffer_t inputToken,
             gss_buffer_t outputToken,
             struct gss_eap_sm *sm, /* ordered by state */
             size_t smCount)
{
    OM_uint32 major, tmpMajor, tmpMinor;
    struct gss_eap_token_buffer_set inputTokens = { { 0, GSS_C_NO_BUFFER }, NULL };
    struct gss_eap_token_buffer_set outputTokens = { { 0, GSS_C_NO_BUFFER }, NULL };
    gss_buffer_desc unwrappedInputToken = GSS_C_EMPTY_BUFFER;
    gss_buffer_desc unwrappedOutputToken = GSS_C_EMPTY_BUFFER;
    unsigned int smFlags = 0;
    size_t i, j;
    int initialContextToken = 0;
    enum gss_eap_token_type tokType;

    GSSEAP_ASSERT(smCount > 0);

    *minor = 0;

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

    if (inputToken != GSS_C_NO_BUFFER && inputToken->length != 0) {
        major = gssEapVerifyToken(minor, ctx, inputToken, &tokType,
                                  &unwrappedInputToken);
        if (GSS_ERROR(major))
            goto cleanup;

        if (tokType != (CTX_IS_INITIATOR(ctx)
                    ? TOK_TYPE_ACCEPTOR_CONTEXT : TOK_TYPE_INITIATOR_CONTEXT)) {
            major = GSS_S_DEFECTIVE_TOKEN;
            *minor = GSSEAP_WRONG_TOK_ID;
            goto cleanup;
        }
    } else if (!CTX_IS_INITIATOR(ctx) || ctx->state != GSSEAP_STATE_INITIAL) {
        major = GSS_S_DEFECTIVE_TOKEN;
        *minor = GSSEAP_WRONG_SIZE;
        goto cleanup;
    } else {
        initialContextToken = 1;
    }

    if (CTX_IS_ESTABLISHED(ctx)) {
        major = GSS_S_BAD_STATUS;
        *minor = GSSEAP_CONTEXT_ESTABLISHED;
        goto cleanup;
    }

    GSSEAP_ASSERT(ctx->state < GSSEAP_STATE_ESTABLISHED);

    major = gssEapDecodeInnerTokens(minor, &unwrappedInputToken, &inputTokens);
    if (GSS_ERROR(major))
        goto cleanup;

    major = gssEapAllocInnerTokens(minor, smCount, &outputTokens);
    if (GSS_ERROR(major))
        goto cleanup;

    ctx->inputTokens = &inputTokens;
    ctx->outputTokens = &outputTokens;

    /* Process all the tokens that are valid for the current state. */
    for (i = 0; i < smCount; i++) {
        struct gss_eap_sm *smp = &sm[i];
        int processToken = 0;
        gss_buffer_t innerInputToken = GSS_C_NO_BUFFER;
        OM_uint32 *inputTokenType = NULL;
        gss_buffer_desc innerOutputToken = GSS_C_EMPTY_BUFFER;

        if ((smp->validStates & ctx->state) == 0)
            continue;

        /*
         * We special case the first call to gss_init_sec_context so that
         * all token providers have the opportunity to generate an initial
         * context token. Providers where inputTokenType is ITOK_TYPE_NONE
         * are always called and generally act on state transition boundaries,
         * for example to advance the state after a series of optional tokens
         * (as is the case with the extension token exchange) or to generate
         * a new token after the state was advanced by a provider which did
         * not emit a token.
         */
        if (smp->inputTokenType == ITOK_TYPE_NONE || initialContextToken) {
            processToken = 1;
        } else if ((smFlags & SM_FLAG_TRANSITED) == 0) {
            /* Don't regurgitate a token which belonds to a previous state. */
            for (j = 0; j < inputTokens.buffers.count; j++) {
                if ((inputTokens.types[j] & ITOK_TYPE_MASK) == smp->inputTokenType) {
                    if (processToken) {
                        /* Check for duplicate inner tokens */
                        major = GSS_S_DEFECTIVE_TOKEN;
                        *minor = GSSEAP_DUPLICATE_ITOK;
                        break;
                    }
                    processToken = 1;
                    innerInputToken = &inputTokens.buffers.elements[j];
                    inputTokenType = &inputTokens.types[j];
                }
            }
            if (GSS_ERROR(major))
                break;
        }

        if (processToken) {
            enum gss_eap_state oldState = ctx->state;

            smFlags = 0;
            if (inputTokenType != NULL && (*inputTokenType & ITOK_FLAG_CRITICAL))
                smFlags |= SM_FLAG_INPUT_TOKEN_CRITICAL;

            major = smp->processToken(minor, cred, ctx, target, mech, reqFlags,
                                      timeReq, chanBindings, innerInputToken,
                                      &innerOutputToken, &smFlags);
            if (GSS_ERROR(major))
                break;

            if (inputTokenType != NULL)
                *inputTokenType |= ITOK_FLAG_VERIFIED;
            if (ctx->state < oldState)
                i = 0; /* restart */
            else if (ctx->state != oldState)
                smFlags |= SM_FLAG_TRANSITED;

            if (innerOutputToken.value != NULL) {
                outputTokens.buffers.elements[outputTokens.buffers.count] = innerOutputToken;
                GSSEAP_ASSERT(smp->outputTokenType != ITOK_TYPE_NONE);
                outputTokens.types[outputTokens.buffers.count] = smp->outputTokenType;
                if (smFlags & SM_FLAG_OUTPUT_TOKEN_CRITICAL)
                    outputTokens.types[outputTokens.buffers.count] |= ITOK_FLAG_CRITICAL;
                outputTokens.buffers.count++;
            }
            /*
             * Break out if we made a state transition and have some tokens to send.
             */
            if ((smFlags & SM_FLAG_TRANSITED) &&
                 ((smFlags & SM_FLAG_FORCE_SEND_TOKEN) || outputTokens.buffers.count != 0)) {
                SM_ASSERT_VALID(ctx, major);
                break;
            }
        } else if ((smp->itokFlags & SM_ITOK_FLAG_REQUIRED) &&
            smp->inputTokenType != ITOK_TYPE_NONE) {
            /* Check for required inner tokens */
            major = GSS_S_DEFECTIVE_TOKEN;
            *minor = GSSEAP_MISSING_REQUIRED_ITOK;
            break;
        }
    }

    GSSEAP_ASSERT(outputTokens.buffers.count <= smCount);

    /* Check we understood all critical tokens sent by peer */
    if (!GSS_ERROR(major)) {
        for (j = 0; j < inputTokens.buffers.count; j++) {
            if ((inputTokens.types[j] & ITOK_FLAG_CRITICAL) &&
                (inputTokens.types[j] & ITOK_FLAG_VERIFIED) == 0) {
                major = GSS_S_UNAVAILABLE;
                *minor = GSSEAP_CRIT_ITOK_UNAVAILABLE;
                goto cleanup;
            }
        }
    }

    /* Optionaly emit an error token if we are the acceptor */
    if (GSS_ERROR(major)) {
        if (CTX_IS_INITIATOR(ctx))
            goto cleanup; /* return error directly to caller */

        /* replace any emitted tokens with error token */
        gssEapReleaseInnerTokens(&tmpMinor, &outputTokens, 1);

        tmpMajor = makeErrorToken(&tmpMinor, major, *minor, &outputTokens);
        if (GSS_ERROR(tmpMajor)) {
            major = tmpMajor;
            *minor = tmpMinor;
            goto cleanup;
        }
    }

    /* Format output token from inner tokens */
    if (outputTokens.buffers.count != 0 ||            /* inner tokens to send */
        !CTX_IS_INITIATOR(ctx) ||                   /* any leg acceptor */
        !CTX_IS_ESTABLISHED(ctx)) {                 /* non-last leg initiator */
        tmpMajor = gssEapEncodeInnerTokens(&tmpMinor, &outputTokens, &unwrappedOutputToken);
        if (tmpMajor == GSS_S_COMPLETE) {
            if (CTX_IS_INITIATOR(ctx))
                tokType = TOK_TYPE_INITIATOR_CONTEXT;
            else
                tokType = TOK_TYPE_ACCEPTOR_CONTEXT;

            tmpMajor = gssEapMakeToken(&tmpMinor, ctx, &unwrappedOutputToken,
                                       tokType, outputToken);
            if (GSS_ERROR(tmpMajor)) {
                major = tmpMajor;
                *minor = tmpMinor;
                goto cleanup;
            }
        }
    }

    /* If the context is established, empty tokens only to be emitted by initiator */
    GSSEAP_ASSERT(!CTX_IS_ESTABLISHED(ctx) || ((outputToken->length == 0) == CTX_IS_INITIATOR(ctx)));

    SM_ASSERT_VALID(ctx, major);

cleanup:
    gssEapReleaseInnerTokens(&tmpMinor, &inputTokens, 0);
    gssEapReleaseInnerTokens(&tmpMinor, &inputTokens, 1);

    gss_release_buffer(&tmpMinor, &unwrappedOutputToken);

    ctx->inputTokens = NULL;
    ctx->outputTokens = NULL;

    return major;
}
static OM_uint32
inquireNegoExKey(OM_uint32 *minor,
                  const gss_ctx_id_t ctx,
                  const gss_OID desired_object,
                  gss_buffer_set_t *dataSet)
{
    OM_uint32 major, tmpMinor;
    int bInitiatorKey;
    gss_buffer_desc salt;
    gss_buffer_desc key = GSS_C_EMPTY_BUFFER;
    size_t keySize;

    bInitiatorKey = CTX_IS_INITIATOR(ctx);

    if (ctx->encryptionType == ENCTYPE_NULL) {
        major = GSS_S_UNAVAILABLE;
        *minor = GSSEAP_KEY_UNAVAILABLE;
        goto cleanup;
    }

    /*
     * If the caller supplied the verify key OID, then we need the acceptor
     * key if we are the initiator, and vice versa.
     */
    if (desired_object->length == 11 &&
        memcmp(desired_object->elements,
               "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x07", 11) == 0)
        bInitiatorKey ^= 1;

    if (bInitiatorKey) {
        salt.length = NEGOEX_INITIATOR_SALT_LEN;
        salt.value  = NEGOEX_INITIATOR_SALT;
    } else {
        salt.length = NEGOEX_ACCEPTOR_SALT_LEN;
        salt.value  = NEGOEX_ACCEPTOR_SALT;
    }

    keySize = KRB_KEY_LENGTH(&ctx->rfc3961Key);

    major = gssEapPseudoRandom(minor, ctx, GSS_C_PRF_KEY_FULL, &salt,
                               keySize, &key);
    if (GSS_ERROR(major))
        goto cleanup;

    major = gss_add_buffer_set_member(minor, &key, dataSet);
    if (GSS_ERROR(major))
        goto cleanup;

    major = addEnctypeOidToBufferSet(minor, ctx->encryptionType, dataSet);
    if (GSS_ERROR(major))
        goto cleanup;

    major = GSS_S_COMPLETE;
    *minor = 0;

cleanup:
    if (key.value != NULL) {
        memset(key.value, 0, key.length);
        gss_release_buffer(&tmpMinor, &key);
    }
    if (GSS_ERROR(major))
        zeroAndReleaseBufferSet(dataSet);

    return major;
}