EAP Channel binding support
authorSam Hartman <hartmans@debian.org>
Fri, 13 Sep 2013 19:41:19 +0000 (15:41 -0400)
committerSam Hartman <hartmans@debian.org>
Fri, 13 Sep 2013 19:41:19 +0000 (15:41 -0400)
Merge remote-tracking branch 'origin/eap-chbind'

Conflicts:
mech_eap/accept_sec_context.c
mech_eap/dictionary.ukerna
mech_eap/gsseap_err.et
mech_eap/util_radius.h

1  2 
libeap/src/utils/common.h
mech_eap/accept_sec_context.c
mech_eap/dictionary.ukerna
mech_eap/gssapiP_eap.h
mech_eap/gsseap_err.et
mech_eap/init_sec_context.c
mech_eap/util.h
mech_eap/util_radius.cpp
mech_eap/util_radius.h

@@@ -76,7 -76,12 +76,12 @@@ static inline unsigned int bswap_32(uns
  #endif /* __SYMBIAN32__ */
  
  #ifdef CONFIG_NATIVE_WINDOWS
+ #ifdef CONFIG_IPV6
+ #include <winsock2.h>
+ #include <ws2tcpip.h>
+ #else
  #include <winsock.h>
+ #endif
  
  typedef int socklen_t;
  
@@@ -87,9 -92,7 +92,9 @@@
  #endif /* CONFIG_NATIVE_WINDOWS */
  
  #ifdef _MSC_VER
 +#ifndef __cplusplus
  #define inline __inline
 +#endif
  
  #undef vsnprintf
  #define vsnprintf _vsnprintf
@@@ -59,7 -59,7 +59,7 @@@ static OM_uint3
  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))
@@@ -288,7 -287,7 +288,7 @@@ importInitiatorIdentity(OM_uint32 *mino
  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;
@@@ -471,7 -460,7 +462,7 @@@ eapGssSmAcceptAuthenticate(OM_uint32 *m
      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))
