Merge remote-tracking branch 'origin/eap-tls'
authorSam Hartman <hartmans@debian.org>
Fri, 20 Sep 2013 17:04:12 +0000 (13:04 -0400)
committerSam Hartman <hartmans@debian.org>
Fri, 20 Sep 2013 17:04:12 +0000 (13:04 -0400)
The eap-tls branch includes build dependencies on openssl which we
need for the sha2 hash support in IDP certs.  The eap-tls changes are
not widely exposed, but to the extent they are present are harmless.

Conflicts:
libeap/Makefile.am
mech_eap/Makefile.am
mech_eap/gssapiP_eap.h
mech_eap/init_sec_context.c

44 files changed:
.gitignore
acinclude.m4
build-aux/compile
configure.ac
libeap/Makefile.am
libeap/src/eap_common/eap_ttls.h
libeap/src/eap_peer/eap_config.h
libeap/src/eap_peer/eap_ttls.c
libeap/src/utils/common.h
libeap/src/utils/radius_utils.c [new file with mode: 0644]
libeap/src/utils/radius_utils.h [new file with mode: 0644]
mech_eap.spec.in
mech_eap/Makefile.am
mech_eap/README
mech_eap/README.samba4
mech_eap/TODO
mech_eap/accept_sec_context.c
mech_eap/compare_name.c
mech_eap/dictionary.ukerna
mech_eap/export_sec_context.c
mech_eap/gssapiP_eap.h
mech_eap/gsseap_err.et
mech_eap/import_sec_context.c
mech_eap/init_sec_context.c
mech_eap/inquire_context.c
mech_eap/inquire_sec_context_by_oid.c
mech_eap/mech
mech_eap/pseudo_random.c
mech_eap/radsec.conf
mech_eap/util.h
mech_eap/util_adshim.c
mech_eap/util_attr.cpp
mech_eap/util_context.c
mech_eap/util_cred.c
mech_eap/util_krb.c
mech_eap/util_mech.c
mech_eap/util_moonshot.c
mech_eap/util_name.c
mech_eap/util_ordering.c
mech_eap/util_radius.cpp
mech_eap/util_radius.h
mech_eap/util_reauth.c
mech_eap/util_saml.cpp
mech_eap/util_shib.cpp

index 57a94cd..3674727 100644 (file)
@@ -12,3 +12,5 @@ build-aux
 !build-aux/compile
 mech_eap.spec
 mech_eap*tar*
+*.lo
+*#
index 401ad2a..9da57ed 100644 (file)
@@ -17,17 +17,19 @@ AC_ARG_WITH(krb5,
        [Use krb5 (in specified installation directory)]),
     [check_krb5_dir="$withval"],
     [check_krb5_dir=])
-for dir in $check_krb5_dir $prefix /usr /usr/local ; do
+for dir in $check_krb5_dir $prefix /usr/local /usr ; do
    krb5dir="$dir"
    if test -x "$dir/bin/krb5-config"; then
      found_krb5="yes";
      if test "x$target_windows" = "xyes"; then
         KRB5_CFLAGS=-I"$check_krb5_dir/include";
-        KRB5_LIBS="-L$check_krb5_dir/lib/ -lkrb5_32 -lgssapi32";
+        KRB5_LDFLAGS="-L$check_krb5_dir/lib/";
+        KRB5_LIBS="-lkrb5_32 -lgssapi32";
         COMPILE_ET="$check_krb5_dir/bin/compile_et";
        AC_MSG_RESULT([yes])
      else
         KRB5_CFLAGS=`$dir/bin/krb5-config gssapi --cflags`;
+        KRB5_LDFLAGS="-L$dir/lib";
         KRB5_LIBS=`$dir/bin/krb5-config gssapi --libs`
 AC_MSG_RESULT([yes])
         AC_PATH_PROG(COMPILE_ET, [compile_et], [compile_et], [$dir/bin$PATH_SEPARATOr])
@@ -48,6 +50,7 @@ if test x_$found_krb5 != x_yes; then
 else
        printf "Kerberos found in $krb5dir\n";
        AC_SUBST(KRB5_CFLAGS)
+        AC_SUBST(KRB5_LDFLAGS)
        AC_SUBST(KRB5_LIBS)
        AC_SUBST(COMPILE_ET)
        AC_CHECK_LIB(krb5, GSS_C_NT_COMPOSITE_EXPORT, [AC_DEFINE_UNQUOTED([HAVE_GSS_C_NT_COMPOSITE_EXPORT], 1, [Define if GSS-API library supports recent naming extensions draft])], [], "$KRB5_LIBS")
index 5360806..c0096a7 100755 (executable)
@@ -18,8 +18,7 @@ scriptversion=2009-10-06.20; # UTC
 # GNU General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 # As a special exception to the GNU General Public License, if you
 # distribute this file as part of a program that contains a
index 1049dd7..4b8bfde 100644 (file)
@@ -1,5 +1,5 @@
-AC_PREREQ([2.61])
-AC_INIT([mech_eap], [0.1], [bugs@project-moonshot.org])
+/AC_PREREQ([2.61])
+AC_INIT([mech_eap], [0.9], [bugs@project-moonshot.org])
 AC_CONFIG_MACRO_DIR([m4])
 AC_CONFIG_AUX_DIR([build-aux])
 
index 8cc9fb5..a51424e 100644 (file)
@@ -64,38 +64,38 @@ SOURCES_peer += src/eap_peer/eap_tls_common.c \
        src/eap_peer/mschapv2.h \
        src/eap_peer/tncc.h
 
-CFLAGS += -DEAP_TLS
-CFLAGS += -DEAP_PEAP
-CFLAGS += -DEAP_TTLS
-CFLAGS += -DEAP_MD5
-CFLAGS += -DEAP_MSCHAPv2
-CFLAGS += -DEAP_GTC
-CFLAGS += -DEAP_OTP
-CFLAGS += -DEAP_LEAP
-CFLAGS += -DEAP_PSK
-CFLAGS += -DEAP_PAX
-CFLAGS += -DEAP_SAKE
-CFLAGS += -DEAP_GPSK -DEAP_GPSK_SHA256
+AM_CFLAGS = -DEAP_TLS
+AM_CFLAGS += -DEAP_PEAP
+AM_CFLAGS += -DEAP_TTLS
+AM_CFLAGS += -DEAP_MD5
+AM_CFLAGS += -DEAP_MSCHAPv2
+AM_CFLAGS += -DEAP_GTC
+AM_CFLAGS += -DEAP_OTP
+AM_CFLAGS += -DEAP_LEAP
+AM_CFLAGS += -DEAP_PSK
+AM_CFLAGS += -DEAP_PAX
+AM_CFLAGS += -DEAP_SAKE
+AM_CFLAGS += -DEAP_GPSK -DEAP_GPSK_SHA256
 
-CFLAGS += -DEAP_SERVER_IDENTITY
-CFLAGS += -DEAP_SERVER_TLS
-CFLAGS += -DEAP_SERVER_PEAP
-CFLAGS += -DEAP_SERVER_TTLS
-CFLAGS += -DEAP_SERVER_MD5
-CFLAGS += -DEAP_SERVER_MSCHAPV2
-CFLAGS += -DEAP_SERVER_GTC
-CFLAGS += -DEAP_SERVER_PSK
-CFLAGS += -DEAP_SERVER_PAX
-CFLAGS += -DEAP_SERVER_SAKE
-CFLAGS += -DEAP_SERVER_GPSK -DEAP_SERVER_GPSK_SHA256
+AM_CFLAGS += -DEAP_SERVER_IDENTITY
+AM_CFLAGS += -DEAP_SERVER_TLS
+AM_CFLAGS += -DEAP_SERVER_PEAP
+AM_CFLAGS += -DEAP_SERVER_TTLS
+AM_CFLAGS += -DEAP_SERVER_MD5
+AM_CFLAGS += -DEAP_SERVER_MSCHAPV2
+AM_CFLAGS += -DEAP_SERVER_GTC
+AM_CFLAGS += -DEAP_SERVER_PSK
+AM_CFLAGS += -DEAP_SERVER_PAX
+AM_CFLAGS += -DEAP_SERVER_SAKE
+AM_CFLAGS += -DEAP_SERVER_GPSK -DEAP_SERVER_GPSK_SHA256
 
-CFLAGS += -DIEEE8021X_EAPOL
-CFLAGS += -DCONFIG_IPV6
+AM_CFLAGS += -DIEEE8021X_EAPOL
+AM_CFLAGS += -DCONFIG_IPV6
 
-CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH
-CFLAGS += -DCONFIG_INTERNAL_SHA1
-CFLAGS += -DEAP_TLS_OPENSSL
-CFLAGS += -DPKCS12_FUNCS
+AM_CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH
+AM_CFLAGS += -DCONFIG_INTERNAL_SHA1
+AM_CFLAGS += -DEAP_TLS_OPENSSL
+AM_CFLAGS += -DPKCS12_FUNCS
 
 UTILS_SRCS = src/utils/base64.c \
        src/utils/common.c \
@@ -106,6 +106,8 @@ UTILS_SRCS = src/utils/base64.c \
        src/utils/wpa_debug.c \
        src/utils/wpabuf.c \
        src/utils/os_unix.c \
+       src/utils/radius_utils.c \
+src/utils/radius_utils.h \
        src/utils/base64.h \
        src/utils/build_config.h \
        src/utils/common.h \
index 09b6083..08f371c 100644 (file)
@@ -35,6 +35,7 @@ struct ttls_avp_vendor {
        /* Data */
 };
 
+
 #define AVP_FLAGS_VENDOR 0x80
 #define AVP_FLAGS_MANDATORY 0x40
 
