#endif
static OM_uint32
-completeAccept(OM_uint32 *minor, gss_ctx_id_t ctx)
+acceptReady(OM_uint32 *minor, gss_ctx_id_t ctx)
{
OM_uint32 major;
krb5_context krbContext;
if (ctx->acceptorCtx.eapPolInterface->eapSuccess) {
ctx->acceptorCtx.eapPolInterface->eapSuccess = 0;
- ctx->state = EAP_STATE_ESTABLISHED;
- major = completeAccept(minor, ctx);
+ major = acceptReady(minor, ctx);
+ if (GSS_ERROR(major))
+ goto cleanup;
+
+ ctx->state = EAP_STATE_GSS_CHANNEL_BINDINGS;
+ major = GSS_S_CONTINUE_NEEDED;
} else if (ctx->acceptorCtx.eapPolInterface->eapFail) {
ctx->acceptorCtx.eapPolInterface->eapFail = 0;
major = GSS_S_FAILURE;
}
static OM_uint32
+eapGssSmAcceptGssChannelBindings(OM_uint32 *minor,
+ gss_ctx_id_t ctx,
+ gss_cred_id_t cred,
+ gss_buffer_t inputToken,
+ gss_channel_bindings_t chanBindings,
+ gss_buffer_t outputToken)
+{
+ OM_uint32 major, tmpMinor;
+ gss_iov_buffer_desc iov[2];
+
+ outputToken->length = 0;
+ outputToken->value = NULL;
+
+ if (chanBindings == GSS_C_NO_CHANNEL_BINDINGS) {
+ ctx->state = EAP_STATE_ESTABLISHED;
+ return GSS_S_COMPLETE;
+ }
+
+ if (inputToken->length < 14) {
+ return GSS_S_DEFECTIVE_TOKEN;
+ }
+
+ iov[0].type = GSS_IOV_BUFFER_TYPE_DATA;
+ iov[0].buffer.length = 0;
+ iov[0].buffer.value = NULL;
+
+ major = gssEapEncodeGssChannelBindings(minor, chanBindings,
+ &iov[0].buffer);
+ if (GSS_ERROR(major))
+ return major;
+
+ iov[1].type = GSS_IOV_BUFFER_TYPE_HEADER;
+ iov[1].buffer.length = 16;
+ iov[1].buffer.value = (unsigned char *)inputToken->value - 2;
+
+ assert(load_uint16_be(iov[1].buffer.value) == TOK_TYPE_GSS_CB);
+
+ iov[2].type = GSS_IOV_BUFFER_TYPE_TRAILER;
+ iov[2].buffer.length = inputToken->length - 14;
+ iov[2].buffer.value = (unsigned char *)inputToken->value + 14;
+
+ major = gssEapUnwrapOrVerifyMIC(minor, ctx, NULL, NULL,
+ iov, 3, TOK_TYPE_GSS_CB);
+ if (major == GSS_S_COMPLETE) {
+ ctx->state = EAP_STATE_ESTABLISHED;
+ }
+
+ gss_release_buffer(&tmpMinor, &iov[0].buffer);
+
+ return major;
+}
+
+static OM_uint32
eapGssSmAcceptEstablished(OM_uint32 *minor,
gss_ctx_id_t ctx,
gss_cred_id_t cred,
gss_channel_bindings_t,
gss_buffer_t);
} eapGssAcceptorSm[] = {
- { TOK_TYPE_EAP_RESP, TOK_TYPE_EAP_REQ, eapGssSmAcceptAuthenticate },
- { TOK_TYPE_EAP_RESP, TOK_TYPE_EAP_REQ, NULL },
- { TOK_TYPE_EAP_RESP, TOK_TYPE_EAP_REQ, NULL },
- { TOK_TYPE_GSS_CB, TOK_TYPE_NONE, NULL },
- { TOK_TYPE_NONE, TOK_TYPE_NONE, eapGssSmAcceptEstablished },
+ { TOK_TYPE_EAP_RESP, TOK_TYPE_EAP_REQ, eapGssSmAcceptAuthenticate },
+#if 0
+ { TOK_TYPE_EAP_RESP, TOK_TYPE_EAP_REQ, NULL },
+ { TOK_TYPE_EAP_RESP, TOK_TYPE_EAP_REQ, NULL },
+#endif
+ { TOK_TYPE_GSS_CB, TOK_TYPE_NONE, eapGssSmAcceptGssChannelBindings },
+ { TOK_TYPE_NONE, TOK_TYPE_NONE, eapGssSmAcceptEstablished },
};
OM_uint32
if (GSS_ERROR(major))
goto cleanup;
+ /* If credentials were provided, check they're usable with this mech */
+ if (!gssEapCredAvailable(cred, ctx->mechanismUsed)) {
+ major = GSS_S_BAD_MECH;
+ goto cleanup;
+ }
+
do {
+ sm = &eapGssAcceptorSm[ctx->state];
+
major = (sm->processToken)(minor,
ctx,
cred,
message_token->value = NULL;
message_token->length = 0;
+ if (!CTX_IS_ESTABLISHED(ctx))
+ return GSS_S_NO_CONTEXT;
+
iov[0].type = GSS_IOV_BUFFER_TYPE_DATA;
iov[0].buffer = *message_buffer;
iov[1].buffer.value = NULL;
iov[1].buffer.length = 0;
- major = gssEapWrapOrGetMIC(minor, ctx, FALSE, FALSE, iov, 2, TOK_TYPE_MIC);
+ major = gssEapWrapOrGetMIC(minor, ctx, FALSE, NULL, iov, 2, TOK_TYPE_MIC);
if (major == GSS_S_COMPLETE) {
*message_token = iov[1].buffer;
}
enum eap_gss_state {
EAP_STATE_AUTHENTICATE = 0,
+#if 0
EAP_STATE_KEY_TRANSPORT,
EAP_STATE_SECURE_ASSOCIATION,
+#endif
EAP_STATE_GSS_CHANNEL_BINDINGS,
EAP_STATE_ESTABLISHED
};
#define KEY_USAGE_ACCEPTOR_SIGN 23
#define KEY_USAGE_INITIATOR_SEAL 24
#define KEY_USAGE_INITIATOR_SIGN 25
+#define KEY_USAGE_CHANNEL_BINDINGS 64
/* wrap_iov.c */
OM_uint32
}
static OM_uint32
-completeInit(OM_uint32 *minor,
- gss_ctx_id_t ctx)
+initReady(OM_uint32 *minor, gss_ctx_id_t ctx)
{
OM_uint32 major;
const unsigned char *key;
OM_uint32 tmpMajor, tmpMinor;
time_t now;
int initialContextToken = 0, code;
- struct wpabuf *resp = NULL;
+ gss_buffer_desc respBuf;
+
+ respBuf.length = 0;
+ respBuf.value = NULL;
initialContextToken = (inputToken == GSS_C_NO_BUFFER ||
inputToken->length == 0);
if (GSS_ERROR(major))
goto cleanup;
- /* Use this to emit an empty token*/
- wpabuf_set(&ctx->initiatorCtx.reqData, "", 0);
- resp = &ctx->initiatorCtx.reqData;
+ /* If credentials were provided, check they're usable with this mech */
+ if (!gssEapCredAvailable(cred, ctx->mechanismUsed)) {
+ major = GSS_S_BAD_MECH;
+ goto cleanup;
+ }
+
+ respBuf.value = ""; /* emit empty inner token */
major = GSS_S_CONTINUE_NEEDED;
goto cleanup;
} else {
code = eap_peer_sm_step(ctx->initiatorCtx.eap);
if (ctx->flags & CTX_FLAG_EAP_RESP) {
+ struct wpabuf *resp;
+
ctx->flags &= ~(CTX_FLAG_EAP_RESP);
resp = eap_get_eapRespData(ctx->initiatorCtx.eap);
+ if (resp != NULL) {
+ respBuf.length = wpabuf_len(resp);
+ respBuf.value = (void *)wpabuf_head(resp);
+ }
} else if (ctx->flags & CTX_FLAG_EAP_SUCCESS) {
- major = completeInit(minor, ctx);
+ major = initReady(minor, ctx);
+ if (GSS_ERROR(major))
+ goto cleanup;
+
ctx->flags &= ~(CTX_FLAG_EAP_SUCCESS);
- ctx->state = EAP_STATE_ESTABLISHED;
+ major = GSS_S_CONTINUE_NEEDED;
+ ctx->state = EAP_STATE_GSS_CHANNEL_BINDINGS;
} else if ((ctx->flags & CTX_FLAG_EAP_FAIL) || code == 0) {
major = GSS_S_FAILURE;
}
cleanup:
- if (resp != NULL) {
+ if (respBuf.value != NULL) {
OM_uint32 tmpMajor;
- gss_buffer_desc buf;
assert(major == GSS_S_CONTINUE_NEEDED);
- buf.length = wpabuf_len(resp);
- buf.value = (void *)wpabuf_head(resp);
-
- tmpMajor = duplicateBuffer(&tmpMinor, &buf, outputToken);
+ tmpMajor = duplicateBuffer(&tmpMinor, &respBuf, outputToken);
if (GSS_ERROR(tmpMajor)) {
major = tmpMajor;
*minor = tmpMinor;
return major;
}
+#if 0
static OM_uint32
eapGssSmInitKeyTransport(OM_uint32 *minor,
gss_cred_id_t cred,
{
GSSEAP_NOT_IMPLEMENTED;
}
+#endif
static OM_uint32
eapGssSmInitGssChannelBindings(OM_uint32 *minor,
gss_buffer_t inputToken,
gss_buffer_t outputToken)
{
- GSSEAP_NOT_IMPLEMENTED;
+ OM_uint32 major, tmpMinor;
+ gss_iov_buffer_desc iov[2];
+ gss_buffer_desc buf;
+
+ iov[0].type = GSS_IOV_BUFFER_TYPE_DATA;
+ iov[0].buffer.length = 0;
+ iov[0].buffer.value = NULL;
+
+ iov[1].type = GSS_IOV_BUFFER_TYPE_HEADER | GSS_IOV_BUFFER_FLAG_ALLOCATE;
+ iov[1].buffer.length = 0;
+ iov[1].buffer.value = NULL;
+
+ if (chanBindings != GSS_C_NO_CHANNEL_BINDINGS) {
+ major = gssEapEncodeGssChannelBindings(minor, chanBindings,
+ &iov[0].buffer);
+ if (GSS_ERROR(major))
+ goto cleanup;
+
+ iov[0].type |= GSS_IOV_BUFFER_FLAG_ALLOCATED;
+ } else {
+ iov[0].buffer.length = sizeof("NO_CHANNEL_BINDINGS") - 1;
+ iov[0].buffer.value = "NO_CHANNEL_BINDINGS";
+ }
+
+ major = gssEapWrapOrGetMIC(minor, ctx, FALSE, FALSE, iov, 2,
+ TOK_TYPE_GSS_CB);
+ if (GSS_ERROR(major))
+ goto cleanup;
+
+ /* Skip past token ID */
+ assert(iov[1].buffer.length > 2);
+ assert(load_uint16_be(iov[1].buffer.value) == TOK_TYPE_GSS_CB);
+
+ buf.length = iov[1].buffer.length - 2;
+ buf.value = (unsigned char *)iov[1].buffer.value + 2;
+
+ major = duplicateBuffer(minor, &buf, outputToken);
+ if (GSS_ERROR(major))
+ goto cleanup;
+
+ major = GSS_S_COMPLETE;
+ ctx->state = EAP_STATE_ESTABLISHED;
+
+cleanup:
+ gssEapReleaseIov(iov, 2);
+
+ return major;
}
static OM_uint32
gss_buffer_t);
} eapGssInitiatorSm[] = {
{ TOK_TYPE_EAP_REQ, TOK_TYPE_EAP_RESP, eapGssSmInitAuthenticate },
+#if 0
{ TOK_TYPE_EAP_REQ, TOK_TYPE_EAP_RESP, eapGssSmInitKeyTransport },
{ TOK_TYPE_EAP_REQ, TOK_TYPE_EAP_RESP, eapGssSmInitSecureAssoc },
- { TOK_TYPE_GSS_CB, TOK_TYPE_NONE, eapGssSmInitGssChannelBindings },
+#endif
+ { TOK_TYPE_NONE, TOK_TYPE_GSS_CB, eapGssSmInitGssChannelBindings },
{ TOK_TYPE_NONE, TOK_TYPE_NONE, eapGssSmInitEstablished },
};
* the status is not GSS_S_COMPLETE or an error status.
*/
do {
+ sm = &eapGssInitiatorSm[ctx->state];
+
major = (sm->processToken)(minor,
cred,
ctx,
trailer = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER);
acceptorFlag = CTX_IS_INITIATOR(ctx) ? TOK_FLAG_SENDER_IS_ACCEPTOR : 0;
- keyUsage = (toktype == TOK_TYPE_WRAP
- ? (!CTX_IS_INITIATOR(ctx)
+ switch (toktype) {
+ case TOK_TYPE_WRAP:
+ keyUsage = !CTX_IS_INITIATOR(ctx)
? KEY_USAGE_INITIATOR_SEAL
- : KEY_USAGE_ACCEPTOR_SEAL)
- : (!CTX_IS_INITIATOR(ctx)
+ : KEY_USAGE_ACCEPTOR_SEAL;
+ break;
+ case TOK_TYPE_GSS_CB:
+ keyUsage = KEY_USAGE_CHANNEL_BINDINGS;
+ break;
+ case TOK_TYPE_MIC:
+ default:
+ keyUsage = !CTX_IS_INITIATOR(ctx)
? KEY_USAGE_INITIATOR_SIGN
- : KEY_USAGE_ACCEPTOR_SIGN));
+ : KEY_USAGE_ACCEPTOR_SIGN;
+ break;
+ }
gssEapIovMessageLength(iov, iov_count, &dataLen, &assocDataLen);
}
code = sequenceCheck(&ctx->seqState, seqnum);
- } else if (toktype == TOK_TYPE_MIC) {
- if (load_uint16_be(ptr) != TOK_TYPE_MIC)
+ } else if (toktype == TOK_TYPE_MIC || toktype == TOK_TYPE_GSS_CB) {
+ if (load_uint16_be(ptr) != toktype)
goto defective;
verify_mic_1:
*minor = code;
return GSS_S_BAD_SIG;
}
- code = sequenceCheck(&ctx->seqState, seqnum);
+ if (toktype != TOK_TYPE_GSS_CB)
+ code = sequenceCheck(&ctx->seqState, seqnum);
} else if (toktype == TOK_TYPE_DELETE_CONTEXT) {
if (load_uint16_be(ptr) != TOK_TYPE_DELETE_CONTEXT)
goto defective;
int iov_count,
int *valid);
+OM_uint32
+gssEapEncodeGssChannelBindings(OM_uint32 *minor,
+ gss_channel_bindings_t chanBindings,
+ gss_buffer_t encodedBindings);
+
/* util_context.c */
OM_uint32 gssEapAllocContext(OM_uint32 *minor, gss_ctx_id_t *pCtx);
OM_uint32 gssEapReleaseContext(OM_uint32 *minor, gss_ctx_id_t *pCtx);
gss_OID_set *pActualMechs,
OM_uint32 *timeRec);
+int gssEapCredAvailable(gss_cred_id_t cred, gss_OID mech);
+
/* util_crypt.c */
int
gssEapEncrypt(krb5_context context, int dce_style, size_t ec,
unsigned char **buf,
enum gss_eap_token_type tok_type);
-int
-verifyTokenHeader(gss_OID mech,
+OM_uint32
+verifyTokenHeader(OM_uint32 *minor,
+ gss_OID mech,
size_t *body_size,
unsigned char **buf_in,
size_t toksize_in,
return ((uint64_t)load_uint32_be(p) << 32) | load_uint32_be(p + 4);
}
+static inline void
+store_buffer(gss_buffer_t buffer, void *vp, int wide_nums)
+{
+ unsigned char *p = (unsigned char *)vp;
+
+ if (wide_nums)
+ store_uint64_be(buffer->length, p);
+ else
+ store_uint32_be(buffer->length, p);
+ if (buffer->value != NULL)
+ memcpy(p + 4, buffer->value, buffer->length);
+}
+
#endif /* _UTIL_H_ */
return gssEapChecksum(context, type, rrc, key,
sign_usage, iov, iov_count, 1, valid);
}
+
+OM_uint32
+gssEapEncodeGssChannelBindings(OM_uint32 *minor,
+ gss_channel_bindings_t chanBindings,
+ gss_buffer_t encodedBindings)
+{
+ OM_uint32 major, tmpMinor;
+ size_t length;
+ unsigned char *p;
+
+ if (chanBindings != GSS_C_NO_CHANNEL_BINDINGS) {
+ length = sizeof(OM_uint32) * 5;
+ length += chanBindings->initiator_address.length;
+ length += chanBindings->acceptor_address.length;
+ length += chanBindings->application_data.length;
+
+ encodedBindings->value = GSSEAP_MALLOC(length);
+ if (encodedBindings->value == NULL) {
+ *minor = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+
+ encodedBindings->length = length;
+ p = (unsigned char *)encodedBindings->value;
+
+ store_uint32_be(chanBindings->initiator_addrtype, p);
+ store_buffer(&chanBindings->initiator_address, p + 4, 0);
+ p += 4 + chanBindings->initiator_address.length;
+
+ store_uint32_be(chanBindings->acceptor_addrtype, p);
+ store_buffer(&chanBindings->acceptor_address, p + 4, 0);
+ p += 4 + chanBindings->acceptor_address.length;
+
+ store_buffer(&chanBindings->application_data, p, 1);
+ p += chanBindings->application_data.length;
+ } else {
+ encodedBindings->length = 0;
+ encodedBindings->value = NULL;
+ }
+
+ *minor = 0;
+ return GSS_S_COMPLETE;
+}
oid = &oidBuf;
}
- major = verifyTokenHeader(oid, &bodySize, &p, inputToken->length, tokenType);
+ major = verifyTokenHeader(minor, oid, &bodySize, &p,
+ inputToken->length, tokenType);
if (GSS_ERROR(major))
- return major;
+ return GSS_S_DEFECTIVE_TOKEN;
if (ctx->mechanismUsed == GSS_C_NO_OID) {
if (!gssEapIsConcreteMechanismOid(oid))
buf.value = getlogin(); /* XXX */
buf.length = strlen((char *)buf.value);
- major = gss_import_name(&minor, &buf,
+ major = gss_import_name(minor, &buf,
GSS_C_NT_USER_NAME, &cred->name);
if (GSS_ERROR(major))
goto cleanup;
return major;
}
+
+int
+gssEapCredAvailable(gss_cred_id_t cred, gss_OID mech)
+{
+ OM_uint32 minor;
+ int present = 0;
+
+ assert(mech != GSS_C_NO_OID);
+
+ if (cred == GSS_C_NO_CREDENTIAL || cred->mechanisms == GSS_C_NO_OID_SET)
+ return TRUE;
+
+ gss_test_oid_set_member(&minor, mech, cred->mechanisms, &present);
+
+ return present;
+}
* *body_size are left unmodified on error.
*/
-int
-verifyTokenHeader(gss_OID mech,
+OM_uint32
+verifyTokenHeader(OM_uint32 *minor,
+ gss_OID mech,
size_t *body_size,
unsigned char **buf_in,
size_t toksize_in,
gss_OID_desc toid;
ssize_t toksize = (ssize_t)toksize_in;
+ *minor = 0;
+
if ((toksize -= 1) < 0)
- return ERANGE;
+ return GSS_S_DEFECTIVE_TOKEN;
if (*buf++ != 0x60)
- return EINVAL;
+ return GSS_S_DEFECTIVE_TOKEN;
seqsize = der_read_length(&buf, &toksize);
if (seqsize < 0)
- return ERANGE;
+ return GSS_S_DEFECTIVE_TOKEN;
if (seqsize != toksize)
- return ERANGE;
+ return GSS_S_DEFECTIVE_TOKEN;
if ((toksize -= 1) < 0)
- return ERANGE;
+ return GSS_S_DEFECTIVE_TOKEN;
if (*buf++ != 0x06)
- return EINVAL;
+ return GSS_S_DEFECTIVE_TOKEN;
if ((toksize -= 1) < 0)
- return ERANGE;
+ return GSS_S_DEFECTIVE_TOKEN;
toid.length = *buf++;
if ((toksize -= toid.length) < 0)
- return ERANGE;
+ return GSS_S_DEFECTIVE_TOKEN;
toid.elements = buf;
buf += toid.length;
if (mech->elements == NULL) {
*mech = toid;
if (toid.length == 0)
- return EINVAL;
+ return GSS_S_BAD_MECH;
} else if (!oidEqual(&toid, mech)) {
- return EINVAL;
+ return GSS_S_BAD_MECH;
}
if (tok_type != TOK_TYPE_NONE) {
if ((toksize -= 2) < 0)
- return EINVAL;
+ return GSS_S_DEFECTIVE_TOKEN;
if ((*buf++ != ((tok_type >> 8) & 0xff)) ||
(*buf++ != (tok_type & 0xff)))
- return EINVAL;
+ return GSS_S_DEFECTIVE_TOKEN;
}
*buf_in = buf;
*body_size = toksize;
- return 0;
+ return GSS_S_COMPLETE;
}
size_t dataLen, assocDataLen;
krb5_context krbContext;
- if (!CTX_IS_ESTABLISHED(ctx))
- return GSS_S_NO_CONTEXT;
-
if (ctx->encryptionType == ENCTYPE_NULL)
return GSS_S_UNAVAILABLE;
GSSEAP_KRB_INIT(&krbContext);
acceptorFlag = CTX_IS_INITIATOR(ctx) ? 0 : TOK_FLAG_SENDER_IS_ACCEPTOR;
- keyUsage = ((toktype == TOK_TYPE_WRAP)
- ? (CTX_IS_INITIATOR(ctx)
+
+ switch (toktype) {
+ case TOK_TYPE_WRAP:
+ keyUsage = CTX_IS_INITIATOR(ctx)
? KEY_USAGE_INITIATOR_SEAL
- : KEY_USAGE_ACCEPTOR_SEAL)
- : (CTX_IS_INITIATOR(ctx)
+ : KEY_USAGE_ACCEPTOR_SEAL;
+ break;
+ case TOK_TYPE_GSS_CB:
+ keyUsage = KEY_USAGE_CHANNEL_BINDINGS;
+ break;
+ case TOK_TYPE_MIC:
+ default:
+ keyUsage = CTX_IS_INITIATOR(ctx)
? KEY_USAGE_INITIATOR_SIGN
- : KEY_USAGE_ACCEPTOR_SIGN));
+ : KEY_USAGE_ACCEPTOR_SIGN;
+ break;
+ }
gssEapIovMessageLength(iov, iov_count, &dataLen, &assocDataLen);
if (code != 0)
goto cleanup;
- ctx->sendSeq++;
+ if (toktype != TOK_TYPE_GSS_CB)
+ ctx->sendSeq++;
if (toktype == TOK_TYPE_WRAP) {
/* Fix up EC field */
/* Fix up RRC field */
store_uint16_be(rrc, outbuf + 6);
}
- } else if (toktype == TOK_TYPE_MIC) {
+ } else if (toktype == TOK_TYPE_MIC || toktype == TOK_TYPE_GSS_CB) {
trailer = NULL;
goto wrap_with_checksum;
} else if (toktype == TOK_TYPE_DELETE_CONTEXT) {
gss_iov_buffer_desc *iov,
int iov_count)
{
+ if (!CTX_IS_ESTABLISHED(ctx))
+ return GSS_S_NO_CONTEXT;
+
return gssEapWrapOrGetMIC(minor, ctx, conf_req_flag, conf_state,
iov, iov_count, TOK_TYPE_WRAP);
}