@@@ -647,41 -630,39 +638,41 @@@ eapGssSmAcceptGssChannelBindings(OM_uin
                                   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
@@@ -692,27 -673,13 +683,27 @@@ eapGssSmAcceptInitiatorMIC(OM_uint32 *m
                             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;
@@@ -831,7 -798,7 +822,7 @@@ static struct gss_eap_sm eapGssAcceptor
          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,
@@@ -904,11 -864,13 +895,11 @@@ gssEapAcceptSecContext(OM_uint32 *minor
       * 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,
@@@ -9,12 -9,13 +9,13 @@@ VENDOR        UKERNA                          2562
  
  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
diff --combined mech_eap/gssapiP_eap.h
@@@ -90,11 -90,28 +90,11 @@@ typedef const gss_OID_desc *gss_const_O
  #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"
@@@ -161,7 -178,6 +161,7 @@@ struct gss_cred_id_struc
  
  #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
@@@ -192,7 -211,7 +195,7 @@@ struct gss_eap_acceptor_ctx 
      struct rs_connection *radConn;
      char *radServer;
      gss_buffer_desc state;
 -    VALUE_PAIR *vps;
 +    rs_avp *vps;
  };
  #endif
  
@@@ -241,10 -260,6 +244,10 @@@ struct gss_ctx_id_struc
  #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,
@@@ -326,12 -341,9 +329,12 @@@ gssEapDisplayStatus(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
@@@ -368,6 -380,7 +371,6 @@@ gssEapPseudoRandom(OM_uint32 *minor
                     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 */
diff --combined mech_eap/gsseap_err.et
@@@ -70,7 -70,7 +70,8 @@@ error_code GSSEAP_BAD_SERVICE_NAME
  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"
  
@@@ -157,7 -157,6 +158,7 @@@ error_code GSSEAP_SHIB_LISTENER_FAILURE
  # 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"
  
@@@ -36,6 -36,9 +36,9 @@@
   */
  
  #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)
@@@ -194,6 -197,145 +197,145 @@@ static struct eapol_callbacks gssEapPol
  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;
  }
@@@ -288,18 -451,12 +451,12 @@@ peerConfigFree(OM_uint32 *minor
   * 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))
@@@ -561,45 -718,17 +718,45 @@@ eapGssSmInitAcceptorName(OM_uint32 *min
                                    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;
  }
  
@@@ -709,7 -847,7 +875,7 @@@ eapGssSmInitAuthenticate(OM_uint32 *min
  
          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;
  
@@@ -787,45 -925,21 +953,45 @@@ eapGssSmInitGssChannelBindings(OM_uint3
                                 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;
  }
  
@@@ -855,7 -969,7 +1021,7 @@@ eapGssSmInitInitiatorMIC(OM_uint32 *min
  
      return GSS_S_CONTINUE_NEEDED;
  }
 - 
 +
  #ifdef GSSEAP_ENABLE_REAUTH
  static OM_uint32
  eapGssSmInitReauthCreds(OM_uint32 *minor,
@@@ -920,8 -1034,7 +1086,8 @@@ static struct gss_eap_sm eapGssInitiato
      {
          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
      },
      {
@@@ -1071,8 -1184,10 +1237,10 @@@ gssEapInitSecContext(OM_uint32 *minor
              goto cleanup;
          }
      }
      if (ret_flags != NULL)
          *ret_flags = ctx->gssFlags;
      if (time_rec != NULL)
          gssEapContextTime(&tmpMinor, ctx, time_rec);
  
diff --combined mech_eap/util.h
@@@ -73,9 -73,7 +73,9 @@@
  #include <krb5.h>
  
  #ifdef WIN32
 -#define inline __inline
 +# ifndef __cplusplus
 +# define inline __inline
 +# endif
  #define snprintf _snprintf
  #endif
  
@@@ -381,16 -379,6 +381,16 @@@ gssEapDeriveRfc3961Key(OM_uint32 *minor
  
  #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 {        \
@@@ -613,13 -593,10 +615,13 @@@ gssEapDisplayName(OM_uint32 *minor
                    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 */
@@@ -799,13 -776,20 +801,20 @@@ verifyTokenHeader(OM_uint32 *minor
                    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
@@@ -997,13 -981,54 +1006,54 @@@ static inline voi
  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
diff --combined mech_eap/util_radius.cpp
   */
  
  #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)
  {
@@@ -72,7 -63,7 +76,7 @@@
  gss_eap_radius_attr_provider::~gss_eap_radius_attr_provider(void)
  {
      if (m_vps != NULL)
 -        pairfree(&m_vps);
 +        rs_avp_free(&m_vps);
  }
  
  bool
@@@ -87,7 -78,7 +91,7 @@@ gss_eap_radius_attr_provider::initWithE
      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;
  
@@@ -109,7 -100,7 +113,7 @@@ gss_eap_radius_attr_provider::initWithG
                  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;
@@@ -243,78 -230,68 +247,78 @@@ boo
  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);
@@@ -334,31 -313,32 +338,31 @@@ gss_eap_radius_attr_provider::setAttrib
                                             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);
@@@ -372,25 -352,25 +376,25 @@@ gss_eap_radius_attr_provider::getAttrib
                                             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
@@@ -483,8 -487,8 +487,8 @@@ voi
  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
@@@ -507,35 -511,38 +511,35 @@@ gss_eap_radius_attr_provider::createAtt
      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;
  }
@@@ -675,28 -647,25 +679,28 @@@ gssEapRadiusAttrProviderFinalize(OM_uin
  }
  
  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;
  }
@@@ -806,6 -770,8 +810,6 @@@ boo
  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
@@@ -838,7 -805,7 +842,7 @@@ gss_eap_radius_attr_provider::jsonRepre
  {
      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
@@@ -882,7 -844,7 +886,7 @@@ gssEapRadiusMapError(OM_uint32 *minor
          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);
@@@ -923,6 -885,11 +927,6 @@@ gssEapCreateRadiusContext(OM_uint32 *mi
          goto fail;
      }
  
 -    if (rs_context_init_freeradius_dict(radContext, NULL) != 0) {
 -        err = rs_err_ctx_pop(radContext);
 -        goto fail;
 -    }
 -
      *pRadContext = radContext;
  
      *minor = 0;
@@@ -934,3 -901,17 +938,17 @@@ fail
  
      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;
+ }
diff --combined mech_eap/util_radius.h
  
  #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);
@@@ -149,9 -165,35 +155,21 @@@ gssEapCreateRadiusContext(OM_uint32 *mi
                            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