gss_buffer_t outputToken GSSEAP_UNUSED,
OM_uint32 *smFlags GSSEAP_UNUSED)
{
- OM_uint32 major;
- gss_iov_buffer_desc iov[2];
+ krb5_error_code code;
+ krb5_context krbContext;
+ krb5_data data;
+ krb5_checksum cksum;
+ krb5_boolean valid = FALSE;
- iov[0].type = GSS_IOV_BUFFER_TYPE_DATA | GSS_IOV_BUFFER_FLAG_ALLOCATE;
- iov[0].buffer.length = 0;
- iov[0].buffer.value = NULL;
+ if (chanBindings == GSS_C_NO_CHANNEL_BINDINGS ||
+ chanBindings->application_data.length == 0)
+ return GSS_S_CONTINUE_NEEDED;
- iov[1].type = GSS_IOV_BUFFER_TYPE_STREAM | GSS_IOV_BUFFER_FLAG_ALLOCATED;
+ GSSEAP_KRB_INIT(&krbContext);
- /* XXX necessary because decrypted in place and we verify it later */
- major = duplicateBuffer(minor, inputToken, &iov[1].buffer);
- if (GSS_ERROR(major))
- return major;
+ KRB_DATA_INIT(&data);
- major = gssEapUnwrapOrVerifyMIC(minor, ctx, NULL, NULL,
- iov, 2, TOK_TYPE_WRAP);
- if (GSS_ERROR(major)) {
- gssEapReleaseIov(iov, 2);
- return major;
+ gssBufferToKrbData(&chanBindings->application_data, &data);
+
+ KRB_CHECKSUM_INIT(&cksum, ctx->checksumType, inputToken);
+
+ code = krb5_c_verify_checksum(krbContext, &ctx->rfc3961Key,
+ KEY_USAGE_GSSEAP_CHBIND_MIC,
+ &data, &cksum, &valid);
+ if (code != 0) {
+ *minor = code;
+ return GSS_S_FAILURE;
}
- if (chanBindings != GSS_C_NO_CHANNEL_BINDINGS &&
- !bufferEqual(&iov[0].buffer, &chanBindings->application_data)) {
- major = GSS_S_BAD_BINDINGS;
+ if (valid == FALSE) {
*minor = GSSEAP_BINDINGS_MISMATCH;
- } else {
- major = GSS_S_CONTINUE_NEEDED;
- *minor = 0;
+ return GSS_S_BAD_BINDINGS;
}
- gssEapReleaseIov(iov, 2);
+ ctx->flags |= CTX_FLAG_CHANNEL_BINDINGS_VERIFIED;
- return major;
+ *minor = 0;
+ return GSS_S_CONTINUE_NEEDED;
}
static OM_uint32
gss_OID mech GSSEAP_UNUSED,
OM_uint32 reqFlags GSSEAP_UNUSED,
OM_uint32 timeReq GSSEAP_UNUSED,
- gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
+ gss_channel_bindings_t chanBindings,
gss_buffer_t inputToken,
gss_buffer_t outputToken GSSEAP_UNUSED,
OM_uint32 *smFlags GSSEAP_UNUSED)
{
OM_uint32 major;
+ /*
+ * The channel binding token is optional, however if the caller indicated
+ * bindings we must raise an error if it was absent.
+ *
+ * In the future, we might use a context option to allow the caller to
+ * indicate that missing bindings are acceptable.
+ */
+ if (chanBindings != NULL &&
+ chanBindings->application_data.length != 0 &&
+ (ctx->flags & CTX_FLAG_CHANNEL_BINDINGS_VERIFIED) == 0) {
+ *minor = GSSEAP_MISSING_BINDINGS;
+ return GSS_S_BAD_BINDINGS;
+ }
+
major = gssEapVerifyTokenMIC(minor, ctx, inputToken);
if (GSS_ERROR(major))
return major;
ITOK_TYPE_GSS_CHANNEL_BINDINGS,
ITOK_TYPE_NONE,
GSSEAP_STATE_INITIATOR_EXTS,
- SM_ITOK_FLAG_REQUIRED,
+ 0,
eapGssSmAcceptGssChannelBindings,
},
{
#define CTX_FLAG_INITIATOR 0x00000001
#define CTX_FLAG_KRB_REAUTH 0x00000002
+#define CTX_FLAG_CHANNEL_BINDINGS_VERIFIED 0x00000004
#define CTX_IS_INITIATOR(ctx) (((ctx)->flags & CTX_FLAG_INITIATOR) != 0)
#define KEY_USAGE_INITIATOR_SEAL 24
#define KEY_USAGE_INITIATOR_SIGN 25
+#define KEY_USAGE_GSSEAP_CHBIND_MIC 60
+#define KEY_USAGE_GSSEAP_ACCTOKEN_MIC 61
+#define KEY_USAGE_GSSEAP_INITOKEN_MIC 62
+
/* accept_sec_context.c */
OM_uint32
gssEapAcceptSecContext(OM_uint32 *minor,
# Extensions
#
error_code GSSEAP_BINDINGS_MISMATCH, "Channel bindings do not match"
+error_code GSSEAP_MISSING_BINDINGS, "Channel binding token missing"
error_code GSSEAP_NO_MECHGLUE_SYMBOL, "Could not find symbol in mechanism glue"
error_code GSSEAP_BAD_INVOCATION, "Bad mechanism invoke OID"
OM_uint32 *smFlags)
{
OM_uint32 major;
- gss_buffer_desc buffer = GSS_C_EMPTY_BUFFER;
+ krb5_error_code code;
+ krb5_context krbContext;
+ krb5_data data;
+ krb5_checksum cksum;
+ gss_buffer_desc cksumBuffer;
- if (chanBindings != GSS_C_NO_CHANNEL_BINDINGS)
- buffer = chanBindings->application_data;
+ if (chanBindings == GSS_C_NO_CHANNEL_BINDINGS ||
+ chanBindings->application_data.length == 0)
+ return GSS_S_CONTINUE_NEEDED;
- major = gssEapWrap(minor, ctx, TRUE, GSS_C_QOP_DEFAULT,
- &buffer, NULL, outputToken);
- if (GSS_ERROR(major))
- return major;
+ GSSEAP_KRB_INIT(&krbContext);
+
+ KRB_DATA_INIT(&data);
+
+ gssBufferToKrbData(&chanBindings->application_data, &data);
+
+ code = krb5_c_make_checksum(krbContext, ctx->checksumType,
+ &ctx->rfc3961Key,
+ KEY_USAGE_GSSEAP_CHBIND_MIC,
+ &data, &cksum);
+ if (code != 0) {
+ *minor = code;
+ return GSS_S_FAILURE;
+ }
+
+ cksumBuffer.length = KRB_CHECKSUM_LENGTH(&cksum);
+ cksumBuffer.value = KRB_CHECKSUM_DATA(&cksum);
- GSSEAP_ASSERT(outputToken->value != NULL);
+ major = duplicateBuffer(minor, &cksumBuffer, outputToken);
+ if (GSS_ERROR(major)) {
+ krb5_free_checksum_contents(krbContext, &cksum);
+ return major;
+ }
*minor = 0;
*smFlags |= SM_FLAG_OUTPUT_TOKEN_CRITICAL;
+ krb5_free_checksum_contents(krbContext, &cksum);
+
return GSS_S_CONTINUE_NEEDED;
}
return GSS_S_CONTINUE_NEEDED;
}
-
+
#ifdef GSSEAP_ENABLE_REAUTH
static OM_uint32
eapGssSmInitReauthCreds(OM_uint32 *minor,
ITOK_TYPE_NONE,
ITOK_TYPE_GSS_CHANNEL_BINDINGS,
GSSEAP_STATE_INITIATOR_EXTS,
- SM_ITOK_FLAG_REQUIRED,
+ 0,
eapGssSmInitGssChannelBindings
},
{
#define KRB_DATA_INIT(d) krb5_data_zero((d))
+#define KRB_CHECKSUM_TYPE(c) ((c)->cksumtype)
+#define KRB_CHECKSUM_LENGTH(c) ((c)->checksum.length)
+#define KRB_CHECKSUM_DATA(c) ((c)->checksum.data)
+
+#define KRB_CHECKSUM_INIT(cksum, type, d) do { \
+ (cksum)->cksumtype = (type); \
+ (cksum)->checksum.length = (d)->length; \
+ (cksum)->checksum.data = (d)->value; \
+ } while (0)
+
#else
#define KRB_TIME_FOREVER KRB5_INT32_MAX
(d)->data = NULL; \
} while (0)
+#define KRB_CHECKSUM_TYPE(c) ((c)->checksum_type)
+#define KRB_CHECKSUM_LENGTH(c) ((c)->length)
+#define KRB_CHECKSUM_DATA(c) ((c)->contents)
+
+#define KRB_CHECKSUM_INIT(cksum, type, d) do { \
+ (cksum)->checksum_type = (type); \
+ (cksum)->length = (d)->length; \
+ (cksum)->contents = (d)->value; \
+ } while (0)
+
#endif /* HAVE_HEIMDAL_VERSION */
#define KRB_KEY_INIT(key) do { \
int verifyMIC)
{
OM_uint32 major;
- gss_iov_buffer_desc *iov = NULL;
size_t i = 0, j;
enum gss_eap_token_type tokType;
OM_uint32 micTokType;
unsigned char wireTokType[2];
unsigned char *innerTokTypes = NULL, *innerTokLengths = NULL;
const struct gss_eap_token_buffer_set *tokens;
+ ssize_t checksumIndex = -1;
+
+ krb5_keyusage usage;
+ krb5_error_code code;
+ krb5_context krbContext;
+ krb5_crypto_iov *kiov = NULL;
+#ifdef HAVE_HEIMDAL_VERSION
+ krb5_crypto krbCrypto = NULL;
+ krb5_cksumtype cksumType;
+#endif
+ size_t kiovCount;
+
+ GSSEAP_KRB_INIT(&krbContext);
tokens = verifyMIC ? ctx->inputTokens : ctx->outputTokens;
GSSEAP_ASSERT(tokens != NULL);
- iov = GSSEAP_CALLOC(2 + (3 * tokens->buffers.count) + 1, sizeof(*iov));
- if (iov == NULL) {
+#ifdef HAVE_HEIMDAL_VERSION
+ code = krb5_crypto_init(krbContext, &ctx->rfc3961Key, ETYPE_NULL, &krbCrypto);
+ if (code != 0)
+ goto cleanup;
+#endif
+
+ kiovCount = 2 + (3 * tokens->buffers.count) + 1;
+
+ if (verifyMIC) {
+ assert(tokens->buffers.count != 0);
+ kiovCount -= 3;
+ }
+
+ kiov = GSSEAP_CALLOC(kiovCount, sizeof(*kiov));
+ if (kiov == NULL) {
major = GSS_S_FAILURE;
*minor = ENOMEM;
goto cleanup;
/* Mechanism OID */
GSSEAP_ASSERT(ctx->mechanismUsed != GSS_C_NO_OID);
- iov[i].type = GSS_IOV_BUFFER_TYPE_DATA;
- iov[i].buffer.length = ctx->mechanismUsed->length;
- iov[i].buffer.value = ctx->mechanismUsed->elements;
+ kiov[i].flags = KRB5_CRYPTO_TYPE_SIGN_ONLY;
+ kiov[i].data.length = ctx->mechanismUsed->length;
+ kiov[i].data.data = ctx->mechanismUsed->elements;
i++;
/* Token type */
if (CTX_IS_INITIATOR(ctx) ^ verifyMIC) {
tokType = TOK_TYPE_INITIATOR_CONTEXT;
micTokType = ITOK_TYPE_INITIATOR_MIC;
+ usage = KEY_USAGE_GSSEAP_INITOKEN_MIC;
} else {
tokType = TOK_TYPE_ACCEPTOR_CONTEXT;
micTokType = ITOK_TYPE_ACCEPTOR_MIC;
+ usage = KEY_USAGE_GSSEAP_ACCTOKEN_MIC;
}
store_uint16_be(tokType, wireTokType);
- iov[i].type = GSS_IOV_BUFFER_TYPE_DATA;
- iov[i].buffer.length = sizeof(wireTokType);
- iov[i].buffer.value = wireTokType;
+ kiov[i].flags = KRB5_CRYPTO_TYPE_SIGN_ONLY;
+ kiov[i].data.length = sizeof(wireTokType);
+ kiov[i].data.data = (char *)wireTokType;
i++;
for (j = 0; j < tokens->buffers.count; j++) {
if (verifyMIC &&
- (tokens->types[j] & ITOK_TYPE_MASK) == micTokType)
- continue; /* will use this slot for trailer */
+ (tokens->types[j] & ITOK_TYPE_MASK) == micTokType) {
+ continue;
+ }
- iov[i].type = GSS_IOV_BUFFER_TYPE_DATA;
- iov[i].buffer.length = 4;
- iov[i].buffer.value = &innerTokTypes[j * 4];
+ kiov[i].flags = KRB5_CRYPTO_TYPE_SIGN_ONLY;
+ kiov[i].data.length = 4;
+ kiov[i].data.data = (char *)&innerTokTypes[j * 4];
store_uint32_be(tokens->types[j] & ~(ITOK_FLAG_VERIFIED),
- iov[i].buffer.value);
+ kiov[i].data.data);
i++;
- iov[i].type = GSS_IOV_BUFFER_TYPE_DATA;
- iov[i].buffer.length = 4;
- iov[i].buffer.value = &innerTokLengths[j * 4];
+ kiov[i].flags = KRB5_CRYPTO_TYPE_SIGN_ONLY;
+ kiov[i].data.length = 4;
+ kiov[i].data.data = (char *)&innerTokLengths[j * 4];
store_uint32_be(tokens->buffers.elements[j].length,
- iov[i].buffer.value);
+ kiov[i].data.data);
i++;
- iov[i].type = GSS_IOV_BUFFER_TYPE_DATA;
- iov[i].buffer = tokens->buffers.elements[j];
+ kiov[i].flags = KRB5_CRYPTO_TYPE_SIGN_ONLY;
+ gssBufferToKrbData(&tokens->buffers.elements[j], &kiov[i].data);
i++;
}
+ kiov[i].flags = KRB5_CRYPTO_TYPE_CHECKSUM;
if (verifyMIC) {
- GSSEAP_ASSERT(tokenMIC->length >= 16);
+ gssBufferToKrbData(tokenMIC, &kiov[i].data);
+ } else {
+ size_t checksumSize;
- GSSEAP_ASSERT(i < 2 + (3 * tokens->buffers.count));
+ code = krb5_c_checksum_length(krbContext, ctx->checksumType,
+ &checksumSize);
+ if (code != 0)
+ goto cleanup;
- iov[i].type = GSS_IOV_BUFFER_TYPE_HEADER;
- iov[i].buffer = *tokenMIC;
- i++;
+ kiov[i].data.data = GSSEAP_MALLOC(checksumSize);
+ if (kiov[i].data.data == NULL) {
+ code = ENOMEM;
+ goto cleanup;
+ }
+ kiov[i].data.length = checksumSize;
+ checksumIndex = i;
+ }
+ i++;
+ GSSEAP_ASSERT(i == kiovCount);
- major = gssEapUnwrapOrVerifyMIC(minor, ctx, NULL, NULL,
- iov, i, TOK_TYPE_MIC);
+#ifdef HAVE_HEIMDAL_VERSION
+ cksumType = ctx->checksumType;
+
+ if (verifyMIC) {
+ code = krb5_verify_checksum_iov(krbContext, krbCrypto, usage,
+ kiov, i, &cksumType);
+ } else {
+ code = krb5_create_checksum_iov(krbContext, krbCrypto, usage,
+ kiov, i, &cksumType);
+ }
+#else
+ if (verifyMIC) {
+ krb5_boolean kvalid = FALSE;
+
+ code = krb5_c_verify_checksum_iov(krbContext, ctx->checksumType,
+ &ctx->rfc3961Key,
+ usage, kiov, i, &kvalid);
+ if (code == 0 && !kvalid) {
+ code = KRB5KRB_AP_ERR_BAD_INTEGRITY;
+ }
} else {
- iov[i++].type = GSS_IOV_BUFFER_TYPE_HEADER | GSS_IOV_BUFFER_FLAG_ALLOCATE;
- major = gssEapWrapOrGetMIC(minor, ctx, FALSE, NULL,
- iov, i, TOK_TYPE_MIC);
- if (!GSS_ERROR(major))
- *tokenMIC = iov[i - 1].buffer;
+ code = krb5_c_make_checksum_iov(krbContext, ctx->checksumType,
+ &ctx->rfc3961Key,
+ usage, kiov, i);
+ }
+#endif /* HAVE_HEIMDAL_VERSION */
+
+ if (code == 0 && !verifyMIC) {
+ krbDataToGssBuffer(&kiov[checksumIndex].data, tokenMIC);
+ checksumIndex = -1;
}
cleanup:
- if (iov != NULL)
- gssEapReleaseIov(iov, tokens->buffers.count);
+ if (checksumIndex != -1)
+ GSSEAP_FREE(kiov[checksumIndex].data.data);
+ if (kiov != NULL)
+ GSSEAP_FREE(kiov);
if (innerTokTypes != NULL)
GSSEAP_FREE(innerTokTypes);
if (innerTokLengths != NULL)
GSSEAP_FREE(innerTokLengths);
+#ifdef HAVE_HEIMDAL_VERSION
+ if (krbCrypto != NULL)
+ krb5_crypto_destroy(krbContext, krbCrypto);
+#endif
+
+ *minor = code;
+
+ switch (code) {
+ case KRB5KRB_AP_ERR_BAD_INTEGRITY:
+ major = GSS_S_BAD_SIG;
+ break;
+ case 0:
+ major = GSS_S_COMPLETE;
+ break;
+ default:
+ major = GSS_S_FAILURE;
+ break;
+ }
return major;
}
gss_ctx_id_t ctx,
const gss_buffer_t tokenMIC)
{
- if (tokenMIC->length < 16) {
- *minor = GSSEAP_TOK_TRUNC;
- return GSS_S_BAD_SIG;
- }
-
return gssEapMakeOrVerifyTokenMIC(minor, ctx, tokenMIC, TRUE);
}
if (*minor != 0)
return GSS_S_FAILURE;
-#ifdef HAVE_HEIMDAL_VERSION
- *cksumtype = cksum.cksumtype;
-#else
- *cksumtype = cksum.checksum_type;
-#endif
+ *cksumtype = KRB_CHECKSUM_TYPE(&cksum);
krb5_free_checksum_contents(krbContext, &cksum);
#endif /* HAVE_KRB5INT_C_MANDATORY_CKSUMTYPE */