/*
- * Copyright (c) 2011, JANET(UK)
+ * Copyright (c) 2011, 2013, 2015, JANET(UK)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
eapGssSmAcceptGssReauth(OM_uint32 *minor,
gss_cred_id_t cred,
gss_ctx_id_t ctx,
- gss_name_t target,
+ gss_const_name_t target,
gss_OID mech,
OM_uint32 reqFlags,
OM_uint32 timeReq,
acceptReadyEap(OM_uint32 *minor, gss_ctx_id_t ctx, gss_cred_id_t cred)
{
OM_uint32 major, tmpMinor;
- VALUE_PAIR *vp;
+ rs_const_avp *vp;
gss_buffer_desc nameBuf = GSS_C_EMPTY_BUFFER;
/* Cache encryption type derived from selected mechanism OID */
major = gssEapRadiusGetRawAvp(minor, ctx->acceptorCtx.vps,
PW_USER_NAME, 0, &vp);
- if (major == GSS_S_COMPLETE && vp->length) {
- nameBuf.length = vp->length;
- nameBuf.value = vp->vp_strvalue;
+ if (major == GSS_S_COMPLETE && rs_avp_length(vp) != 0) {
+ rs_avp_octets_value_byref((rs_avp *)vp,
+ (unsigned char **)&nameBuf.value,
+ &nameBuf.length);
} else {
ctx->gssFlags |= GSS_C_ANON_FLAG;
}
return major;
major = gssEapRadiusGetRawAvp(minor, ctx->acceptorCtx.vps,
- PW_MS_MPPE_SEND_KEY, VENDORPEC_MS, &vp);
+ PW_MS_MPPE_SEND_KEY, VENDORPEC_MICROSOFT, &vp);
if (GSS_ERROR(major)) {
*minor = GSSEAP_KEY_UNAVAILABLE;
return GSS_S_UNAVAILABLE;
}
major = gssEapDeriveRfc3961Key(minor,
- vp->vp_octets,
- vp->length,
+ rs_avp_octets_value_const_ptr(vp),
+ rs_avp_length(vp),
ctx->encryptionType,
&ctx->rfc3961Key);
if (GSS_ERROR(major))
eapGssSmAcceptAcceptorName(OM_uint32 *minor,
gss_cred_id_t cred GSSEAP_UNUSED,
gss_ctx_id_t ctx,
- gss_name_t target GSSEAP_UNUSED,
+ gss_const_name_t target GSSEAP_UNUSED,
gss_OID mech GSSEAP_UNUSED,
OM_uint32 reqFlags GSSEAP_UNUSED,
OM_uint32 timeReq GSSEAP_UNUSED,
eapGssSmAcceptVendorInfo(OM_uint32 *minor,
gss_cred_id_t cred GSSEAP_UNUSED,
gss_ctx_id_t ctx GSSEAP_UNUSED,
- gss_name_t target GSSEAP_UNUSED,
+ gss_const_name_t target GSSEAP_UNUSED,
gss_OID mech GSSEAP_UNUSED,
OM_uint32 reqFlags GSSEAP_UNUSED,
OM_uint32 timeReq GSSEAP_UNUSED,
eapGssSmAcceptIdentity(OM_uint32 *minor,
gss_cred_id_t cred,
gss_ctx_id_t ctx,
- gss_name_t target GSSEAP_UNUSED,
+ gss_const_name_t target GSSEAP_UNUSED,
gss_OID mech GSSEAP_UNUSED,
OM_uint32 reqFlags GSSEAP_UNUSED,
OM_uint32 timeReq GSSEAP_UNUSED,
static OM_uint32
setInitiatorIdentity(OM_uint32 *minor,
gss_ctx_id_t ctx,
- VALUE_PAIR **vps)
+ struct rs_packet *req)
{
OM_uint32 major, tmpMinor;
gss_buffer_desc nameBuf;
if (GSS_ERROR(major))
return major;
- major = gssEapRadiusAddAvp(minor, vps, PW_USER_NAME, 0, &nameBuf);
+ major = gssEapRadiusAddAvp(minor, req, PW_USER_NAME, 0, &nameBuf);
if (GSS_ERROR(major))
return major;
static OM_uint32
setAcceptorIdentity(OM_uint32 *minor,
gss_ctx_id_t ctx,
- VALUE_PAIR **vps)
+ struct rs_packet *req)
{
OM_uint32 major;
gss_buffer_desc nameBuf;
krbPrinc = ctx->acceptorName->krbPrincipal;
GSSEAP_ASSERT(krbPrinc != NULL);
- GSSEAP_ASSERT(KRB_PRINC_LENGTH(krbPrinc) >= 2);
+ GSSEAP_ASSERT(KRB_PRINC_LENGTH(krbPrinc) >= 1);
/* Acceptor-Service-Name */
krbPrincComponentToGssBuffer(krbPrinc, 0, &nameBuf);
- major = gssEapRadiusAddAvp(minor, vps,
+ major = gssEapRadiusAddAvp(minor, req,
PW_GSS_ACCEPTOR_SERVICE_NAME,
- VENDORPEC_UKERNA,
+ 0,
&nameBuf);
if (GSS_ERROR(major))
return major;
/* Acceptor-Host-Name */
- krbPrincComponentToGssBuffer(krbPrinc, 1, &nameBuf);
-
- major = gssEapRadiusAddAvp(minor, vps,
- PW_GSS_ACCEPTOR_HOST_NAME,
- VENDORPEC_UKERNA,
- &nameBuf);
- if (GSS_ERROR(major))
- return major;
-
+ if (KRB_PRINC_LENGTH(krbPrinc) >= 2) {
+ krbPrincComponentToGssBuffer(krbPrinc, 1, &nameBuf);
+
+ major = gssEapRadiusAddAvp(minor, req,
+ PW_GSS_ACCEPTOR_HOST_NAME,
+ 0,
+ &nameBuf);
+ if (GSS_ERROR(major))
+ return major;
+ }
if (KRB_PRINC_LENGTH(krbPrinc) > 2) {
/* Acceptor-Service-Specific */
*minor = krbPrincUnparseServiceSpecifics(krbContext,
if (*minor != 0)
return GSS_S_FAILURE;
- major = gssEapRadiusAddAvp(minor, vps,
- PW_GSS_ACCEPTOR_SERVICE_SPECIFIC,
- VENDORPEC_UKERNA,
+ major = gssEapRadiusAddAvp(minor, req,
+ PW_GSS_ACCEPTOR_SERVICE_SPECIFICS,
+ 0,
&nameBuf);
-
- if (GSS_ERROR(major)) {
- krbFreeUnparsedName(krbContext, &nameBuf);
+ krbFreeUnparsedName(krbContext, &nameBuf);
+ if (GSS_ERROR(major))
return major;
- }
- krbFreeUnparsedName(krbContext, &nameBuf);
}
krbPrincRealmToGssBuffer(krbPrinc, &nameBuf);
if (nameBuf.length != 0) {
/* Acceptor-Realm-Name */
- major = gssEapRadiusAddAvp(minor, vps,
+ major = gssEapRadiusAddAvp(minor, req,
PW_GSS_ACCEPTOR_REALM_NAME,
- VENDORPEC_UKERNA,
+ 0,
&nameBuf);
if (GSS_ERROR(major))
return major;
return GSS_S_COMPLETE;
}
+/**
+ * Choose the correct error for an access reject packet.
+ */
+static OM_uint32
+eapGssAcceptHandleReject(OM_uint32 *minor,
+ struct rs_packet *response)
+{
+ rs_avp **vps;
+ rs_const_avp *vp = NULL;
+ OM_uint32 major;
+ const char *reply_message = NULL;
+ size_t reply_length = 0;
+
+ rs_packet_avps(response, &vps);
+ major = gssEapRadiusGetRawAvp(minor, *vps,
+ PW_REPLY_MESSAGE, 0, &vp);
+ if (!GSS_ERROR(major)) {
+ reply_message = rs_avp_string_value(vp);
+ reply_length = rs_avp_length(vp);
+ }
+
+ major = gssEapRadiusGetRawAvp(minor, *vps,
+ PW_ERROR_CAUSE, 0, &vp);
+ if (!GSS_ERROR(major)) {
+ switch (rs_avp_integer_value(vp)) {
+ /* Values from http://www.iana.org/assignments/radius-types/radius-types.xhtml#radius-types-18 */
+ case 502: /* request not routable (proxy) */
+ *minor = GSSEAP_RADIUS_UNROUTABLE;
+ break;
+ case 501: /* administratively prohibited */
+ *minor = GSSEAP_RADIUS_ADMIN_PROHIBIT;
+ break;
+
+ default:
+ *minor = GSSEAP_RADIUS_AUTH_FAILURE;
+ break;
+ }
+ } else
+ *minor = GSSEAP_RADIUS_AUTH_FAILURE;
+
+ if (reply_message != NULL)
+ gssEapSaveStatusInfo(*minor, "%s: %.*s", error_message(*minor),
+ reply_length, reply_message);
+ else
+ gssEapSaveStatusInfo(*minor, "%s", error_message(*minor));
+
+ return GSS_S_DEFECTIVE_CREDENTIAL;
+}
+
/*
* Process a EAP response from the initiator.
*/
eapGssSmAcceptAuthenticate(OM_uint32 *minor,
gss_cred_id_t cred,
gss_ctx_id_t ctx,
- gss_name_t target GSSEAP_UNUSED,
+ gss_const_name_t target GSSEAP_UNUSED,
gss_OID mech GSSEAP_UNUSED,
OM_uint32 reqFlags GSSEAP_UNUSED,
OM_uint32 timeReq GSSEAP_UNUSED,
struct rs_connection *rconn;
struct rs_request *request = NULL;
struct rs_packet *req = NULL, *resp = NULL;
- struct radius_packet *frreq, *frresp;
+ int isAccessChallenge;
if (ctx->acceptorCtx.radContext == NULL) {
/* May be NULL from an imported partial context */
major = gssEapRadiusMapError(minor, rs_err_conn_pop(rconn));
goto cleanup;
}
- frreq = rs_packet_frpkt(req);
- major = setInitiatorIdentity(minor, ctx, &frreq->vps);
+ major = setInitiatorIdentity(minor, ctx, req);
if (GSS_ERROR(major))
goto cleanup;
- major = setAcceptorIdentity(minor, ctx, &frreq->vps);
+ major = setAcceptorIdentity(minor, ctx, req);
if (GSS_ERROR(major))
goto cleanup;
- major = gssEapRadiusAddAvp(minor, &frreq->vps,
+ major = gssEapRadiusAddAvp(minor, req,
PW_EAP_MESSAGE, 0, inputToken);
if (GSS_ERROR(major))
goto cleanup;
if (ctx->acceptorCtx.state.length != 0) {
- major = gssEapRadiusAddAvp(minor, &frreq->vps, PW_STATE, 0,
+ major = gssEapRadiusAddAvp(minor, req, PW_STATE, 0,
&ctx->acceptorCtx.state);
if (GSS_ERROR(major))
goto cleanup;
GSSEAP_ASSERT(resp != NULL);
- frresp = rs_packet_frpkt(resp);
- switch (frresp->code) {
+ isAccessChallenge = 0;
+
+ switch (rs_packet_code(resp)) {
case PW_ACCESS_CHALLENGE:
- case PW_AUTHENTICATION_ACK:
+ isAccessChallenge = 1;
break;
- case PW_AUTHENTICATION_REJECT:
- *minor = GSSEAP_RADIUS_AUTH_FAILURE;
- major = GSS_S_DEFECTIVE_CREDENTIAL;
+ case PW_ACCESS_ACCEPT:
+ break;
+ case PW_ACCESS_REJECT:
+ major = eapGssAcceptHandleReject( minor, resp);
goto cleanup;
break;
default:
break;
}
- major = gssEapRadiusGetAvp(minor, frresp->vps, PW_EAP_MESSAGE, 0,
+ major = gssEapRadiusGetAvp(minor, resp, PW_EAP_MESSAGE, 0,
outputToken, TRUE);
- if (major == GSS_S_UNAVAILABLE && frresp->code == PW_ACCESS_CHALLENGE) {
+ if (major == GSS_S_UNAVAILABLE && isAccessChallenge) {
*minor = GSSEAP_MISSING_EAP_REQUEST;
major = GSS_S_DEFECTIVE_TOKEN;
goto cleanup;
} else if (GSS_ERROR(major))
goto cleanup;
- if (frresp->code == PW_ACCESS_CHALLENGE) {
- major = gssEapRadiusGetAvp(minor, frresp->vps, PW_STATE, 0,
+ if (isAccessChallenge) {
+ major = gssEapRadiusGetAvp(minor, resp, PW_STATE, 0,
&ctx->acceptorCtx.state, TRUE);
if (GSS_ERROR(major) && *minor != GSSEAP_NO_SUCH_ATTR)
goto cleanup;
} else {
- ctx->acceptorCtx.vps = frresp->vps;
- frresp->vps = NULL;
+ rs_avp **vps;
+
+ rs_packet_avps(resp, &vps);
+
+ ctx->acceptorCtx.vps = *vps;
+ *vps = NULL;
major = acceptReadyEap(minor, ctx, cred);
if (GSS_ERROR(major))
eapGssSmAcceptGssFlags(OM_uint32 *minor,
gss_cred_id_t cred GSSEAP_UNUSED,
gss_ctx_id_t ctx,
- gss_name_t target GSSEAP_UNUSED,
+ gss_const_name_t target GSSEAP_UNUSED,
gss_OID mech GSSEAP_UNUSED,
OM_uint32 reqFlags GSSEAP_UNUSED,
OM_uint32 timeReq GSSEAP_UNUSED,
eapGssSmAcceptGssChannelBindings(OM_uint32 *minor,
gss_cred_id_t cred GSSEAP_UNUSED,
gss_ctx_id_t ctx,
- gss_name_t target GSSEAP_UNUSED,
+ gss_const_name_t target GSSEAP_UNUSED,
gss_OID mech GSSEAP_UNUSED,
OM_uint32 reqFlags GSSEAP_UNUSED,
OM_uint32 timeReq GSSEAP_UNUSED,
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;
+#ifdef HAVE_HEIMDAL_VERSION
+ krb5_crypto krbCrypto;
+#endif
- 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);
+
+#ifdef HAVE_HEIMDAL_VERSION
+ code = krb5_crypto_init(krbContext, &ctx->rfc3961Key, 0, &krbCrypto);
+ 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;
+ code = krb5_verify_checksum(krbContext, krbCrypto,
+ KEY_USAGE_GSSEAP_CHBIND_MIC,
+ data.data, data.length, &cksum);
+ if (code == KRB5KRB_AP_ERR_BAD_INTEGRITY) {
+ code = 0;
+ valid = FALSE;
+ } else if (code == 0) {
+ valid = TRUE;
+ }
+
+ krb5_crypto_destroy(krbContext, krbCrypto);
+#else
+ code = krb5_c_verify_checksum(krbContext, &ctx->rfc3961Key,
+ KEY_USAGE_GSSEAP_CHBIND_MIC,
+ &data, &cksum, &valid);
+#endif /* HAVE_HEIMDAL_VERSION */
+ if (code != 0) {
+ *minor = code;
+ return GSS_S_FAILURE;
+ }
+
+ 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
eapGssSmAcceptInitiatorMIC(OM_uint32 *minor,
gss_cred_id_t cred GSSEAP_UNUSED,
gss_ctx_id_t ctx,
- gss_name_t target GSSEAP_UNUSED,
+ gss_const_name_t target GSSEAP_UNUSED,
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;
eapGssSmAcceptReauthCreds(OM_uint32 *minor,
gss_cred_id_t cred,
gss_ctx_id_t ctx,
- gss_name_t target GSSEAP_UNUSED,
+ gss_const_name_t target GSSEAP_UNUSED,
gss_OID mech GSSEAP_UNUSED,
OM_uint32 reqFlags GSSEAP_UNUSED,
OM_uint32 timeReq GSSEAP_UNUSED,
eapGssSmAcceptAcceptorMIC(OM_uint32 *minor,
gss_cred_id_t cred GSSEAP_UNUSED,
gss_ctx_id_t ctx,
- gss_name_t target GSSEAP_UNUSED,
+ gss_const_name_t target GSSEAP_UNUSED,
gss_OID mech GSSEAP_UNUSED,
OM_uint32 reqFlags GSSEAP_UNUSED,
OM_uint32 timeReq GSSEAP_UNUSED,
ITOK_TYPE_GSS_CHANNEL_BINDINGS,
ITOK_TYPE_NONE,
GSSEAP_STATE_INITIATOR_EXTS,
- SM_ITOK_FLAG_REQUIRED,
+ 0,
eapGssSmAcceptGssChannelBindings,
},
{
#endif
{
ITOK_TYPE_NONE,
+ ITOK_TYPE_ACCEPTOR_NAME_RESP,
+ GSSEAP_STATE_ACCEPTOR_EXTS,
+ 0,
+ eapGssSmAcceptAcceptorName
+ },
+ {
+ ITOK_TYPE_NONE,
ITOK_TYPE_ACCEPTOR_MIC,
GSSEAP_STATE_ACCEPTOR_EXTS,
0,
* credential handle.
*/
- /*
- * Calling gssEapInquireCred() forces the default acceptor credential name
- * to be resolved.
- */
- major = gssEapInquireCred(minor, cred, &ctx->acceptorName, NULL, NULL, NULL);
- if (GSS_ERROR(major))
- goto cleanup;
+ if (cred->name != GSS_C_NO_NAME) {
+ major = gssEapDuplicateName(minor, cred->name, &ctx->acceptorName);
+ if (GSS_ERROR(major))
+ goto cleanup;
+ }
major = gssEapSmStep(minor,
cred,
eapGssSmAcceptGssReauth(OM_uint32 *minor,
gss_cred_id_t cred,
gss_ctx_id_t ctx,
- gss_name_t target GSSEAP_UNUSED,
+ gss_const_name_t target GSSEAP_UNUSED,
gss_OID mech,
OM_uint32 reqFlags GSSEAP_UNUSED,
OM_uint32 timeReq GSSEAP_UNUSED,
OM_uint32 GSSAPI_CALLCONV
gss_accept_sec_context(OM_uint32 *minor,
gss_ctx_id_t *context_handle,
+#ifdef HAVE_HEIMDAL_VERSION
+ gss_const_cred_id_t cred,
+#else
gss_cred_id_t cred,
+#endif
gss_buffer_t input_token,
gss_channel_bindings_t input_chan_bindings,
gss_name_t *src_name,
major = gssEapAcceptSecContext(minor,
ctx,
- cred,
+ (gss_cred_id_t)cred,
input_token,
input_chan_bindings,
src_name,
if (GSS_ERROR(major))
gssEapReleaseContext(&tmpMinor, context_handle);
+ gssEapTraceStatus("gss_accept_sec_context", major, *minor);
+
return major;
}