@@ -52,6 +53,7 @@ do { \
 #define RADIUS_ATTR_USER_PASSWORD 2
 #define RADIUS_ATTR_CHAP_PASSWORD 3
 #define RADIUS_ATTR_REPLY_MESSAGE 18
+#define RADIUS_ATTR_VENDOR_SPECIFIC 26
 #define RADIUS_ATTR_CHAP_CHALLENGE 60
 #define RADIUS_ATTR_EAP_MESSAGE 79
 
@@ -72,6 +74,9 @@ do { \
 #define EAP_TTLS_CHAP_CHALLENGE_LEN 16
 #define EAP_TTLS_CHAP_PASSWORD_LEN 16
 
+#define RADIUS_VENDOR_ID_UKERNA 25622
+#define RADIUS_ATTR_UKERNA_CHBIND 135
+
 #ifdef __cplusplus
 }
 #endif
index cb381bc..c78fba1 100644 (file)
 extern "C" {
 #endif
 
+/* http://tools.ietf.org/html/draft-ietf-emu-chbind-13#section-5.3.1 */
+#define CHBIND_CODE_REQUEST 1
+#define CHBIND_CODE_SUCCESS 2
+#define CHBIND_CODE_FAILURE 3
+/* http://tools.ietf.org/html/draft-ietf-emu-chbind-13#section-5.3. */
+#define CHBIND_NSID_RADIUS 1
+
+struct eap_peer_chbind_config
+{
+    /* namespace id for this channel binding info */
+    int nsid;
+
+    /* data to be sent in channel binding request */
+    u8 *req_data;
+
+    size_t req_data_len;
+
+    /* lower level callback invoked when response is received */
+    void (*response_cb)(void *ctx, int code, int nsid, u8 *resp_data, size_t resp_data_len);
+
+    /* context for response callback */
+    void *ctx;
+};
+
 /**
  * struct eap_peer_config - EAP peer configuration/credentials
  */
@@ -628,6 +652,16 @@ struct eap_peer_config {
         */
        int fragment_size;
 
+    /**
+     * chbind_config - eap channel binding config data
+     */
+    struct eap_peer_chbind_config *chbind_config;
+
+    /**
+     * chbind_config_len - channel binding config data count
+     */
+    size_t chbind_config_len;
+
 #define EAP_CONFIG_FLAGS_PASSWORD_NTHASH BIT(0)
        /**
         * flags - Network configuration flags (bitfield)
index 2573780..70827e4 100644 (file)
@@ -15,6 +15,7 @@
 #include "includes.h"
 
 #include "common.h"
+#include "radius/radius.h"
 #include "crypto/ms_funcs.h"
 #include "crypto/sha1.h"
 #include "crypto/tls.h"
@@ -73,7 +74,9 @@ struct eap_ttls_data {
        u8 *key_data;
 
        struct wpabuf *pending_phase2_req;
-
+       int chbind_req_sent; /* channel binding request was sent */
+       int done_butfor_cb; /*we turned METHOD_DONE into METHOD_MAY_CONT to receive cb*/
+  EapDecision cbDecision;
 #ifdef EAP_TNC
        int ready_for_tnc;
        int tnc_started;
@@ -81,6 +84,23 @@ struct eap_ttls_data {
 };
 
 
+/* draft-ietf-emu-chbind-13 section 5.3 */
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct chbind_hdr {
+       u16 len;
+       u8 nsid;
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+
+
 static void * eap_ttls_init(struct eap_sm *sm)
 {
        struct eap_ttls_data *data;
@@ -245,6 +265,48 @@ static int eap_ttls_avp_encapsulate(struct wpabuf **resp, u32 avp_code,
        return 0;
 }
 
+/* chop up resp into multiple vsa's as necessary*/
+static int eap_ttls_avp_radius_vsa_encapsulate(struct wpabuf **resp, u32 vendor,
+                                       u8 attr, int mandatory)
+{
+       struct wpabuf *msg;
+       u8 *avp, *pos, *src, *final;
+       size_t size = wpabuf_len(*resp);
+       size_t num_msgs = 1 + (size / 248);
+       size_t msg_wrapper_size = sizeof(struct ttls_avp_vendor) + 6;
+       size_t allocated_total = num_msgs * (4 + msg_wrapper_size) + size;
+
+       msg = wpabuf_alloc(allocated_total);
+       if (msg == NULL) {
+               wpabuf_free(*resp);
+               *resp = NULL;
+               return -1;
+       }
+       src = wpabuf_mhead(*resp);
+       avp = wpabuf_mhead(msg);
+       while (size > 0) {
+               int avp_size = size > 248 ? 248 : size;
+               size -= avp_size;
+               pos = eap_ttls_avp_hdr(avp, RADIUS_ATTR_VENDOR_SPECIFIC, 0, mandatory,
+                                      avp_size+6);
+               wpabuf_put(msg, pos-avp);
+               wpabuf_put_be32(msg, vendor);
+               wpabuf_put_u8(msg, (u8) attr);
+               wpabuf_put_u8(msg, (u8) avp_size+2);
+               wpabuf_put_data(msg, src, avp_size);
+               src += avp_size;
+               pos = wpabuf_mhead_u8(msg) + wpabuf_len(msg);
+               final = pos; /*keep pos so we know how much padding is added*/
+               AVP_PAD(avp, final); /*final modified*/
+               if (final > pos)
+                       wpabuf_put(msg, final-pos);
+               avp = final;
+       }
+       /* check avp-wpabuf_mhead(msg) < allocated_total */
+       wpabuf_free(*resp);
+       *resp = msg;
+       return 0;
+}
 
 #if EAP_TTLS_VERSION > 0
 static int eap_ttls_ia_permute_inner_secret(struct eap_sm *sm,
@@ -1061,6 +1123,8 @@ struct ttls_parse_avp {
        u8 *mschapv2;
        u8 *eapdata;
        size_t eap_len;
+       u8 *chbind_data;
+       size_t chbind_len;
        int mschapv2_error;
 };
 
@@ -1094,6 +1158,38 @@ static int eap_ttls_parse_attr_eap(const u8 *dpos, size_t dlen,
 }
 
 
+static int eap_ttls_parse_attr_chbind(const u8 *dpos, size_t dlen,
+                                  struct ttls_parse_avp *parse)
+{
+       wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP - Channel Binding Message");
+
+       if (parse->chbind_data == NULL) {
+               parse->chbind_data = os_malloc(dlen);
+               if (parse->chbind_data == NULL) {
+                       wpa_printf(MSG_WARNING, "EAP-TTLS: Failed to allocate "
+                                  "memory for Phase 2 channel binding data");
+                       return -1;
+               }
+               os_memcpy(parse->chbind_data, dpos, dlen);
+               parse->chbind_len = dlen;
+       } else {
+        /* TODO: can this really happen?  maybe just make this an error? */
+               u8 *newchbind = os_realloc(parse->chbind_data,
+                                          parse->chbind_len + dlen);
+               if (newchbind == NULL) {
+                       wpa_printf(MSG_WARNING, "EAP-TTLS: Failed to allocate "
+                                  "memory for Phase 2 channel binding data");
+                       return -1;
+               }
+               os_memcpy(newchbind + parse->chbind_len, dpos, dlen);
+               parse->chbind_data = newchbind;
+               parse->chbind_len += dlen;
+       }
+
+       return 0;
+}
+
+
 static int eap_ttls_parse_avp(u8 *pos, size_t left,
                              struct ttls_parse_avp *parse)
 {
@@ -1144,6 +1240,11 @@ static int eap_ttls_parse_avp(u8 *pos, size_t left,
        if (vendor_id == 0 && avp_code == RADIUS_ATTR_EAP_MESSAGE) {
                if (eap_ttls_parse_attr_eap(dpos, dlen, parse) < 0)
                        return -1;
+       } else if (vendor_id == RADIUS_VENDOR_ID_UKERNA &&
+                  avp_code == RADIUS_ATTR_UKERNA_CHBIND) {
+               /* message containing channel binding data */
+               if (eap_ttls_parse_attr_chbind(dpos, dlen, parse) < 0)
+                       return -1;
        } else if (vendor_id == 0 && avp_code == RADIUS_ATTR_REPLY_MESSAGE) {
                /* This is an optional message that can be displayed to
                 * the user. */
@@ -1265,6 +1366,100 @@ static int eap_ttls_encrypt_response(struct eap_sm *sm,
        return 0;
 }
 
+static int eap_ttls_add_chbind_request(struct eap_sm *sm,
+                                      struct eap_ttls_data *data,
+                                      struct wpabuf **resp)
+{
+       struct wpabuf *chbind_req, *res;
+       int length = 1, i;
+       struct eap_peer_config *config = eap_get_config(sm);
+
+       if (!config->chbind_config || config->chbind_config_len <= 0)
+               return -1;
+
+       for (i=0; i<config->chbind_config_len; i++) {
+               length += 3 + config->chbind_config[i].req_data_len;
+       }
+
+       chbind_req = wpabuf_alloc(length);
+       if (!chbind_req)
+               return -1;
+
+       wpabuf_put_u8(chbind_req, CHBIND_CODE_REQUEST);
+       for (i=0; i<config->chbind_config_len; i++) {
+               struct eap_peer_chbind_config *chbind_config =
+                       &config->chbind_config[i];
+               wpabuf_put_be16(chbind_req, chbind_config->req_data_len);
+               wpabuf_put_u8(chbind_req, chbind_config->nsid);
+               wpabuf_put_data(chbind_req, chbind_config->req_data,
+                               chbind_config->req_data_len);
+       }
+       if (eap_ttls_avp_radius_vsa_encapsulate(&chbind_req,
+                                        RADIUS_VENDOR_ID_UKERNA,
+                                        RADIUS_ATTR_UKERNA_CHBIND, 0) < 0)
+               return -1;
+
+       /* bleh. This will free *resp regardless of whether combined buffer
+          alloc succeeds, which is not consistent with the other error
+          condition behavior in this function */
+       *resp = wpabuf_concat(chbind_req, *resp);
+
+       return (*resp) ? 0 : -1;
+}
+
+
+static int eap_ttls_process_chbind(struct eap_sm *sm,
+                                  struct eap_ttls_data *data,
+                                  struct eap_method_ret *ret,
+                                  struct ttls_parse_avp *parse,
+                                  struct wpabuf **resp)
+{
+       size_t pos=0;
+       u8 code;
+       u16 len;
+       struct chbind_hdr *hdr;
+       struct eap_peer_config *config = eap_get_config(sm);
+
+       if (parse->chbind_data == NULL) {
+               wpa_printf(MSG_WARNING, "EAP-TTLS: No channel binding message "
+                          "in the packet - dropped");
+               return -1;
+       }
+       if (parse->chbind_len < 1 + sizeof(*hdr)) {
+               wpa_printf(MSG_WARNING, "EAP-TTLS: bad channel binding response "
+                               "frame (len=%lu, expected %lu or more) - dropped",
+                               (unsigned long) parse->chbind_len,
+                               (unsigned long) sizeof(*hdr));
+               return -1;
+       }
+       code = parse->chbind_data[pos++];
+       while (pos+sizeof(*hdr) < parse->chbind_len) {
+               hdr = (struct chbind_hdr *)(&parse->chbind_data[pos]);
+               pos += sizeof(*hdr);
+               len = be_to_host16(hdr->len);
+               if (pos + len <= parse->chbind_len) {
+                       int i;
+                       for (i=0; i<config->chbind_config_len; i++) {
+                               struct eap_peer_chbind_config *chbind_config =
+                                       &config->chbind_config[i];
+                               if (chbind_config->nsid == hdr->nsid)
+                                       chbind_config->response_cb(
+                                               chbind_config->ctx,
+                                               code, hdr->nsid,
+                                               &parse->chbind_data[pos], len);
+                       }
+               }
+               pos += len;
+       }
+       if (pos != parse->chbind_len) {
+               wpa_printf(MSG_WARNING, "EAP-TTLS: bad channel binding response "
+                               "frame (parsed len=%lu, expected %lu) - dropped",
+                               (unsigned long) pos,
+                               (unsigned long) parse->chbind_len);
+               return -1;
+       }
+       return 0;
+}
 
 static int eap_ttls_process_phase2_eap(struct eap_sm *sm,
                                       struct eap_ttls_data *data,
@@ -1438,6 +1633,19 @@ static int eap_ttls_process_decrypted(struct eap_sm *sm,
                phase2_type = EAP_TTLS_PHASE2_EAP;
 #endif /* EAP_TNC */
 
+       /* handle channel binding response here */
+       if (parse->chbind_data) {
+               /* received channel binding repsonse */
+               if (eap_ttls_process_chbind(sm, data, ret, parse, &resp) < 0)
+                       return -1;
+               if (data->done_butfor_cb) {
+                       ret->methodState = METHOD_DONE;
+                       ret->decision = data->cbDecision;
+                       data->phase2_success = 1;
+                       return 1; /*request ack*/
+               }
+       }
+
        switch (phase2_type) {
        case EAP_TTLS_PHASE2_EAP:
                if (eap_ttls_process_phase2_eap(sm, data, ret, parse, &resp) <
@@ -1477,16 +1685,32 @@ static int eap_ttls_process_decrypted(struct eap_sm *sm,
 #endif /* EAP_TNC */
        }
 
+       if (!resp && (config->pending_req_identity ||
+                       config->pending_req_password ||
+                       config->pending_req_otp ||
+                       config->pending_req_new_password)) {
+               wpabuf_free(data->pending_phase2_req);
+               data->pending_phase2_req = wpabuf_dup(in_decrypted);
+               return 0;
+       }
+
+       /* issue channel binding request when appropriate */
+       if (config->chbind_config && config->chbind_config_len > 0 &&
+               !data->chbind_req_sent) {
+               if (eap_ttls_add_chbind_request(sm, data, &resp) < 0)
+                       return -1;
+               data->chbind_req_sent = 1;
+               if (ret->methodState == METHOD_DONE) {
+                       data->done_butfor_cb = 1;
+                       data->cbDecision = ret->decision;
+                       ret->methodState = METHOD_MAY_CONT;
+               }
+       }
+
        if (resp) {
                if (eap_ttls_encrypt_response(sm, data, resp, identifier,
                                              out_data) < 0)
                        return -1;
-       } else if (config->pending_req_identity ||
-                  config->pending_req_password ||
-                  config->pending_req_otp ||
-                  config->pending_req_new_password) {
-               wpabuf_free(data->pending_phase2_req);
-               data->pending_phase2_req = wpabuf_dup(in_decrypted);
        }
 
        return 0;
index 96ff59c..2868df7 100644 (file)
@@ -76,7 +76,12 @@ static inline unsigned int bswap_32(unsigned int v)
 #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,7 +92,9 @@ typedef int socklen_t;
 #endif /* CONFIG_NATIVE_WINDOWS */
 
 #ifdef _MSC_VER
+#ifndef __cplusplus
 #define inline __inline
+#endif
 
 #undef vsnprintf
 #define vsnprintf _vsnprintf
diff --git a/libeap/src/utils/radius_utils.c b/libeap/src/utils/radius_utils.c
new file mode 100644 (file)
index 0000000..b5b8e80
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * RADIUS tlv construction and parsing utilites
+ * Copyright (c) 2012, Painless Security, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+
+#include "radius/radius.h"
+#include "radius_utils.h"
+#include "wpabuf.h"
+
+int radius_add_tlv(struct wpabuf **buf, u32 type, u32 vendor, u8 *data,
+                  size_t len)
+{
+       u8 base_type;
+       u8 total;
+       if (vendor) {
+               if (len + 6 > RADIUS_MAX_ATTR_LEN)
+                       return -1;
+               total = len + 2 + 6;
+               base_type = RADIUS_ATTR_VENDOR_SPECIFIC;
+       } else {
+               if (len > RADIUS_MAX_ATTR_LEN)
+                       return -1;
+               total = len + 2;
+               base_type = type;
+       }
+
+       /* ensure buffer has enough space */
+       if (wpabuf_resize(buf, total))
+               return -1;
+
+       /* write into buffer */
+       wpabuf_put_u8(*buf, base_type);
+       wpabuf_put_u8(*buf, total);
+       if (vendor) {
+               wpabuf_put_be32(*buf, vendor);
+               wpabuf_put_u8(*buf, (u8 )type);
+               wpabuf_put_u8(*buf, (u8 )len+2);
+       }
+       wpabuf_put_data(*buf, data, len);
+       return 0;
+}
+
+struct radius_parser_struct
+{
+       u8 *data;
+       size_t len;
+       size_t pos;
+};
+
+radius_parser radius_parser_start(void *tlvdata, size_t len)
+{
+       radius_parser parser = malloc(sizeof(struct radius_parser_struct));
+       if (parser) {
+               parser->data = (u8 *)tlvdata;
+               parser->len = len;
+               parser->pos = 0;
+       }
+       return parser;
+}
+
+void radius_parser_finish(radius_parser parser)
+{
+       free(parser);
+}
+
+int radius_parser_parse_tlv(radius_parser parser, u8 *type, u32 *vendor_id,
+                           void **value, size_t *len)
+{
+       u8 rawtype, rawlen;
+       if (!parser)
+               return -1;
+       if (parser->len < parser->pos + 3)
+               return -1;
+       rawtype = parser->data[parser->pos];
+       rawlen = parser->data[parser->pos+1];
+       if (parser->len < parser->pos + rawlen)
+               return -1;
+
+       if (rawtype == RADIUS_ATTR_VENDOR_SPECIFIC) {
+               if (rawlen < 7)
+                       return -1;
+               *vendor_id = WPA_GET_BE24(&parser->data[parser->pos + 3]);
+               *value = &parser->data[parser->pos + 6];
+               *len = rawlen - 6;
+       } else {
+               if (rawlen < 3)
+                       return -1;
+
+               *value = &parser->data[parser->pos + 2];
+               *len = rawlen - 2;
+       }
+       *type = rawtype;
+
+       parser->pos += rawlen;
+       return 0;
+}
+
+int radius_parser_parse_vendor_specific(radius_parser parser, u8 *vendor_type,
+                                       void **value, size_t *len)
+{
+       u8 rawtype, rawlen;
+       if (!parser)
+               return -1;
+       if (parser->len < parser->pos + 3)
+               return -1;
+       rawtype = parser->data[parser->pos];
+       rawlen = parser->data[parser->pos+1];
+       if (parser->len < parser->pos + rawlen)
+               return -1;
+
+       if (rawlen < 3)
+               return -1;
+
+       *value = &parser->data[parser->pos + 2];
+       *len = rawlen - 2;
+       *vendor_type = rawtype;
+
+       parser->pos += rawlen;
+       return 0;
+}
diff --git a/libeap/src/utils/radius_utils.h b/libeap/src/utils/radius_utils.h
new file mode 100644 (file)
index 0000000..b0560a4
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * RADIUS tlv construction and parsing utilites
+ * Copyright (c) 2012, Painless Security, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef RADIUS_UTILS_H
+#define RADIUS_UTILS_H
+
+struct wpabuf;
+struct radius_parser_struct;
+typedef struct radius_parser_struct *radius_parser;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Simple utility to add a single type-length-value attribute to a buffer.
+ * Currently, there is no dictionary support: 'type' and 'len' are always
+ * assumed to be octets, and data is placed directly into buf untranslated
+ * for byte order.  If vendor is zero, len should be no greater than 253
+ * otherwise, no greater than 247.
+ * returns 0 on success, -1 on failure (allocation failure or len too large)
+ */
+int radius_add_tlv(struct wpabuf **buf, u32 type, u32 vendor, u8 *data,
+                  size_t len);
+
+/*
+ * simple radius parser
+ * Could be made considerably simpler by dropping support for parsing multiple
+ * sub-attributes from a vsa.
+ */
+
+/*
+ * create parser object
+ */
+radius_parser radius_parser_start(void *tlvdata, size_t len);
+
+/*
+ * parse a single tlv;
+ * There is no dictionary support; if the tlv is a vsa (attribute 26),
+ * sub-attributes are not immediately parsed: instead, the raw data is returned
+ * in 'value'.
+ * returns 0 on success, -1 on failure (malformed buffer or end of buffer)
+ */
+int radius_parser_parse_tlv(radius_parser parser, u8 *type, u32 *vendor_id,
+                           void **value, size_t *len);
+
+/*
+ * parse a single sub-attribute of a vsa: assumes octets for
+ * vendor_type and len
+ * returns 0 on success, -1 on failure (malformed buffer or end of buffer)
+ */
+int radius_parser_parse_vendor_specific(radius_parser parser, u8 *vendor_type,
+                                       void **value, size_t *len);
+
+/*
+ * destroy parser object
+ */
+void radius_parser_finish(radius_parser parser);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* RADIUS_UTILS_H */
\ No newline at end of file
index 90ac6cf..83984c6 100644 (file)
@@ -14,8 +14,8 @@ BuildRequires:         %{_moonshot_krb5} >= 1.9.1
 BuildRequires:  moonshot-ui-devel
 BuildRequires: jansson-devel
 Requires:      moonshot-ui
-BuildRequires: libradsec-devel
-BuildRequires: shibboleth-devel >= 2.5
+BuildRequires: libradsec-devel >= 0.0.3
+BuildRequires: shibboleth-sp-devel >= 2.5
 BuildRequires: libshibresolver-devel
 
 
@@ -29,8 +29,7 @@ Project Moonshot provides federated access management.
 
 
 %build
-       export LDFLAGS='-L/usr/%{_lib}/freeradius -Wl,--rpath=/usr/%{_lib}/freeradius'
-%configure  --with-libmoonshot=%{_prefix} --with-krb5=%{_prefix} --disable-reauth
+%configure  --with-libmoonshot=%{_prefix} --with-krb5=%{_prefix} --enable-reauth LDFLAGs="${LDFLAGS} -L/opt/shibboleth/%{_lib} -Wl,--rpath=/opt/shibboleth/%{_lib}" CPPFLAGS="${CPPFLAGS} -I/opt/shibboleth/include"
 make %{?_smp_mflags}
 
 
@@ -51,7 +50,7 @@ rm -rf $RPM_BUILD_ROOT
 %{_libdir}/gss/mech_eap.so
 %exclude %{_libdir}/gss/mech_eap.la
 %{_includedir}/gssapi/*.h
-#%exclude %{_libdir}/krb5/plugins/authdata/*la
+%exclude %{_libdir}/krb5/plugins/authdata/*
 #%{_libdir}/krb5/plugins/authdata/*.so
 
 
index a652182..145188f 100644 (file)
@@ -42,7 +42,8 @@ mech_eap_la_CXXFLAGS += \
                        @TARGET_CFLAGS@ $(EAP_CFLAGS)
 mech_eap_la_LDFLAGS  = -avoid-version -module \
                        -export-symbols $(GSSEAP_EXPORTS) -no-undefined \
-                       @RADSEC_LDFLAGS@ @OPENSSL_LDFLAGS@ @TARGET_LDFLAGS@
+                       @KRB5_LDFLAGS@ @RADSEC_LDFLAGS@ @TARGET_LDFLAGS@ @OPENSSL_LDFLAGS@
+
 if TARGET_WINDOWS
 mech_eap_la_LDFLAGS += -debug
 endif
@@ -108,6 +109,7 @@ mech_eap_la_SOURCES =                       \
        util_name.c                             \
        util_oid.c                              \
        util_ordering.c                         \
+       util_radius.cpp                         \
        util_sm.c                               \
        util_tld.c                              \
        util_token.c                            \
@@ -147,8 +149,7 @@ mech_eap_la_SOURCES +=                              \
        set_name_attribute.c                    \
        util_attr.cpp                           \
        util_base64.c                           \
-       util_json.cpp                           \
-       util_radius.cpp
+       util_json.cpp
 
 if OPENSAML
 mech_eap_la_SOURCES += util_saml.cpp
index 3cb2d50..c16ef62 100644 (file)
@@ -111,7 +111,7 @@ Sample usage is given below. Substitute <user>, <pass> and <host>
 appropriately (<host> is the name of the host running the server,
 not the RADIUS server).
 
-% gss-client -port 5555 -spnego -mech "{1 3 6 1 4 1 5322 22 1 18}" \
+% gss-client -port 5555 -spnego -mech "{1 3 6 1 5 5 15 1 1 18}" \
   -user <user>@<realm> -pass <pass> <host> host@<host> \
   "Testing GSS EAP"
 % gss-server -port 5555 -export host@<host>
index d0a94d1..cf58667 100644 (file)
@@ -16,8 +16,8 @@ Shibboleth
 * Add a mapping from the PAC RADIUS attribute to urn:mspac: in the file
   /usr/local/etc/shibboleth/attribute-map.xml:
 
-  <GSSAPIAttribute name="urn:ietf:params:gss-eap:radius-avp urn:x-radius:1679163525"
-                   id="urn:mspac:" binary="true"/>
+  <GSSAPIAttribute name="urn:ietf:params:gss:radius-attribute 26.25622.133"
+   id="urn:mspac:" binary="true"/>
 
 FreeRADIUS
 ----------
@@ -46,7 +46,7 @@ on OS X it was necessary to start it with -M single to function under gdb.
 
 For the client, the GSS EAP mechanism can be specified on the command line:
 
-smbclient --password samba --mechanism 1.3.6.1.4.1.5322.22.1.18 '\\host\share'".
+smbclient --password samba --mechanism 1.3.6.1.5.5.15.1.1.18 '\\host\share'".
 
 There is no Moonshot SSPI implementation as yet, so it is not possible to test
 with a Windows client.
index 0111459..78d92c8 100644 (file)
@@ -1,3 +1,4 @@
+- draft-ietf-radext-radius-extensions
 - integration with initiator-side EAP channel bindings
 - investigate initiator-side credential locking
 - always intern OIDs so they never need to be freed
index b089bae..95e0e0d 100644 (file)
@@ -59,7 +59,7 @@ static OM_uint32
 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 */
@@ -72,9 +72,10 @@ acceptReadyEap(OM_uint32 *minor, gss_ctx_id_t ctx, gss_cred_id_t cred)
 
     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;
     }
@@ -88,15 +89,15 @@ acceptReadyEap(OM_uint32 *minor, gss_ctx_id_t ctx, gss_cred_id_t cred)
         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))
@@ -287,7 +288,7 @@ importInitiatorIdentity(OM_uint32 *minor,
 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;
@@ -303,7 +304,7 @@ setInitiatorIdentity(OM_uint32 *minor,
         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;
 
@@ -320,7 +321,7 @@ setInitiatorIdentity(OM_uint32 *minor,
 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;
@@ -349,9 +350,9 @@ setAcceptorIdentity(OM_uint32 *minor,
     /* 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;
@@ -359,47 +360,35 @@ setAcceptorIdentity(OM_uint32 *minor,
     /* 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)) {
-            krb5_free_unparsed_name(krbContext, ssi);
+       krbFreeUnparsedName(krbContext, &nameBuf);
+        if (GSS_ERROR(major))
             return major;
-        }
-        krb5_free_unparsed_name(krbContext, ssi);
     }
 
     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;
@@ -469,7 +458,7 @@ eapGssSmAcceptAuthenticate(OM_uint32 *minor,
     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 */
@@ -490,23 +479,22 @@ eapGssSmAcceptAuthenticate(OM_uint32 *minor,
         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;
@@ -529,12 +517,15 @@ eapGssSmAcceptAuthenticate(OM_uint32 *minor,
 
     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;
@@ -546,23 +537,27 @@ eapGssSmAcceptAuthenticate(OM_uint32 *minor,
         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))
@@ -639,39 +634,41 @@ eapGssSmAcceptGssChannelBindings(OM_uint32 *minor,
                                  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
@@ -682,13 +679,27 @@ eapGssSmAcceptInitiatorMIC(OM_uint32 *minor,
                            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;
@@ -807,7 +818,7 @@ static struct gss_eap_sm eapGssAcceptorSm[] = {
         ITOK_TYPE_GSS_CHANNEL_BINDINGS,
         ITOK_TYPE_NONE,
         GSSEAP_STATE_INITIATOR_EXTS,
-        SM_ITOK_FLAG_REQUIRED,
+        0,
         eapGssSmAcceptGssChannelBindings,
     },
     {
@@ -828,6 +839,13 @@ static struct gss_eap_sm eapGssAcceptorSm[] = {
 #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,
@@ -873,13 +891,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,
index edadf3e..1da8354 100644 (file)
@@ -42,5 +42,5 @@ gss_compare_name(OM_uint32 *minor,
                  gss_name_t name2,
                  int *name_equal)
 {
-    return gssEapCompareName(minor, name1, name2, name_equal);
+    return gssEapCompareName(minor, name1, name2, 0, name_equal);
 }
index 1694566..235606f 100644 (file)
@@ -9,11 +9,13 @@ VENDOR        UKERNA                          25622
 
 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      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
index e5be6d8..3b74366 100644 (file)
@@ -121,6 +121,11 @@ gssEapExportSecContext(OM_uint32 *minor,
     key.length = KRB_KEY_LENGTH(&ctx->rfc3961Key);
     key.value  = KRB_KEY_DATA(&ctx->rfc3961Key);
 
+    /*
+     * As a shortcut, we omit the mechanism OID of the initiator name because
+     * we know it will match the context mechanism. The acceptor name mech OID
+     * is always included.
+     */
     if (ctx->initiatorName != GSS_C_NO_NAME) {
         major = gssEapExportNameInternal(minor, ctx->initiatorName,
                                          &initiatorName,
@@ -132,7 +137,7 @@ gssEapExportSecContext(OM_uint32 *minor,
     if (ctx->acceptorName != GSS_C_NO_NAME) {
         major = gssEapExportNameInternal(minor, ctx->acceptorName,
                                          &acceptorName,
-                                         EXPORT_NAME_FLAG_COMPOSITE);
+                                         EXPORT_NAME_FLAG_OID | EXPORT_NAME_FLAG_COMPOSITE);
         if (GSS_ERROR(major))
             goto cleanup;
     }
index eb7e7db..8a997d5 100644 (file)
@@ -90,28 +90,11 @@ typedef const gss_OID_desc *gss_const_OID;
 #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"
@@ -182,6 +165,7 @@ struct gss_cred_id_struct
 
 #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)
 
@@ -197,6 +181,7 @@ struct gss_cred_id_struct
 #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
 
 #define CONFIG_BLOB_CLIENT_CERT             0
@@ -208,6 +193,8 @@ struct gss_eap_initiator_ctx {
     struct eap_peer_config eapPeerConfig;
     struct eap_sm *eap;
     struct wpabuf reqData;
+    struct wpabuf *chbindData;
+    unsigned int chbindReqFlags;
     struct wpa_config_blob configBlobs[CONFIG_BLOB_MAX];
 };
 
@@ -217,7 +204,7 @@ struct gss_eap_acceptor_ctx {
     struct rs_connection *radConn;
     char *radServer;
     gss_buffer_desc state;
-    VALUE_PAIR *vps;
+    rs_avp *vps;
 };
 #endif
 
@@ -266,6 +253,10 @@ struct gss_ctx_id_struct
 #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,
@@ -347,9 +338,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
@@ -386,7 +380,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 */
index d60c2c7..2f0774b 100644 (file)
@@ -70,6 +70,8 @@ error_code GSSEAP_BAD_SERVICE_NAME,             "Name is not a valid service nam
 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"
 
@@ -156,6 +158,7 @@ error_code GSSEAP_SHIB_LISTENER_FAILURE,        "Failed to communicate with loca
 # 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"
 
index 1533a16..a0ebb8c 100644 (file)
@@ -209,11 +209,12 @@ importKerberosKey(OM_uint32 *minor,
 
 static OM_uint32
 importName(OM_uint32 *minor,
+           gss_OID mech,
            unsigned char **pBuf,
            size_t *pRemain,
            gss_name_t *pName)
 {
-    OM_uint32 major;
+    OM_uint32 major, tmpMinor, flags;
     unsigned char *p = *pBuf;
     size_t remain = *pRemain;
     gss_buffer_desc tmp;
@@ -232,10 +233,21 @@ importName(OM_uint32 *minor,
 
         tmp.value = p + 4;
 
-        major = gssEapImportNameInternal(minor, &tmp, pName,
-                                         EXPORT_NAME_FLAG_COMPOSITE);
+        flags = EXPORT_NAME_FLAG_COMPOSITE;
+        if (mech == GSS_C_NO_OID)
+            flags |= EXPORT_NAME_FLAG_OID;
+
+        major = gssEapImportNameInternal(minor, &tmp, pName, flags);
         if (GSS_ERROR(major))
             return major;
+
+        if ((flags & EXPORT_NAME_FLAG_OID) == 0) {
+            major = gssEapCanonicalizeOid(minor, mech, 0, &(*pName)->mechanismUsed);
+            if (GSS_ERROR(major)) {
+                gssEapReleaseName(&tmpMinor, pName);
+                return major;
+            }
+        }
     }
 
     *pBuf    += 4 + tmp.length;
@@ -288,11 +300,12 @@ gssEapImportContext(OM_uint32 *minor,
     if (GSS_ERROR(major))
         return major;
 
-    major = importName(minor, &p, &remain, &ctx->initiatorName);
+    /* Initiator name OID matches the context mechanism, so it's not encoded */
+    major = importName(minor, ctx->mechanismUsed, &p, &remain, &ctx->initiatorName);
     if (GSS_ERROR(major))
         return major;
 
-    major = importName(minor, &p, &remain, &ctx->acceptorName);
+    major = importName(minor, GSS_C_NO_OID, &p, &remain, &ctx->acceptorName);
     if (GSS_ERROR(major))
         return major;
 
index a67d381..39929a6 100644 (file)
@@ -36,6 +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)
@@ -204,6 +207,143 @@ static struct eapol_callbacks gssEapPolicyCallbacks = {
 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
+
+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)
 {
@@ -271,6 +411,26 @@ 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;
+    }
     if (cred->flags & CRED_FLAG_CERTIFICATE) {
         /*
          * CRED_FLAG_CONFIG_BLOB is an internal flag which will be used in the
@@ -322,18 +482,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))
@@ -595,17 +749,45 @@ eapGssSmInitAcceptorName(OM_uint32 *minor,
                                   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.
@@ -615,6 +797,15 @@ eapGssSmInitAcceptorName(OM_uint32 *minor,
         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;
 }
 
@@ -715,7 +906,7 @@ eapGssSmInitAuthenticate(OM_uint32 *minor,
 
         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;
 
@@ -793,21 +984,45 @@ eapGssSmInitGssChannelBindings(OM_uint32 *minor,
                                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;
 }
 
@@ -837,7 +1052,7 @@ eapGssSmInitInitiatorMIC(OM_uint32 *minor,
 
     return GSS_S_CONTINUE_NEEDED;
 }
+
 #ifdef GSSEAP_ENABLE_REAUTH
 static OM_uint32
 eapGssSmInitReauthCreds(OM_uint32 *minor,
@@ -884,6 +1099,11 @@ eapGssSmInitAcceptorMIC(OM_uint32 *minor,
     if (GSS_ERROR(major))
         return major;
 
+    /*
+     * As a temporary measure, force mutual authentication until channel binding is
+     * more widely deployed.
+     */
+    ctx->gssFlags |= GSS_C_MUTUAL_FLAG;
     GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_ESTABLISHED);
 
     *minor = 0;
@@ -902,7 +1122,8 @@ static struct gss_eap_sm eapGssInitiatorSm[] = {
     {
         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
     },
@@ -952,7 +1173,7 @@ static struct gss_eap_sm eapGssInitiatorSm[] = {
         ITOK_TYPE_NONE,
         ITOK_TYPE_GSS_CHANNEL_BINDINGS,
         GSSEAP_STATE_INITIATOR_EXTS,
-        SM_ITOK_FLAG_REQUIRED,
+        0,
         eapGssSmInitGssChannelBindings
     },
     {
@@ -1052,8 +1273,10 @@ gssEapInitSecContext(OM_uint32 *minor,
             goto cleanup;
         }
     }
+
     if (ret_flags != NULL)
         *ret_flags = ctx->gssFlags;
+
     if (time_rec != NULL)
         gssEapContextTime(&tmpMinor, ctx, time_rec);
 
index d37818d..305145c 100644 (file)
@@ -57,15 +57,21 @@ gss_inquire_context(OM_uint32 *minor,
     GSSEAP_MUTEX_LOCK(&ctx->mutex);
 
     if (src_name != NULL) {
-        major = gssEapDuplicateName(minor, ctx->initiatorName, src_name);
-        if (GSS_ERROR(major))
-            goto cleanup;
+        if (ctx->initiatorName != GSS_C_NO_NAME) {
+            major = gssEapDuplicateName(minor, ctx->initiatorName, src_name);
+            if (GSS_ERROR(major))
+                goto cleanup;
+        } else
+            *src_name = GSS_C_NO_NAME;
     }
 
     if (targ_name != NULL) {
-        major = gssEapDuplicateName(minor, ctx->acceptorName, targ_name);
-        if (GSS_ERROR(major))
-            goto cleanup;
+        if (ctx->acceptorName != GSS_C_NO_NAME) {
+            major = gssEapDuplicateName(minor, ctx->acceptorName, targ_name);
+            if (GSS_ERROR(major))
+                goto cleanup;
+        } else
+            *targ_name = GSS_C_NO_NAME;
     }
 
     if (lifetime_rec != NULL) {
index 7435f2e..bde7e1c 100644 (file)
@@ -156,8 +156,16 @@ inquireNegoExKey(OM_uint32 *minor,
 
     keySize = KRB_KEY_LENGTH(&ctx->rfc3961Key);
 
-    major = gssEapPseudoRandom(minor, ctx, GSS_C_PRF_KEY_FULL, &salt,
-                               keySize, &key);
+    key.value = GSSEAP_MALLOC(keySize);
+    if (key.value == NULL) {
+        major = GSS_S_FAILURE;
+        *minor = ENOMEM;
+        goto cleanup;
+    }
+
+    key.length = keySize;
+
+    major = gssEapPseudoRandom(minor, ctx, GSS_C_PRF_KEY_FULL, &salt, &key);
     if (GSS_ERROR(major))
         goto cleanup;
 
index 258a43a..3d2a2a8 100644 (file)
@@ -4,5 +4,5 @@
 # Any encryption type supported by Kerberos can be defined as the
 # last element of the OID arc.
 #
-eap-aes128             1.3.6.1.4.1.5322.22.1.17        mech_eap.so
-eap-aes256             1.3.6.1.4.1.5322.22.1.18        mech_eap.so
+eap-aes128             1.3.6.1.5.5.15.1.1.17           mech_eap.so
+eap-aes256             1.3.6.1.5.5.15.1.1.18           mech_eap.so
index 61d1f2a..ad079b4 100644 (file)
@@ -64,7 +64,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)
 {
     krb5_error_code code;
@@ -74,9 +73,7 @@ gssEapPseudoRandom(OM_uint32 *minor,
     krb5_data t, ns;
     unsigned char *p;
     krb5_context krbContext;
-
-    prf_out->length = 0;
-    prf_out->value = NULL;
+    ssize_t desired_output_len = prf_out->length;
 
     *minor = 0;
 
@@ -91,13 +88,6 @@ gssEapPseudoRandom(OM_uint32 *minor,
         goto cleanup;
     }
 
-    prf_out->value = GSSEAP_MALLOC(desired_output_len);
-    if (prf_out->value == NULL) {
-        code = ENOMEM;
-        goto cleanup;
-    }
-    prf_out->length = desired_output_len;
-
     code = krb5_c_prf_length(krbContext,
                              ctx->encryptionType,
                              &prflen);
@@ -146,7 +136,7 @@ cleanup:
         GSSEAP_FREE(ns.data);
     }
 #ifdef HAVE_HEIMDAL_VERSION
-    krb5_free_data_contents(krbContext, &t);
+    krb5_data_free(&t);
 #else
     if (t.data != NULL) {
         memset(t.data, 0, t.length);
@@ -181,14 +171,25 @@ gss_pseudo_random(OM_uint32 *minor,
 
     GSSEAP_MUTEX_LOCK(&ctx->mutex);
 
-    if (CTX_IS_ESTABLISHED(ctx)) {
-        major = gssEapPseudoRandom(minor, ctx, prf_key,
-                                   prf_in, desired_output_len, prf_out);
-    } else {
+    if (!CTX_IS_ESTABLISHED(ctx)) {
         major = GSS_S_NO_CONTEXT;
         *minor = GSSEAP_CONTEXT_INCOMPLETE;
+        goto cleanup;
+    }
+
+    prf_out->value = GSSEAP_MALLOC(desired_output_len);
+    if (prf_out->value == NULL) {
+        major = GSS_S_FAILURE;
+        *minor = ENOMEM;
+        goto cleanup;
     }
 
+    prf_out->length = desired_output_len;
+
+    major = gssEapPseudoRandom(minor, ctx, prf_key,
+                               prf_in, prf_out);
+
+cleanup:
     GSSEAP_MUTEX_UNLOCK(&ctx->mutex);
 
     return major;
index 27f895a..cd3a163 100644 (file)
@@ -1,5 +1,3 @@
-dictionary = "/usr/local/etc/raddb/dictionary"
-
 realm gss-eap {
     type = "UDP"
     timeout = 5
index 7a6c094..0aa2479 100644 (file)
@@ -73,7 +73,9 @@
 #include <krb5.h>
 
 #ifdef WIN32
-#define inline __inline
+# ifndef __cplusplus
+# define inline __inline
+# endif
 #define snprintf _snprintf
 #endif
 
@@ -385,6 +387,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
@@ -397,6 +409,8 @@ gssEapDeriveRfc3961Key(OM_uint32 *minor,
 #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))
@@ -409,6 +423,16 @@ gssEapDeriveRfc3961Key(OM_uint32 *minor,
         (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 {        \
@@ -597,10 +621,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 */
@@ -780,13 +807,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
@@ -978,13 +1012,58 @@ static inline void
 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)
+{
+#ifdef HAVE_HEIMDAL_VERSION
+    krb5_xfree((char *) nameBuf->value);
+#else
+    krb5_free_unparsed_name(krbContext, (char *)(nameBuf->value));
+#endif
+    nameBuf->value = NULL;
+    nameBuf->length = 0;
 }
 
 static inline void
index 513a1a8..0dabd55 100644 (file)
@@ -46,7 +46,7 @@ struct radius_ad_context {
 };
 
 static krb5_data radius_ad_attr = {
-    KV5M_DATA, sizeof("urn:authdata-radius-avp") - 1, "urn:authdata-radius-avp" };
+    KV5M_DATA, sizeof("urn:authdata-aaa-radius") - 1, "urn:authdata-aaa-radius" };
 
 static krb5_error_code
 radius_ad_init(krb5_context kcontext GSSEAP_UNUSED,
index 3bfe785..d1c0075 100644 (file)
@@ -58,16 +58,16 @@ GSSEAP_ONCE_CALLBACK(gssEapAttrProvidersInitInternal)
     if (GSS_ERROR(major))
         goto cleanup;
 
-#ifdef HAVE_OPENSAML
-    major = gssEapSamlAttrProvidersInit(&minor);
-    if (GSS_ERROR(major))
-        goto cleanup;
-#endif
 
 #ifdef HAVE_SHIBRESOLVER
     /* Allow Shibboleth initialization failure to be non-fatal */
     gssEapLocalAttrProviderInit(&minor);
 #endif
+#ifdef HAVE_OPENSAML
+    major = gssEapSamlAttrProvidersInit(&minor);
+    if (GSS_ERROR(major))
+        goto cleanup;
+#endif
 
 cleanup:
 #ifdef GSSEAP_DEBUG
index e18edc5..57bd151 100644 (file)
@@ -243,20 +243,45 @@ gssEapMakeOrVerifyTokenMIC(OM_uint32 *minor,
                            int verifyMIC)
 {
     OM_uint32 major;
-    gss_iov_buffer_desc *iov = NULL;
     size_t i = 0, j;
     enum gss_eap_token_type tokType;
     OM_uint32 micTokType;
     unsigned char wireTokType[2];
     unsigned char *innerTokTypes = NULL, *innerTokLengths = NULL;
     const struct gss_eap_token_buffer_set *tokens;
+    ssize_t checksumIndex = -1;
+
+    krb5_keyusage usage;
+    krb5_error_code code = 0;
+    krb5_context krbContext;
+    krb5_crypto_iov *kiov = NULL;
+#ifdef HAVE_HEIMDAL_VERSION
+    krb5_crypto krbCrypto = NULL;
+    krb5_cksumtype cksumType;
+#endif
+    size_t kiovCount;
+
+    GSSEAP_KRB_INIT(&krbContext);
 
     tokens = verifyMIC ? ctx->inputTokens : ctx->outputTokens;
 
     GSSEAP_ASSERT(tokens != NULL);
 
-    iov = GSSEAP_CALLOC(2 + (3 * tokens->buffers.count) + 1, sizeof(*iov));
-    if (iov == NULL) {
+#ifdef HAVE_HEIMDAL_VERSION
+    code = krb5_crypto_init(krbContext, &ctx->rfc3961Key, ETYPE_NULL, &krbCrypto);
+    if (code != 0)
+        goto cleanup;
+#endif
+
+    kiovCount = 2 + (3 * tokens->buffers.count) + 1;
+
+    if (verifyMIC) {
+        assert(tokens->buffers.count != 0);
+        kiovCount -= 3;
+    }
+
+    kiov = GSSEAP_CALLOC(kiovCount, sizeof(*kiov));
+    if (kiov == NULL) {
         major = GSS_S_FAILURE;
         *minor = ENOMEM;
         goto cleanup;
@@ -278,76 +303,134 @@ gssEapMakeOrVerifyTokenMIC(OM_uint32 *minor,
 
     /* Mechanism OID */
     GSSEAP_ASSERT(ctx->mechanismUsed != GSS_C_NO_OID);
-    iov[i].type = GSS_IOV_BUFFER_TYPE_DATA;
-    iov[i].buffer.length = ctx->mechanismUsed->length;
-    iov[i].buffer.value = ctx->mechanismUsed->elements;
+    kiov[i].flags = KRB5_CRYPTO_TYPE_SIGN_ONLY;
+    kiov[i].data.length = ctx->mechanismUsed->length;
+    kiov[i].data.data = ctx->mechanismUsed->elements;
     i++;
 
     /* Token type */
     if (CTX_IS_INITIATOR(ctx) ^ verifyMIC) {
         tokType = TOK_TYPE_INITIATOR_CONTEXT;
         micTokType = ITOK_TYPE_INITIATOR_MIC;
+        usage = KEY_USAGE_GSSEAP_INITOKEN_MIC;
     } else {
         tokType = TOK_TYPE_ACCEPTOR_CONTEXT;
         micTokType = ITOK_TYPE_ACCEPTOR_MIC;
+        usage = KEY_USAGE_GSSEAP_ACCTOKEN_MIC;
     }
     store_uint16_be(tokType, wireTokType);
 
-    iov[i].type = GSS_IOV_BUFFER_TYPE_DATA;
-    iov[i].buffer.length = sizeof(wireTokType);
-    iov[i].buffer.value = wireTokType;
+    kiov[i].flags = KRB5_CRYPTO_TYPE_SIGN_ONLY;
+    kiov[i].data.length = sizeof(wireTokType);
+    kiov[i].data.data = (char *)wireTokType;
     i++;
 
     for (j = 0; j < tokens->buffers.count; j++) {
         if (verifyMIC &&
-            (tokens->types[j] & ITOK_TYPE_MASK) == micTokType)
-            continue; /* will use this slot for trailer */
+            (tokens->types[j] & ITOK_TYPE_MASK) == micTokType) {
+            continue;
+        }
 
-        iov[i].type = GSS_IOV_BUFFER_TYPE_DATA;
-        iov[i].buffer.length = 4;
-        iov[i].buffer.value = &innerTokTypes[j * 4];
+        kiov[i].flags = KRB5_CRYPTO_TYPE_SIGN_ONLY;
+        kiov[i].data.length = 4;
+        kiov[i].data.data = (char *)&innerTokTypes[j * 4];
         store_uint32_be(tokens->types[j] & ~(ITOK_FLAG_VERIFIED),
-                        iov[i].buffer.value);
+                        kiov[i].data.data);
         i++;
 
-        iov[i].type = GSS_IOV_BUFFER_TYPE_DATA;
-        iov[i].buffer.length = 4;
-        iov[i].buffer.value = &innerTokLengths[j * 4];
+        kiov[i].flags = KRB5_CRYPTO_TYPE_SIGN_ONLY;
+        kiov[i].data.length = 4;
+        kiov[i].data.data = (char *)&innerTokLengths[j * 4];
         store_uint32_be(tokens->buffers.elements[j].length,
-                        iov[i].buffer.value);
+                        kiov[i].data.data);
         i++;
 
-        iov[i].type = GSS_IOV_BUFFER_TYPE_DATA;
-        iov[i].buffer = tokens->buffers.elements[j];
+        kiov[i].flags = KRB5_CRYPTO_TYPE_SIGN_ONLY;
+        gssBufferToKrbData(&tokens->buffers.elements[j], &kiov[i].data);
         i++;
     }
 
+    kiov[i].flags = KRB5_CRYPTO_TYPE_CHECKSUM;
     if (verifyMIC) {
-        GSSEAP_ASSERT(tokenMIC->length >= 16);
+        gssBufferToKrbData(tokenMIC, &kiov[i].data);
+    } else {
+        size_t checksumSize;
 
-        GSSEAP_ASSERT(i < 2 + (3 * tokens->buffers.count));
+        code = krb5_c_checksum_length(krbContext, ctx->checksumType,
+                                      &checksumSize);
+        if (code != 0)
+            goto cleanup;
 
-        iov[i].type = GSS_IOV_BUFFER_TYPE_HEADER;
-        iov[i].buffer = *tokenMIC;
-        i++;
+        kiov[i].data.data = GSSEAP_MALLOC(checksumSize);
+        if (kiov[i].data.data == NULL) {
+            code = ENOMEM;
+            goto cleanup;
+        }
+        kiov[i].data.length = checksumSize;
+        checksumIndex = i;
+    }
+    i++;
+    GSSEAP_ASSERT(i == kiovCount);
 
-        major = gssEapUnwrapOrVerifyMIC(minor, ctx, NULL, NULL,
-                                        iov, i, TOK_TYPE_MIC);
+#ifdef HAVE_HEIMDAL_VERSION
+    cksumType = ctx->checksumType;
+
+    if (verifyMIC) {
+        code = krb5_verify_checksum_iov(krbContext, krbCrypto, usage,
+                                        kiov, i, &cksumType);
+    } else {
+        code = krb5_create_checksum_iov(krbContext, krbCrypto, usage,
+                                        kiov, i, &cksumType);
+    }
+#else
+    if (verifyMIC) {
+        krb5_boolean kvalid = FALSE;
+
+        code = krb5_c_verify_checksum_iov(krbContext, ctx->checksumType,
+                                          &ctx->rfc3961Key,
+                                          usage, kiov, i, &kvalid);
+        if (code == 0 && !kvalid) {
+            code = KRB5KRB_AP_ERR_BAD_INTEGRITY;
+        }
     } else {
-        iov[i++].type = GSS_IOV_BUFFER_TYPE_HEADER | GSS_IOV_BUFFER_FLAG_ALLOCATE;
-        major = gssEapWrapOrGetMIC(minor, ctx, FALSE, NULL,
-                                   iov, i, TOK_TYPE_MIC);
-        if (!GSS_ERROR(major))
-            *tokenMIC = iov[i - 1].buffer;
+        code = krb5_c_make_checksum_iov(krbContext, ctx->checksumType,
+                                        &ctx->rfc3961Key,
+                                        usage, kiov, i);
+    }
+#endif /* HAVE_HEIMDAL_VERSION */
+
+    if (code == 0 && !verifyMIC) {
+        krbDataToGssBuffer(&kiov[checksumIndex].data, tokenMIC);
+        checksumIndex = -1;
     }
 
 cleanup:
-    if (iov != NULL)
-        gssEapReleaseIov(iov, tokens->buffers.count);
+    if (checksumIndex != -1)
+        GSSEAP_FREE(kiov[checksumIndex].data.data);
+    if (kiov != NULL)
+        GSSEAP_FREE(kiov);
     if (innerTokTypes != NULL)
         GSSEAP_FREE(innerTokTypes);
     if (innerTokLengths != NULL)
         GSSEAP_FREE(innerTokLengths);
+#ifdef HAVE_HEIMDAL_VERSION
+    if (krbCrypto != NULL)
+        krb5_crypto_destroy(krbContext, krbCrypto);
+#endif
+
+    *minor = code;
+
+    switch (code) {
+    case KRB5KRB_AP_ERR_BAD_INTEGRITY:
+        major = GSS_S_BAD_SIG;
+        break;
+    case 0:
+        major = GSS_S_COMPLETE;
+        break;
+    default:
+        major = GSS_S_FAILURE;
+        break;
+    }
 
     return major;
 }
@@ -368,10 +451,5 @@ gssEapVerifyTokenMIC(OM_uint32 *minor,
                      gss_ctx_id_t ctx,
                      const gss_buffer_t tokenMIC)
 {
-    if (tokenMIC->length < 16) {
-        *minor = GSSEAP_TOK_TRUNC;
-        return GSS_S_BAD_SIG;
-    }
-
     return gssEapMakeOrVerifyTokenMIC(minor, ctx, tokenMIC, TRUE);
 }
index 5973ebe..487aa47 100644 (file)
@@ -243,13 +243,14 @@ cleanup:
 gss_OID
 gssEapPrimaryMechForCred(gss_cred_id_t cred)
 {
-    gss_OID nameMech = GSS_C_NO_OID;
+    gss_OID credMech = GSS_C_NO_OID;
 
-    if (cred->mechanisms != GSS_C_NO_OID_SET &&
+    if (cred != GSS_C_NO_CREDENTIAL &&
+        cred->mechanisms != GSS_C_NO_OID_SET &&
         cred->mechanisms->count == 1)
-        nameMech = &cred->mechanisms->elements[0];
+        credMech = &cred->mechanisms->elements[0];
 
-    return nameMech;
+    return credMech;
 }
 
 OM_uint32
@@ -735,7 +736,8 @@ staticIdentityFileResolveInitiatorCred(OM_uint32 *minor, gss_cred_id_t cred)
             isDefaultIdentity = TRUE;
         } else {
             major = gssEapCompareName(minor, cred->name,
-                                      defaultIdentityName, &isDefaultIdentity);
+                                      defaultIdentityName, 0,
+                                      &isDefaultIdentity);
             if (GSS_ERROR(major))
                 goto cleanup;
         }
index 5eaa31e..78064f3 100644 (file)
@@ -229,7 +229,7 @@ cleanup:
     if (code != 0)
         krb5_free_keyblock_contents(krbContext, &kd);
 #ifdef HAVE_HEIMDAL_VERSION
-    krb5_free_data_contents(krbContext, &t);
+    krb5_data_free(&t);
 #else
     if (t.data != NULL) {
         memset(t.data, 0, t.length);
@@ -283,11 +283,7 @@ rfc3961ChecksumTypeForKey(OM_uint32 *minor,
     if (*minor != 0)
         return GSS_S_FAILURE;
 
-#ifdef HAVE_HEIMDAL_VERSION
-    *cksumtype = cksum.cksumtype;
-#else
-    *cksumtype = cksum.checksum_type;
-#endif
+    *cksumtype = KRB_CHECKSUM_TYPE(&cksum);
 
     krb5_free_checksum_contents(krbContext, &cksum);
 #endif /* HAVE_KRB5INT_C_MANDATORY_CKSUMTYPE */
index 958e43d..8cb7e74 100644 (file)
 #include "gssapiP_eap.h"
 
 /*
- * 1.3.6.1.4.1.5322(padl)
- *      gssEap(22)
+ * Mechanism and name types are taken from 1.3.6.1.5.5(mechanisms)
+ * assigned at http://www.iana.org/assignments/smi-numbers
+ *
+ *      abfab(15)
  *       mechanisms(1)
- *        eap-aes128-cts-hmac-sha1-96(17)
- *        eap-aes256-cts-hmac-sha1-96(18)
- *       nameTypes(2)
+ *        gss-eap-v1(1)
+ *         eap-aes128-cts-hmac-sha1-96(17)
+ *         eap-aes256-cts-hmac-sha1-96(18)
+ *       nametypes(2)
+ *        GSS_EAP_NT_EAP_NAME(1)
+ *
+ * Implementation-internal OIDs are taken from 1.3.6.1.4.1.5322(padl)
+ *      gssEap(22)
  *       apiExtensions(3)
  *        inquireSecContextByOid(1)
  *        inquireCredByOid(2)
  * canonicalized exported names.
  */
 static gss_OID_desc gssEapMechOids[] = {
-    /* 1.3.6.1.4.1.5322.22.1  */
-    { 9, "\x2B\x06\x01\x04\x01\xA9\x4A\x16\x01" },
-    /* 1.3.6.1.4.1.5322.22.1.17 */
-    { 10, "\x2B\x06\x01\x04\x01\xA9\x4A\x16\x01\x11" },
-    /* 1.3.6.1.4.1.5322.22.1.18 */
-    { 10, "\x2B\x06\x01\x04\x01\xA9\x4A\x16\x01\x12" }
+    /* 1.3.6.1.5.5.15.1.1  */
+    { 8, "\x2B\x06\x01\x05\x05\x0f\x01\x01" },
+    /* 1.3.6.1.5.5.15.1.1.17  */
+    { 9, "\x2B\x06\x01\x05\x05\x0f\x01\x01\x11" },
+    /* 1.3.6.1.5.5.15.1.1.18  */
+    { 9, "\x2B\x06\x01\x05\x05\x0f\x01\x01\x12" },
 };
 
 gss_OID GSS_EAP_MECHANISM                            = &gssEapMechOids[0];
index dc0c35e..3e7d14f 100644 (file)
@@ -129,6 +129,15 @@ cleanup:
     return major;
 }
 
+static int stringEmpty(const char * s)
+{
+    if (s == NULL)
+      return 1;
+    if (strlen(s) > 0)
+       return 0;
+    return 1;
+}
+
 OM_uint32
 libMoonshotResolveInitiatorCred(OM_uint32 *minor,
                                 gss_cred_id_t cred,
@@ -148,13 +157,13 @@ libMoonshotResolveInitiatorCred(OM_uint32 *minor,
     MoonshotError *error = NULL;
 
     if (cred->name != GSS_C_NO_NAME) {
-        major = gssEapExportName(minor, cred->name, &initiator);
+        major = gssEapDisplayName(minor, cred->name, &initiator, NULL);
         if (GSS_ERROR(major))
             goto cleanup;
     }
 
     if (targetName != GSS_C_NO_NAME) {
-        major = gssEapExportName(minor, targetName, &target);
+        major = gssEapDisplayName(minor, targetName, &target, NULL);
         if (GSS_ERROR(major))
             goto cleanup;
     }
@@ -194,7 +203,7 @@ libMoonshotResolveInitiatorCred(OM_uint32 *minor,
     gss_release_buffer(&tmpMinor, &cred->subjectNameConstraint);
     gss_release_buffer(&tmpMinor, &cred->subjectAltNameConstraint);
 
-    if (serverCertificateHash != NULL) {
+    if (!stringEmpty(serverCertificateHash)) {
         size_t len = strlen(serverCertificateHash);
 
         #define HASH_PREFIX             "hash://server/sha256/"
@@ -213,13 +222,13 @@ libMoonshotResolveInitiatorCred(OM_uint32 *minor,
         ((char *)cred->caCertificate.value)[HASH_PREFIX_LEN + len] = '\0';
 
         cred->caCertificate.length = HASH_PREFIX_LEN + len;
-    } else if (caCertificate != NULL) {
+    } else if (!stringEmpty(caCertificate)) {
         makeStringBufferOrCleanup(caCertificate, &cred->caCertificate);
     }
 
-    if (subjectNameConstraint != NULL)
+    if (!stringEmpty(subjectNameConstraint))
         makeStringBufferOrCleanup(subjectNameConstraint, &cred->subjectNameConstraint);
-    if (subjectAltNameConstraint != NULL)
+    if (!stringEmpty(subjectAltNameConstraint))
         makeStringBufferOrCleanup(subjectAltNameConstraint, &cred->subjectAltNameConstraint);
 
 cleanup:
index 6045724..e60156c 100644 (file)
@@ -60,8 +60,8 @@
 #include "gssapiP_eap.h"
 
 static gss_OID_desc gssEapNtEapName = {
-    /* 1.3.6.1.4.1.5322.22.2.1  */
-    10, "\x2B\x06\x01\x04\x01\xA9\x4A\x16\x02\x01"
+    /* 1.3.6.1.5.5.15.2.1 */
+    8, "\x2B\x06\x01\x05\x05\x0f\x02\x01"
 };
 
 gss_OID GSS_EAP_NT_EAP_NAME = &gssEapNtEapName;
@@ -702,6 +702,20 @@ gssEapDuplicateName(OM_uint32 *minor,
                                   GSS_C_NO_OID, dest_name);
 }
 
+static int
+hasRealmP(gss_name_t name)
+{
+#ifdef HAVE_HEIMDAL_VERSION
+    if (KRB_PRINC_REALM(name->krbPrincipal) != NULL &&
+        KRB_PRINC_REALM(name->krbPrincipal)[0] != '\0')
+#else
+    if (KRB_PRINC_REALM(name->krbPrincipal)->length != 0)
+#endif
+        return TRUE;
+
+    return FALSE;
+}
+
 OM_uint32
 gssEapDisplayName(OM_uint32 *minor,
                   gss_name_t name,
@@ -728,12 +742,7 @@ gssEapDisplayName(OM_uint32 *minor,
      * According to draft-ietf-abfab-gss-eap-01, when the realm is
      * absent the trailing '@' is not included.
      */
-#ifdef HAVE_HEIMDAL_VERSION
-    if (KRB_PRINC_REALM(name->krbPrincipal) == NULL ||
-        KRB_PRINC_REALM(name->krbPrincipal)[0] == '\0')
-#else
-    if (KRB_PRINC_REALM(name->krbPrincipal)->length == 0)
-#endif
+    if (!hasRealmP(name))
         flags |= KRB5_PRINCIPAL_UNPARSE_NO_REALM;
 
     *minor = krb5_unparse_name_flags(krbContext, name->krbPrincipal,
@@ -743,12 +752,13 @@ gssEapDisplayName(OM_uint32 *minor,
     }
 
     major = makeStringBuffer(minor, krbName, output_name_buffer);
-    if (GSS_ERROR(major)) {
-        krb5_free_unparsed_name(krbContext, krbName);
-        return major;
-    }
-
+#ifdef HAVE_HEIMDAL_VERSION
+    krb5_xfree(krbName);
+#else
     krb5_free_unparsed_name(krbContext, krbName);
+#endif
+    if (GSS_ERROR(major))
+        return major;
 
     if (output_name_buffer->length == 0) {
         name_type = GSS_C_NT_ANONYMOUS;
@@ -768,6 +778,7 @@ OM_uint32
 gssEapCompareName(OM_uint32 *minor,
                   gss_name_t name1,
                   gss_name_t name2,
+                  OM_uint32 flags,
                   int *name_equal)
 {
     krb5_context krbContext;
@@ -780,9 +791,18 @@ gssEapCompareName(OM_uint32 *minor,
         GSSEAP_KRB_INIT(&krbContext);
 
         /* krbPrincipal is immutable, so lock not required */
-        *name_equal = krb5_principal_compare(krbContext,
-                                             name1->krbPrincipal,
-                                             name2->krbPrincipal);
+        if ((flags & COMPARE_NAME_FLAG_IGNORE_EMPTY_REALMS) &&
+            (hasRealmP(name1) == FALSE || hasRealmP(name2) == FALSE)) {
+            *name_equal = krb5_principal_compare_any_realm(krbContext,
+                                                           name1->krbPrincipal,
+                                                           name2->krbPrincipal);
+        } else {
+            *name_equal = krb5_principal_compare(krbContext,
+                                                 name1->krbPrincipal,
+                                                 name2->krbPrincipal);
+        }
+    } else {
+        *name_equal = 0;
     }
 
     return GSS_S_COMPLETE;
index 71ebfb5..bb7e4d5 100644 (file)
@@ -266,7 +266,10 @@ sequenceExternalize(OM_uint32 *minor,
         *minor = GSSEAP_WRONG_SIZE;
         return GSS_S_FAILURE;
     }
-    memcpy(*buf, vqueue, sizeof(queue));
+    if (vqueue != NULL)
+        memcpy(*buf, vqueue, sizeof(queue));
+    else
+        memset(*buf, 0, sizeof(queue));
     *buf += sizeof(queue);
     *lenremain -= sizeof(queue);
 
index 9111e20..d8ec3df 100644 (file)
  */
 
 #include "gssapiP_eap.h"
+#include "util_radius.h"
+#include "utils/radius_utils.h"
 
-/* stuff that should be provided by libradsec/libfreeradius-radius */
-#define VENDORATTR(vendor, attr)            (((vendor) << 16) | (attr))
+#ifdef GSSEAP_ENABLE_ACCEPTOR
 
-#ifndef ATTRID
-#define ATTRID(attr)                        ((attr) & 0xFFFF)
-#endif
+#define RS_MAP_ERROR(code)  (ERROR_TABLE_BASE_rse + (code))
 
-static gss_buffer_desc radiusUrnPrefix = {
-    sizeof("urn:x-radius:") - 1,
-    (void *)"urn:x-radius:"
-};
+static rs_avp *copyAvps(rs_const_avp *src);
 
-static VALUE_PAIR *copyAvps(const VALUE_PAIR *src);
+static OM_uint32
+gssEapRadiusGetAvp(OM_uint32 *minor,
+                   rs_avp *vps,
+                   const gss_eap_attrid &attrid,
+                   gss_buffer_t buffer,
+                   int concat);
+
+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)
 {
@@ -59,7 +76,7 @@ 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
@@ -74,7 +91,7 @@ gss_eap_radius_attr_provider::initWithExistingContext(const gss_eap_attr_ctx *ma
     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;
 
@@ -96,7 +113,7 @@ gss_eap_radius_attr_provider::initWithGssContext(const gss_eap_attr_ctx *manager
                 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;
         }
     }
@@ -105,12 +122,14 @@ gss_eap_radius_attr_provider::initWithGssContext(const gss_eap_attr_ctx *manager
 }
 
 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;
     }
 
@@ -118,13 +137,13 @@ alreadyAddedAttributeP(std::vector <std::string> &attrs, VALUE_PAIR *vp)
 }
 
 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;
@@ -140,27 +159,35 @@ isSecretAttributeP(uint16_t attrid, uint16_t vendor)
 }
 
 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:
@@ -175,48 +202,42 @@ isInternalAttributeP(uint16_t attrid, uint16_t vendor)
 }
 
 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;
@@ -226,68 +247,78 @@ bool
 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;
@@ -296,9 +327,7 @@ gss_eap_radius_attr_provider::setAttribute(int complete GSSEAP_UNUSED,
         !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);
@@ -309,32 +338,31 @@ gss_eap_radius_attr_provider::setAttribute(int complete,
                                            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);
@@ -348,25 +376,25 @@ gss_eap_radius_attr_provider::getAttribute(const gss_buffer_t attr,
                                            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;
@@ -374,7 +402,8 @@ gss_eap_radius_attr_provider::getAttribute(uint32_t attrid,
     if (i == -1)
         i = 0;
 
-    if (isSecretAttributeP(attrid) || isInternalAttributeP(attrid)) {
+    if (isSecretAttributeP(attrid) ||
+        isInternalAttributeP(attrid)) {
         return false;
     } else if (isFragmentedAttributeP(attrid)) {
         return getFragmentedAttribute(attrid,
@@ -383,11 +412,11 @@ gss_eap_radius_attr_provider::getAttribute(uint32_t 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;
         }
@@ -399,19 +428,20 @@ gss_eap_radius_attr_provider::getAttribute(uint32_t attrid,
     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);
@@ -426,15 +456,14 @@ gss_eap_radius_attr_provider::getAttribute(uint32_t attrid,
 }
 
 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;
@@ -444,31 +473,6 @@ gss_eap_radius_attr_provider::getFragmentedAttribute(uint16_t attribute,
     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 @@ void
 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,38 +511,35 @@ gss_eap_radius_attr_provider::createAttrContext(void)
     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;
@@ -548,15 +549,34 @@ gssEapRadiusAddAvp(OM_uint32 *minor,
 }
 
 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;
@@ -565,33 +585,32 @@ gssEapRadiusGetRawAvp(OM_uint32 *minor,
     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) {
@@ -599,13 +618,14 @@ gssEapRadiusGetAvp(OM_uint32 *minor,
             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;
         }
     }
 
@@ -614,10 +634,26 @@ gssEapRadiusGetAvp(OM_uint32 *minor,
 }
 
 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;
 }
@@ -643,25 +679,28 @@ gssEapRadiusAttrProviderFinalize(OM_uint32 *minor)
 }
 
 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 PW_TYPE_STRING:
-        obj.set("value", vp->vp_strvalue);
+    case RS_TYPE_DATE:
+        obj.set("value", rs_avp_date_value(vp));
+        break;
+    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);
@@ -670,62 +709,65 @@ avpToJson(const VALUE_PAIR *vp)
     }
     }
 
-    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;
 
@@ -733,14 +775,16 @@ jsonToAvp(VALUE_PAIR **pVp, JSONObject &obj)
         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;
     }
     }
@@ -751,7 +795,7 @@ jsonToAvp(VALUE_PAIR **pVp, JSONObject &obj)
 
 fail:
     if (vp != NULL)
-        pairbasicfree(vp);
+        rs_avp_free(&vp);
     *pVp = NULL;
     return false;
 }
@@ -766,8 +810,6 @@ bool
 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;
 
@@ -776,13 +818,12 @@ gss_eap_radius_attr_provider::initWithJsonObject(const gss_eap_attr_ctx *ctx,
 
     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;
@@ -793,7 +834,7 @@ gss_eap_radius_attr_provider::initWithJsonObject(const gss_eap_attr_ctx *ctx,
 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
@@ -801,7 +842,7 @@ gss_eap_radius_attr_provider::jsonRepresentation(void) const
 {
     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);
     }
@@ -816,13 +857,18 @@ gss_eap_radius_attr_provider::jsonRepresentation(void) const
 time_t
 gss_eap_radius_attr_provider::getExpiryTime(void) const
 {
-    VALUE_PAIR *vp;
+    rs_const_avp *vp;
+    uint32_t value;
 
-    vp = pairfind(m_vps, PW_SESSION_TIMEOUT);
-    if (vp == NULL || vp->lvalue == 0)
+    vp = rs_avp_find(m_vps, PW_SESSION_TIMEOUT, 0);
+    if (vp == NULL)
         return 0;
 
-    return time(NULL) + vp->lvalue;
+    value = rs_avp_integer_value(vp);
+    if (value == 0)
+        return 0;
+
+    return time(NULL) + value;
 }
 
 OM_uint32
@@ -840,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);
@@ -881,11 +927,6 @@ gssEapCreateRadiusContext(OM_uint32 *minor,
         goto fail;
     }
 
-    if (rs_context_init_freeradius_dict(radContext, NULL) != 0) {
-        err = rs_err_ctx_pop(radContext);
-        goto fail;
-    }
-
     *pRadContext = radContext;
 
     *minor = 0;
@@ -897,3 +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;
+}
index 481876a..d4f86ec 100644 (file)
 
 #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);
@@ -72,30 +76,18 @@ public:
                            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;
@@ -110,42 +102,46 @@ public:
     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);
@@ -159,22 +155,20 @@ gssEapCreateRadiusContext(OM_uint32 *minor,
                           gss_cred_id_t cred,
                           struct rs_context **pRadContext);
 
-/* This really needs to be a function call on Windows */
-#define RS_CONFIG_FILE      SYSCONFDIR "/radsec.conf"
+#endif /* GSSEAP_ENABLE_ACCEPTOR */
 
-#define VENDORPEC_MS                        311 /* RFC 2548 */
+/* initiator utilities that require only libeap, and not freeradius */
+struct wpabuf;
 
-#define PW_MS_MPPE_SEND_KEY                 16
-#define PW_MS_MPPE_RECV_KEY                 17
-
-#define VENDORPEC_UKERNA                    25622
+OM_uint32
+gssEapRadiusAddAttr(OM_uint32 *minor,
+                    struct wpabuf **dst,
+                    uint16_t type,
+                    uint16_t vendor,
+                    gss_buffer_t value);
 
-#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
+/* This really needs to be a function call on Windows */
+#define RS_CONFIG_FILE      SYSCONFDIR "/radsec.conf"
 
 #ifdef __cplusplus
 }
index 50011ca..40e3de1 100644 (file)
@@ -214,6 +214,11 @@ gssEapMakeReauthCreds(OM_uint32 *minor,
     credBuf->length = 0;
     credBuf->value = NULL;
 
+    if (ctx->acceptorName == GSS_C_NO_NAME) {
+        *minor = GSSEAP_NO_ACCEPTOR_NAME;
+        return GSS_S_UNAVAILABLE;
+    }
+
     GSSEAP_KRB_INIT(&krbContext);
 
     code = getAcceptorKey(krbContext, ctx, cred, &server, &acceptorKey);
@@ -635,7 +640,7 @@ cleanup:
 
 #ifndef HAVE_HEIMDAL_VERSION
 static gss_buffer_desc radiusAvpKrbAttr = {
-    sizeof("urn:authdata-radius-avp") - 1, "urn:authdata-radius-avp"
+    sizeof("urn:authdata-aaa-radius") - 1, "urn:authdata-aaa-radius"
 };
 #endif
 
index ce7582e..bf82a57 100644 (file)
@@ -103,6 +103,7 @@ gss_eap_saml_assertion_provider::initWithGssContext(const gss_eap_attr_ctx *mana
     gss_buffer_desc value = GSS_C_EMPTY_BUFFER;
     int authenticated, complete;
     OM_uint32 minor;
+    gss_eap_attrid attrid(VENDORPEC_UKERNA, PW_SAML_AAA_ASSERTION);
 
     GSSEAP_ASSERT(m_assertion == NULL);
 
@@ -115,9 +116,7 @@ gss_eap_saml_assertion_provider::initWithGssContext(const gss_eap_attr_ctx *mana
     radius = static_cast<const gss_eap_radius_attr_provider *>
         (m_manager->getProvider(ATTR_TYPE_RADIUS));
     if (radius != NULL &&
-        radius->getFragmentedAttribute(PW_SAML_AAA_ASSERTION,
-                                       VENDORPEC_UKERNA,
-                                       &authenticated, &complete, &value)) {
+        radius->getFragmentedAttribute(attrid, &authenticated, &complete, &value)) {
         setAssertion(&value, authenticated);
         gss_release_buffer(&minor, &value);
     } else {
@@ -317,7 +316,7 @@ gss_eap_saml_assertion_provider::releaseAnyNameMapping(gss_buffer_t type_id GSSE
 const char *
 gss_eap_saml_assertion_provider::prefix(void) const
 {
-    return "urn:ietf:params:gss-eap:saml-aaa-assertion";
+    return "urn:ietf:params:gss:federated-saml-assertion";
 }
 
 bool
@@ -414,7 +413,7 @@ gss_eap_saml_attr_provider::getAttributeTypes(gss_eap_attr_enumeration_cb addAtt
      *   Each attribute carried in the assertion SHOULD also be a GSS name
      *   attribute.  The name of this attribute has three parts, all separated
      *   by an ASCII space character.  The first part is
-     *   urn:ietf:params:gss-eap:saml-attr.  The second part is the URI for
+     *   urn:ietf:params:gss:federated-saml-attribute.  The second part is the URI for
      *   the SAML attribute name format.  The final part is the name of the
      *   SAML attribute.  If the mechanism performs an additional attribute
      *   query, the retrieved attributes SHOULD be GSS-API name attributes
@@ -730,7 +729,7 @@ gss_eap_saml_attr_provider::releaseAnyNameMapping(gss_buffer_t type_id GSSEAP_UN
 const char *
 gss_eap_saml_attr_provider::prefix(void) const
 {
-    return "urn:ietf:params:gss-eap:saml-attr";
+    return "urn:ietf:params:gss:federated-saml-attribute";
 }
 
 bool
index f8c702b..7b62484 100644 (file)
@@ -163,13 +163,12 @@ gss_eap_shib_attr_provider::initWithGssContext(const gss_eap_attr_ctx *manager,
     const gss_eap_radius_attr_provider *radius;
     int authenticated, complete;
     gss_buffer_desc value = GSS_C_EMPTY_BUFFER;
+    gss_eap_attrid attrid(VENDORPEC_UKERNA, PW_SAML_AAA_ASSERTION);
 
     radius = static_cast<const gss_eap_radius_attr_provider *>
         (m_manager->getProvider(ATTR_TYPE_RADIUS));
     if (radius != NULL &&
-        radius->getFragmentedAttribute(PW_SAML_AAA_ASSERTION,
-                                       VENDORPEC_UKERNA,
-                                       &authenticated, &complete, &value)) {
+        radius->getFragmentedAttribute(attrid, &authenticated, &complete, &value)) {
         string str((char *)value.value, value.length);
         istringstream istream(str);
         DOMDocument *doc = XMLToolingConfig::getConfig().getParser().parse(istream);