#endif /* __SYMBIAN32__ */
#ifdef CONFIG_NATIVE_WINDOWS
+ #ifdef CONFIG_IPV6
+ #include <winsock2.h>
+ #include <ws2tcpip.h>
+ #else
#include <winsock.h>
+ #endif
typedef int socklen_t;
#endif /* CONFIG_NATIVE_WINDOWS */
#ifdef _MSC_VER
+#ifndef __cplusplus
#define inline __inline
+#endif
#undef vsnprintf
#define vsnprintf _vsnprintf
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))
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;
/* 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,
+ major = gssEapRadiusAddAvp(minor, req,
PW_GSS_ACCEPTOR_HOST_NAME,
- VENDORPEC_UKERNA,
+ 0,
&nameBuf);
if (GSS_ERROR(major))
return major;
if (KRB_PRINC_LENGTH(krbPrinc) > 2) {
/* Acceptor-Service-Specific */
- krb5_principal_data ssiPrinc = *krbPrinc;
- char *ssi;
-
- KRB_PRINC_LENGTH(&ssiPrinc) -= 2;
- KRB_PRINC_NAME(&ssiPrinc) += 2;
-
- *minor = krb5_unparse_name_flags(krbContext, &ssiPrinc,
- KRB5_PRINCIPAL_UNPARSE_NO_REALM, &ssi);
+ *minor = krbPrincUnparseServiceSpecifics(krbContext,
+ krbPrinc, &nameBuf);
if (*minor != 0)
return GSS_S_FAILURE;
- nameBuf.value = ssi;
- nameBuf.length = strlen(ssi);
-
- 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);
+#ifdef HAVE_HEIMDAL_VERSION
+ krb5_xfree(ssi);
+#else
+ krb5_free_unparsed_name(krbContext, ssi);
+#endif
+ 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;
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_ACCESS_ACCEPT:
break;
- case PW_AUTHENTICATION_REJECT:
+ case PW_ACCESS_REJECT:
*minor = GSSEAP_RADIUS_AUTH_FAILURE;
major = GSS_S_DEFECTIVE_CREDENTIAL;
goto cleanup;
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))
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,
},
{
#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,
BEGIN-VENDOR UKERNA
-ATTRIBUTE GSS-Acceptor-Service-Name 128 string
-ATTRIBUTE GSS-Acceptor-Host-Name 129 string
-ATTRIBUTE GSS-Acceptor-Service-Specific 130 string
-ATTRIBUTE GSS-Acceptor-Realm-Name 131 string
-ATTRIBUTE SAML-AAA-Assertion 132 string
-ATTRIBUTE MS-Windows-Auth-Data 133 octets
-ATTRIBUTE MS-Windows-Group-Sid 134 string
+ATTRIBUTE GSS-Acceptor-Service-Name-VS 128 string
+ATTRIBUTE GSS-Acceptor-Host-Name-VS 129 string
+ATTRIBUTE GSS-Acceptor-Service-Specific-VS 130 string
+ATTRIBUTE GSS-Acceptor-Realm-Name-VS 131 string
+ATTRIBUTE SAML-AAA-Assertion 132 string
+ATTRIBUTE MS-Windows-Auth-Data 133 octets
+ATTRIBUTE MS-Windows-Group-Sid 134 string
+ ATTRIBUTE EAP-Channel-Binding-Message 135 octets
END-VENDOR UKERNA
#include <wpabuf.h>
#ifdef GSSEAP_ENABLE_ACCEPTOR
-/* FreeRADIUS headers */
-#ifdef __cplusplus
-extern "C" {
-#ifndef WIN32
-#define operator fr_operator
-#endif
-#endif
-#include <freeradius/libradius.h>
-#include <freeradius/radius.h>
-
-#undef pid_t
-
/* libradsec headers */
#include <radsec/radsec.h>
#include <radsec/request.h>
-#ifdef __cplusplus
-#ifndef WIN32
-#undef operator
+#include <radsec/radius.h>
#endif
-}
-#endif
-#endif /* GSSEAP_ENABLE_ACCEPTOR */
#include "gsseap_err.h"
#include "radsec_err.h"
#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 CTX_FLAG_EAP_PORT_ENABLED 0x00400000
#define CTX_FLAG_EAP_ALT_ACCEPT 0x00800000
#define CTX_FLAG_EAP_ALT_REJECT 0x01000000
+ #define CTX_FLAG_EAP_CHBIND_ACCEPT 0x02000000
#define CTX_FLAG_EAP_MASK 0xFFFF0000
struct gss_eap_initiator_ctx {
struct eap_peer_config eapPeerConfig;
struct eap_sm *eap;
struct wpabuf reqData;
+ struct wpabuf *chbindData;
+ unsigned int chbindReqFlags;
};
#ifdef GSSEAP_ENABLE_ACCEPTOR
struct rs_connection *radConn;
char *radServer;
gss_buffer_desc state;
- VALUE_PAIR *vps;
+ rs_avp *vps;
};
#endif
#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,
#define IS_WIRE_ERROR(err) ((err) > GSSEAP_RESERVED && \
(err) <= GSSEAP_RADIUS_PROT_FAILURE)
-/* upper bound of RADIUS error range must be kept in sync with radsec.h */
+#ifdef GSSEAP_ENABLE_ACCEPTOR
#define IS_RADIUS_ERROR(err) ((err) >= ERROR_TABLE_BASE_rse && \
- (err) <= ERROR_TABLE_BASE_rse + 20)
+ (err) <= ERROR_TABLE_BASE_rse + RSE_MAX)
+#else
+#define IS_RADIUS_ERROR(err) (0)
+#endif
/* exchange_meta_data.c */
OM_uint32 GSSAPI_CALLCONV
gss_ctx_id_t ctx,
int prf_key,
const gss_buffer_t prf_in,
- ssize_t desired_output_len,
gss_buffer_t prf_out);
/* query_mechanism_info.c */
error_code GSSEAP_BAD_INITIATOR_NAME, "Initiator identity must be a valid name"
error_code GSSEAP_NO_HOSTNAME, "Could not determine local host name"
error_code GSSEAP_NO_ACCEPTOR_NAME, "Could not determine acceptor identity"
+error_code GSSEAP_WRONG_ACCEPTOR_NAME, "Acceptor identity different than expected"
+ error_code GSSEAP_BAD_ACCEPTOR_NAME, "Acceptor name is too long or has too many components"
error_code GSSEAP_BAD_NAME_TOKEN, "Name token is malformed or corrupt"
error_code GSSEAP_NO_LOCAL_MAPPING, "Unable to map name to a local identity"
# 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"
*/
#include "gssapiP_eap.h"
+ #include "radius/radius.h"
+ #include "util_radius.h"
+ #include "utils/radius_utils.h"
static OM_uint32
policyVariableToFlag(enum eapol_bool_var variable)
extern int wpa_debug_level;
#endif
+ #define CHBIND_SERVICE_NAME_FLAG 0x01
+ #define CHBIND_HOST_NAME_FLAG 0x02
+ #define CHBIND_SERVICE_SPECIFIC_FLAG 0x04
+ #define CHBIND_REALM_NAME_FLAG 0x08
+
+ extern void TestFunc();
+
+ static OM_uint32
+ peerInitEapChannelBinding(OM_uint32 *minor, gss_ctx_id_t ctx)
+ {
+ struct wpabuf *buf = NULL;
+ unsigned int chbindReqFlags = 0;
+ krb5_principal princ = NULL;
+ gss_buffer_desc nameBuf = GSS_C_EMPTY_BUFFER;
+ OM_uint32 major = GSS_S_COMPLETE;
+ krb5_context krbContext = NULL;
+
+ /* XXX is this check redundant? */
+ if (ctx->acceptorName == GSS_C_NO_NAME) {
+ major = GSS_S_BAD_NAME;
+ *minor = GSSEAP_NO_ACCEPTOR_NAME;
+ goto cleanup;
+ }
+
+ princ = ctx->acceptorName->krbPrincipal;
+
+ krbPrincComponentToGssBuffer(princ, 0, &nameBuf);
+ if (nameBuf.length > 0) {
+ major = gssEapRadiusAddAttr(minor, &buf, PW_GSS_ACCEPTOR_SERVICE_NAME,
+ 0, &nameBuf);
+ if (GSS_ERROR(major))
+ goto cleanup;
+
+ chbindReqFlags |= CHBIND_SERVICE_NAME_FLAG;
+ }
+
+ krbPrincComponentToGssBuffer(princ, 1, &nameBuf);
+ if (nameBuf.length > 0) {
+ major = gssEapRadiusAddAttr(minor, &buf, PW_GSS_ACCEPTOR_HOST_NAME,
+ 0, &nameBuf);
+ if (GSS_ERROR(major))
+ goto cleanup;
+
+ chbindReqFlags |= CHBIND_HOST_NAME_FLAG;
+ }
+
+ GSSEAP_KRB_INIT(&krbContext);
+
+ *minor = krbPrincUnparseServiceSpecifics(krbContext, princ, &nameBuf);
+ if (*minor != 0)
+ goto cleanup;
+
+ if (nameBuf.length > 0) {
+ major = gssEapRadiusAddAttr(minor, &buf,
+ PW_GSS_ACCEPTOR_SERVICE_SPECIFICS,
+ 0, &nameBuf);
+ if (GSS_ERROR(major))
+ goto cleanup;
+
+ chbindReqFlags |= CHBIND_SERVICE_SPECIFIC_FLAG;
+ }
+
+ krbFreeUnparsedName(krbContext, &nameBuf);
+ krbPrincRealmToGssBuffer(princ, &nameBuf);
+
+ if (nameBuf.length > 0) {
+ major = gssEapRadiusAddAttr(minor, &buf,
+ PW_GSS_ACCEPTOR_REALM_NAME,
+ 0, &nameBuf);
+ chbindReqFlags |= CHBIND_REALM_NAME_FLAG;
+ }
+
+ if (chbindReqFlags == 0) {
+ major = GSS_S_BAD_NAME;
+ *minor = GSSEAP_BAD_ACCEPTOR_NAME;
+ goto cleanup;
+ }
+
+ ctx->initiatorCtx.chbindData = buf;
+ ctx->initiatorCtx.chbindReqFlags = chbindReqFlags;
+
+ buf = NULL;
+
+ major = GSS_S_COMPLETE;
+ *minor = 0;
+
+ cleanup:
+ krbFreeUnparsedName(krbContext, &nameBuf);
+ wpabuf_free(buf);
+
+ return major;
+ }
+
+ static void
+ peerProcessChbindResponse(void *context, int code, int nsid,
+ u8 *data, size_t len)
+ {
+ radius_parser msg;
+ gss_ctx_id_t ctx = (gss_ctx_id_t )context;
+ void *vsadata;
+ u8 type;
+ u32 vendor_id;
+ u32 chbindRetFlags = 0;
+ size_t vsadata_len;
+
+ if (nsid != CHBIND_NSID_RADIUS)
+ return;
+
+ msg = radius_parser_start(data, len);
+ if (msg == NULL)
+ return;
+
+ while (radius_parser_parse_tlv(msg, &type, &vendor_id, &vsadata,
+ &vsadata_len) == 0) {
+ switch (type) {
+ case PW_GSS_ACCEPTOR_SERVICE_NAME:
+ chbindRetFlags |= CHBIND_SERVICE_NAME_FLAG;
+ break;
+ case PW_GSS_ACCEPTOR_HOST_NAME:
+ chbindRetFlags |= CHBIND_HOST_NAME_FLAG;
+ break;
+ case PW_GSS_ACCEPTOR_SERVICE_SPECIFICS:
+ chbindRetFlags |= CHBIND_SERVICE_SPECIFIC_FLAG;
+ break;
+ case PW_GSS_ACCEPTOR_REALM_NAME:
+ chbindRetFlags |= CHBIND_REALM_NAME_FLAG;
+ break;
+ }
+ }
+
+ radius_parser_finish(msg);
+
+ if (code == CHBIND_CODE_SUCCESS &&
+ ((chbindRetFlags & ctx->initiatorCtx.chbindReqFlags) == ctx->initiatorCtx.chbindReqFlags)) {
+ ctx->flags |= CTX_FLAG_EAP_CHBIND_ACCEPT;
+ ctx->gssFlags |= GSS_C_MUTUAL_FLAG;
+ } /* else log failures? */
+ }
+
static OM_uint32
peerConfigInit(OM_uint32 *minor, gss_ctx_id_t ctx)
{
eapPeerConfig->subject_match = (unsigned char *)cred->subjectNameConstraint.value;
eapPeerConfig->altsubject_match = (unsigned char *)cred->subjectAltNameConstraint.value;
+ /* eap channel binding */
+ if (ctx->initiatorCtx.chbindData != NULL) {
+ struct eap_peer_chbind_config *chbind_config =
+ (struct eap_peer_chbind_config *)GSSEAP_MALLOC(sizeof(struct eap_peer_chbind_config));
+ if (chbind_config == NULL) {
+ *minor = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+
+ chbind_config->req_data = wpabuf_mhead_u8(ctx->initiatorCtx.chbindData);
+ chbind_config->req_data_len = wpabuf_len(ctx->initiatorCtx.chbindData);
+ chbind_config->nsid = CHBIND_NSID_RADIUS;
+ chbind_config->response_cb = &peerProcessChbindResponse;
+ chbind_config->ctx = ctx;
+ eapPeerConfig->chbind_config = chbind_config;
+ eapPeerConfig->chbind_config_len = 1;
+ } else {
+ eapPeerConfig->chbind_config = NULL;
+ eapPeerConfig->chbind_config_len = 0;
+ }
+
*minor = 0;
return GSS_S_COMPLETE;
}
* Mark an initiator context as ready for cryptographic operations
*/
static OM_uint32
- initReady(OM_uint32 *minor, gss_ctx_id_t ctx, OM_uint32 reqFlags)
+ initReady(OM_uint32 *minor, gss_ctx_id_t ctx)
{
OM_uint32 major;
const unsigned char *key;
size_t keyLength;
- #if 1
- /* XXX actually check for mutual auth */
- if (reqFlags & GSS_C_MUTUAL_FLAG)
- ctx->gssFlags |= GSS_C_MUTUAL_FLAG;
- #endif
-
/* Cache encryption type derived from selected mechanism OID */
major = gssEapOidToEnctype(minor, ctx->mechanismUsed, &ctx->encryptionType);
if (GSS_ERROR(major))
outputToken, NULL);
if (GSS_ERROR(major))
return major;
- } else if (inputToken != GSS_C_NO_BUFFER &&
- ctx->acceptorName == GSS_C_NO_NAME) {
- /* Accept target name hint from acceptor */
+ } else if (inputToken != GSS_C_NO_BUFFER) {
+ OM_uint32 tmpMinor;
+ gss_name_t nameHint;
+ int equal;
+
+ /* Accept target name hint from acceptor or verify acceptor */
major = gssEapImportName(minor, inputToken,
GSS_C_NT_USER_NAME,
ctx->mechanismUsed,
- &ctx->acceptorName);
+ &nameHint);
if (GSS_ERROR(major))
return major;
+
+ if (ctx->acceptorName != GSS_C_NO_NAME) {
+ /* verify name hint matched asserted acceptor name */
+ major = gssEapCompareName(minor,
+ nameHint,
+ ctx->acceptorName,
+ COMPARE_NAME_FLAG_IGNORE_EMPTY_REALMS,
+ &equal);
+ if (GSS_ERROR(major)) {
+ gssEapReleaseName(&tmpMinor, &nameHint);
+ return major;
+ }
+
+ gssEapReleaseName(&tmpMinor, &nameHint);
+
+ if (!equal) {
+ *minor = GSSEAP_WRONG_ACCEPTOR_NAME;
+ return GSS_S_DEFECTIVE_TOKEN;
+ }
+ } else { /* acceptor name is no_name */
+ /* accept acceptor name hint */
+ ctx->acceptorName = nameHint;
+ nameHint = GSS_C_NO_NAME;
+ }
}
+
/*
* Currently, other parts of the code assume that the acceptor name
* is available, hence this check.
return GSS_S_FAILURE;
}
+ /*
+ * Generate channel binding data
+ */
+ if (ctx->initiatorCtx.chbindData == NULL) {
+ major = peerInitEapChannelBinding(minor, ctx);
+ if (GSS_ERROR(major))
+ return major;
+ }
+
return GSS_S_CONTINUE_NEEDED;
}
resp = eap_get_eapRespData(ctx->initiatorCtx.eap);
} else if (ctx->flags & CTX_FLAG_EAP_SUCCESS) {
- major = initReady(minor, ctx, reqFlags);
+ major = initReady(minor, ctx);
if (GSS_ERROR(major))
goto cleanup;
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);
- GSSEAP_ASSERT(outputToken->value != NULL);
+ 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);
+
+ 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_ACCEPTOR_NAME_RESP,
ITOK_TYPE_ACCEPTOR_NAME_REQ,
- GSSEAP_STATE_INITIAL | GSSEAP_STATE_AUTHENTICATE,
+ GSSEAP_STATE_INITIAL | GSSEAP_STATE_AUTHENTICATE |
+ GSSEAP_STATE_ACCEPTOR_EXTS,
0,
eapGssSmInitAcceptorName
},
ITOK_TYPE_NONE,
ITOK_TYPE_GSS_CHANNEL_BINDINGS,
GSSEAP_STATE_INITIATOR_EXTS,
- SM_ITOK_FLAG_REQUIRED,
+ 0,
eapGssSmInitGssChannelBindings
},
{
goto cleanup;
}
}
+
if (ret_flags != NULL)
*ret_flags = ctx->gssFlags;
+
if (time_rec != NULL)
gssEapContextTime(&tmpMinor, ctx, time_rec);
#include <krb5.h>
#ifdef WIN32
-#define inline __inline
+# ifndef __cplusplus
+# define inline __inline
+# endif
#define snprintf _snprintf
#endif
#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
#define KRB_PRINC_TYPE(princ) (krb5_princ_type(NULL, (princ)))
#define KRB_PRINC_NAME(princ) (krb5_princ_name(NULL, (princ)))
#define KRB_PRINC_REALM(princ) (krb5_princ_realm(NULL, (princ)))
+ #define KRB_PRINC_COMPONENT(princ, component) \
+ (krb5_princ_component(NULL, (princ), (component)))
#define KRB_KT_ENT_KEYBLOCK(e) (&(e)->key)
#define KRB_KT_ENT_FREE(c, e) krb5_free_keytab_entry_contents((c), (e))
(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 { \
gss_buffer_t output_name_buffer,
gss_OID *output_name_type);
+#define COMPARE_NAME_FLAG_IGNORE_EMPTY_REALMS 0x1
+
OM_uint32
gssEapCompareName(OM_uint32 *minor,
gss_name_t name1,
gss_name_t name2,
+ OM_uint32 flags,
int *name_equal);
/* util_oid.c */
enum gss_eap_token_type *ret_tok_type);
/* Helper macros */
-
#ifndef GSSEAP_MALLOC
+ #if _WIN32
+ #include <gssapi/gssapi_alloc.h>
+ #define GSSEAP_MALLOC gssalloc_malloc
+ #define GSSEAP_CALLOC gssalloc_calloc
+ #define GSSEAP_FREE gssalloc_free
+ #define GSSEAP_REALLOC gssalloc_realloc
+ #else
#define GSSEAP_CALLOC calloc
#define GSSEAP_MALLOC malloc
#define GSSEAP_FREE free
#define GSSEAP_REALLOC realloc
- #endif
+ #endif /* _WIN32 */
+ #endif /* !GSSEAP_MALLOC */
#ifndef GSSAPI_CALLCONV
#define GSSAPI_CALLCONV KRB5_CALLCONV
krbPrincComponentToGssBuffer(krb5_principal krbPrinc,
int index, gss_buffer_t buffer)
{
+ if (KRB_PRINC_LENGTH(krbPrinc) < index) {
+ buffer->value = NULL;
+ buffer->length = 0;
+ } else {
#ifdef HAVE_HEIMDAL_VERSION
- buffer->value = (void *)KRB_PRINC_NAME(krbPrinc)[index];
- buffer->length = strlen((char *)buffer->value);
+ buffer->value = (void *)KRB_PRINC_NAME(krbPrinc)[index];
+ buffer->length = strlen((char *)buffer->value);
#else
- buffer->value = (void *)krb5_princ_component(NULL, krbPrinc, index)->data;
- buffer->length = krb5_princ_component(NULL, krbPrinc, index)->length;
+ buffer->value = (void *)krb5_princ_component(NULL, krbPrinc, index)->data;
+ buffer->length = krb5_princ_component(NULL, krbPrinc, index)->length;
#endif /* HAVE_HEIMDAL_VERSION */
+ }
+ }
+
+ static inline krb5_error_code
+ krbPrincUnparseServiceSpecifics(krb5_context krbContext, krb5_principal krbPrinc,
+ gss_buffer_t nameBuf)
+ {
+ krb5_error_code result = 0;
+ if (KRB_PRINC_LENGTH(krbPrinc) > 2) {
+ /* Acceptor-Service-Specific */
+ krb5_principal_data ssiPrinc = *krbPrinc;
+ char *ssi;
+
+ KRB_PRINC_LENGTH(&ssiPrinc) -= 2;
+ KRB_PRINC_NAME(&ssiPrinc) += 2;
+
+ result = krb5_unparse_name_flags(krbContext, &ssiPrinc,
+ KRB5_PRINCIPAL_UNPARSE_NO_REALM, &ssi);
+ if (result != 0)
+ return result;
+
+ nameBuf->value = ssi;
+ nameBuf->length = strlen(ssi);
+ } else {
+ nameBuf->value = NULL;
+ nameBuf->length = 0;
+ }
+
+ return result;
+ }
+
+ static inline void
+ krbFreeUnparsedName(krb5_context krbContext, gss_buffer_t nameBuf)
+ {
+ krb5_free_unparsed_name(krbContext, (char *)(nameBuf->value));
+ nameBuf->value = NULL;
+ nameBuf->length = 0;
}
static inline void
*/
#include "gssapiP_eap.h"
+ #include "util_radius.h"
+ #include "utils/radius_utils.h"
+
+ #ifdef GSSEAP_ENABLE_ACCEPTOR
-/* stuff that should be provided by libradsec/libfreeradius-radius */
-#define VENDORATTR(vendor, attr) (((vendor) << 16) | (attr))
+#define RS_MAP_ERROR(code) (ERROR_TABLE_BASE_rse + (code))
-#ifndef ATTRID
-#define ATTRID(attr) ((attr) & 0xFFFF)
-#endif
+static rs_avp *copyAvps(rs_const_avp *src);
-static gss_buffer_desc radiusUrnPrefix = {
- sizeof("urn:x-radius:") - 1,
- (void *)"urn:x-radius:"
-};
+static OM_uint32
+gssEapRadiusGetAvp(OM_uint32 *minor,
+ rs_avp *vps,
+ const gss_eap_attrid &attrid,
+ gss_buffer_t buffer,
+ int concat);
-static VALUE_PAIR *copyAvps(const VALUE_PAIR *src);
+static OM_uint32
+gssEapRadiusAddAvp(OM_uint32 *minor,
+ rs_avp **vps,
+ const gss_eap_attrid &attrid,
+ const gss_buffer_t buffer);
+
+static gss_eap_attrid
+avpToAttrId(rs_const_avp *vp)
+{
+ gss_eap_attrid attrid;
+
+ rs_avp_attrid(vp, &attrid.second, &attrid.first);
+
+ return attrid;
+}
gss_eap_radius_attr_provider::gss_eap_radius_attr_provider(void)
{
gss_eap_radius_attr_provider::~gss_eap_radius_attr_provider(void)
{
if (m_vps != NULL)
- pairfree(&m_vps);
+ rs_avp_free(&m_vps);
}
bool
radius = static_cast<const gss_eap_radius_attr_provider *>(ctx);
if (radius->m_vps != NULL)
- m_vps = copyAvps(const_cast<VALUE_PAIR *>(radius->getAvps()));
+ m_vps = copyAvps(radius->getAvps());
m_authenticated = radius->m_authenticated;
return false;
/* We assume libradsec validated this for us */
- GSSEAP_ASSERT(pairfind(m_vps, PW_MESSAGE_AUTHENTICATOR) != NULL);
+ GSSEAP_ASSERT(rs_avp_find(m_vps, PW_MESSAGE_AUTHENTICATOR, 0) != NULL);
m_authenticated = true;
}
}
}
static bool
-alreadyAddedAttributeP(std::vector <std::string> &attrs, VALUE_PAIR *vp)
+alreadyAddedAttributeP(std::vector <gss_eap_attrid> &attrs,
+ gss_eap_attrid &attrid)
{
- for (std::vector<std::string>::const_iterator a = attrs.begin();
+ for (std::vector<gss_eap_attrid>::const_iterator a = attrs.begin();
a != attrs.end();
++a) {
- if (strcmp(vp->name, (*a).c_str()) == 0)
+ if (attrid.first == (*a).first &&
+ attrid.second == (*a).second)
return true;
}
}
static bool
-isSecretAttributeP(uint16_t attrid, uint16_t vendor)
+isSecretAttributeP(const gss_eap_attrid &attrid)
{
bool bSecretAttribute = false;
- switch (vendor) {
- case VENDORPEC_MS:
- switch (attrid) {
+ switch (attrid.first) {
+ case VENDORPEC_MICROSOFT:
+ switch (attrid.second) {
case PW_MS_MPPE_SEND_KEY:
case PW_MS_MPPE_RECV_KEY:
bSecretAttribute = true;
}
static bool
-isSecretAttributeP(uint32_t attribute)
+isSecretAttributeP(rs_const_avp *vp)
{
- return isSecretAttributeP(ATTRID(attribute), VENDOR(attribute));
+ return isSecretAttributeP(avpToAttrId(vp));
}
static bool
-isInternalAttributeP(uint16_t attrid, uint16_t vendor)
+isInternalAttributeP(const gss_eap_attrid &attrid)
{
bool bInternalAttribute = false;
/* should have been filtered */
- GSSEAP_ASSERT(!isSecretAttributeP(attrid, vendor));
+ GSSEAP_ASSERT(!isSecretAttributeP(attrid));
- switch (vendor) {
+ switch (attrid.first) {
case VENDORPEC_UKERNA:
- switch (attrid) {
+ switch (attrid.second) {
+ case PW_SAML_AAA_ASSERTION:
+ bInternalAttribute = true;
+ break;
+ default:
+ break;
+ }
+ break;
+ case 0:
+ switch (attrid.second) {
case PW_GSS_ACCEPTOR_SERVICE_NAME:
case PW_GSS_ACCEPTOR_HOST_NAME:
- case PW_GSS_ACCEPTOR_SERVICE_SPECIFIC:
+ case PW_GSS_ACCEPTOR_SERVICE_SPECIFICS:
case PW_GSS_ACCEPTOR_REALM_NAME:
- case PW_SAML_AAA_ASSERTION:
bInternalAttribute = true;
break;
default:
}
static bool
-isInternalAttributeP(uint32_t attribute)
+isInternalAttributeP(rs_const_avp *vp)
{
- return isInternalAttributeP(ATTRID(attribute), VENDOR(attribute));
+ return isInternalAttributeP(avpToAttrId(vp));
}
static bool
-isFragmentedAttributeP(uint16_t attrid, uint16_t vendor)
+isFragmentedAttributeP(const gss_eap_attrid &attrid)
{
/* A bit of a hack for the PAC for now. Should be configurable. */
- return (vendor == VENDORPEC_UKERNA) &&
- !isInternalAttributeP(attrid, vendor);
-}
-
-static bool
-isFragmentedAttributeP(uint32_t attribute)
-{
- return isFragmentedAttributeP(ATTRID(attribute), VENDOR(attribute));
+ return (attrid.first == VENDORPEC_UKERNA) &&
+ !isInternalAttributeP(attrid);
}
/*
* Copy AVP list, same as paircopy except it filters out attributes
* containing keys.
*/
-static VALUE_PAIR *
-copyAvps(const VALUE_PAIR *src)
+static rs_avp *
+copyAvps(rs_const_avp *src)
{
- const VALUE_PAIR *vp;
- VALUE_PAIR *dst = NULL, **pDst = &dst;
+ rs_const_avp *vp;
+ rs_avp *dst = NULL;
- for (vp = src; vp != NULL; vp = vp->next) {
- VALUE_PAIR *vpcopy;
+ for (vp = src; vp != NULL; vp = rs_avp_next_const(vp)) {
+ rs_avp *vpcopy;
- if (isSecretAttributeP(vp->attribute))
+ if (isSecretAttributeP(vp))
continue;
- vpcopy = paircopyvp(vp);
+ vpcopy = rs_avp_dup(vp);
if (vpcopy == NULL) {
- pairfree(&dst);
+ rs_avp_free(&dst);
throw std::bad_alloc();
}
- *pDst = vpcopy;
- pDst = &vpcopy->next;
+
+ rs_avp_append(&dst, vpcopy);
}
return dst;
gss_eap_radius_attr_provider::getAttributeTypes(gss_eap_attr_enumeration_cb addAttribute,
void *data) const
{
- VALUE_PAIR *vp;
- std::vector <std::string> seen;
+ rs_avp *vp;
+ std::vector <gss_eap_attrid> seen;
- for (vp = m_vps; vp != NULL; vp = vp->next) {
- gss_buffer_desc attribute;
- char attrid[64];
+ for (vp = m_vps; vp != NULL; vp = rs_avp_next(vp)) {
+ gss_buffer_desc desc;
+ gss_eap_attrid attrid;
+ char buf[64];
/* Don't advertise attributes that are internal to the GSS-EAP mechanism */
- if (isInternalAttributeP(vp->attribute))
+ if (isInternalAttributeP(vp))
continue;
- if (alreadyAddedAttributeP(seen, vp))
+ rs_avp_attrid(vp, &attrid.second, &attrid.first);
+
+ if (alreadyAddedAttributeP(seen, attrid))
continue;
- snprintf(attrid, sizeof(attrid), "%s%d",
- (char *)radiusUrnPrefix.value, vp->attribute);
+ if (rs_attr_display_name(attrid.second, attrid.first,
+ buf, sizeof(buf), TRUE) != RSE_OK ||
+ strncmp(buf, "Attr-", 5) != 0)
+ continue;
- attribute.value = attrid;
- attribute.length = strlen(attrid);
+ desc.value = &buf[5];
+ desc.length = strlen((char *)desc.value);
- if (!addAttribute(m_manager, this, &attribute, data))
+ if (!addAttribute(m_manager, this, &desc, data))
return false;
- seen.push_back(std::string(vp->name));
+ seen.push_back(attrid);
}
return true;
}
-uint32_t
-getAttributeId(const gss_buffer_t attr)
+static bool
+getAttributeId(const gss_buffer_t desc,
+ gss_eap_attrid *attrid)
{
- OM_uint32 tmpMinor;
- gss_buffer_desc strAttr = GSS_C_EMPTY_BUFFER;
- DICT_ATTR *da;
- char *s;
- uint32_t attrid = 0;
+ char *strAttr, *s;
+ int canon, code;
- if (attr->length < radiusUrnPrefix.length ||
- memcmp(attr->value, radiusUrnPrefix.value, radiusUrnPrefix.length) != 0)
- return 0;
+ if (desc->length == 0)
+ return false;
+
+ canon = isdigit(*(char *)desc->value);
/* need to duplicate because attr may not be NUL terminated */
- duplicateBuffer(*attr, &strAttr);
- s = (char *)strAttr.value + radiusUrnPrefix.length;
+ strAttr = (char *)GSSEAP_MALLOC((canon ? 5 : 0) + desc->length + 1);
+ if (strAttr == NULL)
+ throw new std::bad_alloc();
- if (isdigit(*s)) {
- attrid = strtoul(s, NULL, 10);
- } else {
- da = dict_attrbyname(s);
- if (da != NULL)
- attrid = da->attr;
+ s = strAttr;
+
+ if (canon) {
+ memcpy(s, "Attr-", 5);
+ s += 5;
}
- gss_release_buffer(&tmpMinor, &strAttr);
+ memcpy(s, desc->value, desc->length);
+ s += desc->length;
+ *s = '\0';
- return attrid;
+ code = rs_attr_parse_name(strAttr, &attrid->second, &attrid->first);
+
+ GSSEAP_FREE(strAttr);
+
+ return (code == RSE_OK);
}
bool
gss_eap_radius_attr_provider::setAttribute(int complete GSSEAP_UNUSED,
- uint32_t attrid,
+ const gss_eap_attrid &attrid,
const gss_buffer_t value)
{
OM_uint32 major = GSS_S_UNAVAILABLE, minor;
!isInternalAttributeP(attrid)) {
deleteAttribute(attrid);
- major = gssEapRadiusAddAvp(&minor, &m_vps,
- ATTRID(attrid), VENDOR(attrid),
- value);
+ major = gssEapRadiusAddAvp(&minor, &m_vps, attrid, value);
}
return !GSS_ERROR(major);
const gss_buffer_t attr,
const gss_buffer_t value)
{
- uint32_t attrid = getAttributeId(attr);
+ gss_eap_attrid attrid;
- if (!attrid)
+ if (!getAttributeId(attr, &attrid))
return false;
return setAttribute(complete, attrid, value);
}
bool
-gss_eap_radius_attr_provider::deleteAttribute(uint32_t attrid)
+gss_eap_radius_attr_provider::deleteAttribute(const gss_eap_attrid &attrid)
{
- if (isSecretAttributeP(attrid) || isInternalAttributeP(attrid) ||
- pairfind(m_vps, attrid) == NULL)
+ if (isSecretAttributeP(attrid) ||
+ isInternalAttributeP(attrid) ||
+ rs_avp_find(m_vps, attrid.second, attrid.first) == NULL)
return false;
- pairdelete(&m_vps, attrid);
-
- return true;
+ return (rs_avp_delete(&m_vps, attrid.second, attrid.first) == RSE_OK);
}
bool
gss_eap_radius_attr_provider::deleteAttribute(const gss_buffer_t attr)
{
- uint32_t attrid = getAttributeId(attr);
+ gss_eap_attrid attrid;
- if (!attrid)
+ if (!getAttributeId(attr, &attrid))
return false;
return deleteAttribute(attrid);
gss_buffer_t display_value,
int *more) const
{
- uint32_t attrid;
+ gss_eap_attrid attrid;
- attrid = getAttributeId(attr);
- if (!attrid)
+ if (!getAttributeId(attr, &attrid))
return false;
- return getAttribute(attrid, authenticated, complete,
+ return getAttribute(attrid,
+ authenticated, complete,
value, display_value, more);
}
bool
-gss_eap_radius_attr_provider::getAttribute(uint32_t attrid,
+gss_eap_radius_attr_provider::getAttribute(const gss_eap_attrid &attrid,
int *authenticated,
int *complete,
gss_buffer_t value,
gss_buffer_t display_value,
int *more) const
{
- VALUE_PAIR *vp;
+ rs_const_avp *vp;
int i = *more, count = 0;
*more = 0;
if (i == -1)
i = 0;
- if (isSecretAttributeP(attrid) || isInternalAttributeP(attrid)) {
+ if (isSecretAttributeP(attrid) ||
+ isInternalAttributeP(attrid)) {
return false;
} else if (isFragmentedAttributeP(attrid)) {
return getFragmentedAttribute(attrid,
value);
}
- for (vp = pairfind(m_vps, attrid);
+ for (vp = rs_avp_find_const(m_vps, attrid.second, attrid.first);
vp != NULL;
- vp = pairfind(vp->next, attrid)) {
+ vp = rs_avp_find_const(rs_avp_next_const(vp), attrid.second, attrid.first)) {
if (count++ == i) {
- if (pairfind(vp->next, attrid) != NULL)
+ if (rs_avp_find_const(rs_avp_next_const(vp), attrid.second, attrid.first) != NULL)
*more = count;
break;
}
if (value != GSS_C_NO_BUFFER) {
gss_buffer_desc valueBuf;
- valueBuf.value = (void *)vp->vp_octets;
- valueBuf.length = vp->length;
+ rs_avp_octets_value_byref((rs_avp *)vp,
+ (unsigned char **)&valueBuf.value,
+ &valueBuf.length);
duplicateBuffer(valueBuf, value);
}
if (display_value != GSS_C_NO_BUFFER &&
- vp->type != PW_TYPE_OCTETS) {
- char displayString[MAX_STRING_LEN];
+ !rs_avp_is_octets(vp)) {
+ char displayString[RS_MAX_STRING_LEN];
gss_buffer_desc displayBuf;
- displayBuf.length = vp_prints_value(displayString,
- sizeof(displayString), vp, 0);
+ displayBuf.length = rs_avp_display_value(vp, displayString,
+ sizeof(displayString));
displayBuf.value = (void *)displayString;
duplicateBuffer(displayBuf, display_value);
}
bool
-gss_eap_radius_attr_provider::getFragmentedAttribute(uint16_t attribute,
- uint16_t vendor,
+gss_eap_radius_attr_provider::getFragmentedAttribute(const gss_eap_attrid &attrid,
int *authenticated,
int *complete,
gss_buffer_t value) const
{
OM_uint32 major, minor;
- major = gssEapRadiusGetAvp(&minor, m_vps, attribute, vendor, value, TRUE);
+ major = gssEapRadiusGetAvp(&minor, m_vps, attrid, value, TRUE);
if (authenticated != NULL)
*authenticated = m_authenticated;
return !GSS_ERROR(major);
}
-bool
-gss_eap_radius_attr_provider::getFragmentedAttribute(uint32_t attrid,
- int *authenticated,
- int *complete,
- gss_buffer_t value) const
-{
- return getFragmentedAttribute(ATTRID(attrid), VENDOR(attrid),
- authenticated, complete, value);
-}
-
-bool
-gss_eap_radius_attr_provider::getAttribute(uint16_t attribute,
- uint16_t vendor,
- int *authenticated,
- int *complete,
- gss_buffer_t value,
- gss_buffer_t display_value,
- int *more) const
-{
-
- return getAttribute(VENDORATTR(attribute, vendor),
- authenticated, complete,
- value, display_value, more);
-}
-
gss_any_t
gss_eap_radius_attr_provider::mapToAny(int authenticated,
gss_buffer_t type_id GSSEAP_UNUSED) const
gss_eap_radius_attr_provider::releaseAnyNameMapping(gss_buffer_t type_id GSSEAP_UNUSED,
gss_any_t input) const
{
- VALUE_PAIR *vp = (VALUE_PAIR *)input;
- pairfree(&vp);
+ rs_avp *vp = (rs_avp *)input;
+ rs_avp_free(&vp);
}
bool
return new gss_eap_radius_attr_provider;
}
-OM_uint32
+static OM_uint32
gssEapRadiusAddAvp(OM_uint32 *minor,
- VALUE_PAIR **vps,
- uint16_t attribute,
- uint16_t vendor,
+ rs_avp **vps,
+ const gss_eap_attrid &attrid,
const gss_buffer_t buffer)
{
- uint32_t attrid = VENDORATTR(vendor, attribute);
unsigned char *p = (unsigned char *)buffer->value;
size_t remain = buffer->length;
do {
- VALUE_PAIR *vp;
+ rs_avp *vp;
size_t n = remain;
/*
* There's an extra byte of padding; RADIUS AVPs can only
* be 253 octets.
*/
- if (n >= MAX_STRING_LEN)
- n = MAX_STRING_LEN - 1;
+ if (n >= RS_MAX_STRING_LEN)
+ n = RS_MAX_STRING_LEN - 1;
- vp = paircreate(attrid, PW_TYPE_OCTETS);
+ vp = rs_avp_alloc(attrid.second, attrid.first);
if (vp == NULL) {
*minor = ENOMEM;
return GSS_S_FAILURE;
}
- memcpy(vp->vp_octets, p, n);
- vp->length = n;
+ rs_avp_octets_set(vp, p, n);
- pairadd(vps, vp);
+ rs_avp_append(vps, vp);
p += n;
remain -= n;
}
OM_uint32
-gssEapRadiusGetRawAvp(OM_uint32 *minor,
- VALUE_PAIR *vps,
- uint16_t attribute,
- uint16_t vendor,
- VALUE_PAIR **vp)
+gssEapRadiusAddAvp(OM_uint32 *minor,
+ struct rs_packet *pkt,
+ unsigned int attribute,
+ unsigned int vendor,
+ const gss_buffer_t buffer)
{
- uint32_t attr = VENDORATTR(vendor, attribute);
+ gss_eap_attrid attrid(vendor, attribute);
+ int code;
+
+ code = rs_packet_append_avp(pkt, attrid.second, attrid.first,
+ buffer->value, buffer->length);
+ if (code != RSE_OK) {
+ *minor = RS_MAP_ERROR(code);
+ return GSS_S_FAILURE;
+ }
- *vp = pairfind(vps, attr);
+ *minor = 0;
+ return GSS_S_COMPLETE;
+}
+
+OM_uint32
+gssEapRadiusGetRawAvp(OM_uint32 *minor,
+ rs_const_avp *vps,
+ unsigned int attribute,
+ unsigned int vendor,
+ rs_const_avp **vp)
+{
+ *vp = rs_avp_find_const(vps, attribute, vendor);
if (*vp == NULL) {
*minor = GSSEAP_NO_SUCH_ATTR;
return GSS_S_UNAVAILABLE;
return GSS_S_COMPLETE;
}
-OM_uint32
+static OM_uint32
gssEapRadiusGetAvp(OM_uint32 *minor,
- VALUE_PAIR *vps,
- uint16_t attribute,
- uint16_t vendor,
+ rs_avp *vps,
+ const gss_eap_attrid &attrid,
gss_buffer_t buffer,
int concat)
{
- VALUE_PAIR *vp;
- unsigned char *p;
- uint32_t attr = VENDORATTR(vendor, attribute);
+ rs_const_avp *vp;
+ int err;
if (buffer != GSS_C_NO_BUFFER) {
buffer->length = 0;
buffer->value = NULL;
}
- vp = pairfind(vps, attr);
+ vp = rs_avp_find_const(vps, attrid.second, attrid.first);
if (vp == NULL) {
*minor = GSSEAP_NO_SUCH_ATTR;
return GSS_S_UNAVAILABLE;
}
if (buffer != GSS_C_NO_BUFFER) {
- do {
- buffer->length += vp->length;
- } while (concat && (vp = pairfind(vp->next, attr)) != NULL);
+ if (concat)
+ rs_avp_fragmented_value(vp, NULL, &buffer->length);
+ else
+ buffer->length = rs_avp_length(vp);
buffer->value = GSSEAP_MALLOC(buffer->length);
if (buffer->value == NULL) {
return GSS_S_FAILURE;
}
- p = (unsigned char *)buffer->value;
+ if (concat)
+ err = rs_avp_fragmented_value(vp, (unsigned char *)buffer->value, &buffer->length);
+ else
+ err = rs_avp_octets_value(vp, (unsigned char *)buffer->value, &buffer->length);
- for (vp = pairfind(vps, attr);
- concat && vp != NULL;
- vp = pairfind(vp->next, attr)) {
- memcpy(p, vp->vp_octets, vp->length);
- p += vp->length;
+ if (err != 0) {
+ *minor = RS_MAP_ERROR(err);
+ return GSS_S_FAILURE;
}
}
}
OM_uint32
+gssEapRadiusGetAvp(OM_uint32 *minor,
+ struct rs_packet *pkt,
+ unsigned int attribute,
+ unsigned int vendor,
+ gss_buffer_t buffer,
+ int concat)
+{
+ rs_avp **vps;
+ gss_eap_attrid attrid(vendor, attribute);
+
+ rs_packet_avps(pkt, &vps);
+
+ return gssEapRadiusGetAvp(minor, *vps, attrid, buffer, concat);
+}
+
+OM_uint32
gssEapRadiusFreeAvps(OM_uint32 *minor,
- VALUE_PAIR **vps)
+ rs_avp **vps)
{
- pairfree(vps);
+ rs_avp_free(vps);
*minor = 0;
return GSS_S_COMPLETE;
}
}
static JSONObject
-avpToJson(const VALUE_PAIR *vp)
+avpToJson(rs_const_avp *vp)
{
JSONObject obj;
+ gss_eap_attrid attrid;
- GSSEAP_ASSERT(vp->length <= MAX_STRING_LEN);
+ GSSEAP_ASSERT(rs_avp_length(vp) <= RS_MAX_STRING_LEN);
- switch (vp->type) {
- case PW_TYPE_INTEGER:
- case PW_TYPE_IPADDR:
- case PW_TYPE_DATE:
- obj.set("value", vp->lvalue);
+ switch (rs_avp_typeof(vp)) {
+ case RS_TYPE_INTEGER:
+ obj.set("value", rs_avp_integer_value(vp));
+ break;
+ case RS_TYPE_DATE:
+ obj.set("value", rs_avp_date_value(vp));
break;
- case PW_TYPE_STRING:
- obj.set("value", vp->vp_strvalue);
+ case RS_TYPE_STRING:
+ obj.set("value", rs_avp_string_value(vp));
break;
default: {
char *b64;
- if (base64Encode(vp->vp_octets, vp->length, &b64) < 0)
+ if (base64Encode(rs_avp_octets_value_const_ptr(vp),
+ rs_avp_length(vp), &b64) < 0)
throw std::bad_alloc();
obj.set("value", b64);
}
}
- obj.set("type", vp->attribute);
+ attrid = avpToAttrId(vp);
+
+ obj.set("type", attrid.second);
+ if (attrid.first != 0)
+ obj.set("vendor", attrid.first);
return obj;
}
static bool
-jsonToAvp(VALUE_PAIR **pVp, JSONObject &obj)
+jsonToAvp(rs_avp **pVp, JSONObject &obj)
{
- VALUE_PAIR *vp = NULL;
- DICT_ATTR *da;
- uint32_t attrid;
+ rs_avp *vp = NULL;
+ gss_eap_attrid attrid;
JSONObject type = obj["type"];
+ JSONObject vendor = obj["vendor"];
JSONObject value = obj["value"];
if (!type.isInteger())
goto fail;
+ attrid.second = type.integer();
- attrid = type.integer();
- da = dict_attrbyvalue(attrid);
- if (da != NULL) {
- vp = pairalloc(da);
+ if (!vendor.isNull()) {
+ if (!vendor.isInteger())
+ goto fail;
+ attrid.first = vendor.integer();
} else {
- int type = base64Valid(value.string()) ?
- PW_TYPE_OCTETS : PW_TYPE_STRING;
- vp = paircreate(attrid, type);
+ attrid.first = 0;
}
+
+ vp = rs_avp_alloc(attrid.second, attrid.first);
if (vp == NULL)
throw std::bad_alloc();
- switch (vp->type) {
- case PW_TYPE_INTEGER:
- case PW_TYPE_IPADDR:
- case PW_TYPE_DATE:
+ switch (rs_avp_typeof(vp)) {
+ case RS_TYPE_INTEGER:
+ case RS_TYPE_IPADDR:
+ case RS_TYPE_DATE:
if (!value.isInteger())
goto fail;
- vp->length = 4;
- vp->lvalue = value.integer();
+ if (rs_avp_integer_set(vp, value.integer()) != RSE_OK)
+ goto fail;
+
break;
- case PW_TYPE_STRING: {
+ case RS_TYPE_STRING: {
if (!value.isString())
goto fail;
- const char *str = value.string();
- size_t len = strlen(str);
-
- if (len >= MAX_STRING_LEN)
+ if (rs_avp_string_set(vp, value.string()) != RSE_OK)
goto fail;
- vp->length = len;
- memcpy(vp->vp_strvalue, str, len + 1);
break;
}
- case PW_TYPE_OCTETS:
+ case RS_TYPE_OCTETS:
default: {
+ unsigned char buf[RS_MAX_STRING_LEN];
+
if (!value.isString())
goto fail;
ssize_t len = strlen(str);
/* this optimization requires base64Decode only understand packed encoding */
- if (len >= BASE64_EXPAND(MAX_STRING_LEN))
+ if (len >= BASE64_EXPAND(RS_MAX_STRING_LEN))
goto fail;
- len = base64Decode(str, vp->vp_octets);
+ len = base64Decode(str, buf);
if (len < 0)
goto fail;
- vp->length = len;
+ if (rs_avp_octets_set(vp, buf, len) != RSE_OK)
+ goto fail;
+
break;
}
}
fail:
if (vp != NULL)
- pairbasicfree(vp);
+ rs_avp_free(&vp);
*pVp = NULL;
return false;
}
gss_eap_radius_attr_provider::initWithJsonObject(const gss_eap_attr_ctx *ctx,
JSONObject &obj)
{
- VALUE_PAIR **pNext = &m_vps;
-
if (!gss_eap_attr_provider::initWithJsonObject(ctx, obj))
return false;
for (size_t i = 0; i < nelems; i++) {
JSONObject attr = attrs[i];
- VALUE_PAIR *vp;
+ rs_avp *vp;
if (!jsonToAvp(&vp, attr))
return false;
- *pNext = vp;
- pNext = &vp->next;
+ rs_avp_append(&m_vps, vp);
}
m_authenticated = obj["authenticated"].integer() ? true : false;
const char *
gss_eap_radius_attr_provider::prefix(void) const
{
- return "urn:ietf:params:gss-eap:radius-avp";
+ return "urn:ietf:params:gss:radius-attribute";
}
JSONObject
{
JSONObject obj, attrs = JSONObject::array();
- for (VALUE_PAIR *vp = m_vps; vp != NULL; vp = vp->next) {
+ for (rs_avp *vp = m_vps; vp != NULL; vp = rs_avp_next(vp)) {
JSONObject attr = avpToJson(vp);
attrs.append(attr);
}
time_t
gss_eap_radius_attr_provider::getExpiryTime(void) const
{
- VALUE_PAIR *vp;
+ rs_const_avp *vp;
+ uint32_t value;
+
+ vp = rs_avp_find(m_vps, PW_SESSION_TIMEOUT, 0);
+ if (vp == NULL)
+ return 0;
- vp = pairfind(m_vps, PW_SESSION_TIMEOUT);
- if (vp == NULL || vp->lvalue == 0)
+ value = rs_avp_integer_value(vp);
+ if (value == 0)
return 0;
- return time(NULL) + vp->lvalue;
+ return time(NULL) + value;
}
OM_uint32
return GSS_S_COMPLETE;
}
- *minor = ERROR_TABLE_BASE_rse + code;
+ *minor = RS_MAP_ERROR(code);
gssEapSaveStatusInfo(*minor, "%s", rs_err_msg(err));
rs_err_free(err);
goto fail;
}
- if (rs_context_init_freeradius_dict(radContext, NULL) != 0) {
- err = rs_err_ctx_pop(radContext);
- goto fail;
- }
-
*pRadContext = radContext;
*minor = 0;
return major;
}
+
+ #endif /* GSSEAP_ENABLE_ACCEPTOR */
+
+ OM_uint32
+ gssEapRadiusAddAttr(OM_uint32 *minor, struct wpabuf **buf, uint16_t attr,
+ uint16_t vendor, gss_buffer_t buffer)
+ {
+ if (radius_add_tlv(buf, attr, vendor, (u8 *)buffer->value,
+ buffer->length) < 0) {
+ *minor = ENOMEM; /* could be length too long, though */
+ return GSS_S_FAILURE;
+ }
+ return GSS_S_COMPLETE;
+ }
#ifdef __cplusplus
+typedef std::pair <unsigned int, unsigned int> gss_eap_attrid;
+ #ifdef GSSEAP_ENABLE_ACCEPTOR
+
+
struct gss_eap_radius_attr_provider : gss_eap_attr_provider {
public:
gss_eap_radius_attr_provider(void);
JSONObject &obj);
JSONObject jsonRepresentation(void) const;
- bool getAttribute(uint32_t attribute,
- int *authenticated,
- int *complete,
- gss_buffer_t value,
- gss_buffer_t display_value,
- int *more) const;
- bool getAttribute(uint16_t attribute,
- uint16_t vendor,
+ bool getAttribute(const gss_eap_attrid &attrid,
int *authenticated,
int *complete,
gss_buffer_t value,
gss_buffer_t display_value,
int *more) const;
bool setAttribute(int complete,
- uint32_t attribute,
+ const gss_eap_attrid &attrid,
const gss_buffer_t value);
- bool deleteAttribute(uint32_t attribute);
+ bool deleteAttribute(const gss_eap_attrid &attrid);
- bool getFragmentedAttribute(uint16_t attribute,
- uint16_t vendor,
- int *authenticated,
- int *complete,
- gss_buffer_t value) const;
- bool getFragmentedAttribute(uint32_t attrid,
+ bool getFragmentedAttribute(const gss_eap_attrid &attrid,
int *authenticated,
int *complete,
gss_buffer_t value) const;
static gss_eap_attr_provider *createAttrContext(void);
private:
- const VALUE_PAIR *getAvps(void) const {
+ rs_const_avp *getAvps(void) const {
return m_vps;
}
- VALUE_PAIR *m_vps;
+ rs_avp *m_vps;
bool m_authenticated;
};
+ #endif /* GSSEAP_ENABLE_ACCEPTOR */
+
/* For now */
extern "C" {
#endif
+ #ifdef GSSEAP_ENABLE_ACCEPTOR
+
OM_uint32
gssEapRadiusAddAvp(OM_uint32 *minor,
- VALUE_PAIR **vp,
- uint16_t type,
- uint16_t vendor,
+ struct rs_packet *pkt,
+ unsigned int type,
+ unsigned int vendor,
const gss_buffer_t buffer);
OM_uint32
gssEapRadiusGetAvp(OM_uint32 *minor,
- VALUE_PAIR *vps,
- uint16_t type,
- uint16_t vendor,
+ struct rs_packet *pkt,
+ unsigned int type,
+ unsigned int vendor,
gss_buffer_t buffer,
int concat);
OM_uint32
gssEapRadiusGetRawAvp(OM_uint32 *minor,
- VALUE_PAIR *vps,
- uint16_t type,
- uint16_t vendor,
- VALUE_PAIR **vp);
+ rs_const_avp *vps,
+ unsigned int type,
+ unsigned int vendor,
+ rs_const_avp **vp);
OM_uint32
gssEapRadiusFreeAvps(OM_uint32 *minor,
- VALUE_PAIR **vps);
+ rs_avp **vps);
OM_uint32 gssEapRadiusAttrProviderInit(OM_uint32 *minor);
OM_uint32 gssEapRadiusAttrProviderFinalize(OM_uint32 *minor);
gss_cred_id_t cred,
struct rs_context **pRadContext);
+ #endif /* GSSEAP_ENABLE_ACCEPTOR */
+
+ /* initiator utilities that require only libeap, and not freeradius */
+ struct wpabuf;
+
+ OM_uint32
+ gssEapRadiusAddAttr(OM_uint32 *minor,
+ struct wpabuf **dst,
+ uint16_t type,
+ uint16_t vendor,
+ gss_buffer_t value);
+
/* This really needs to be a function call on Windows */
#define RS_CONFIG_FILE SYSCONFDIR "/radsec.conf"
-#define VENDORPEC_MS 311 /* RFC 2548 */
-
-#define PW_MS_MPPE_SEND_KEY 16
-#define PW_MS_MPPE_RECV_KEY 17
-
-#define VENDORPEC_UKERNA 25622
-
-#define PW_GSS_ACCEPTOR_SERVICE_NAME 128
-#define PW_GSS_ACCEPTOR_HOST_NAME 129
-#define PW_GSS_ACCEPTOR_SERVICE_SPECIFIC 130
-#define PW_GSS_ACCEPTOR_REALM_NAME 131
-#define PW_SAML_AAA_ASSERTION 132
-#define PW_MS_WINDOWS_AUTH_DATA 133
-
#ifdef __cplusplus
}
#endif