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

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

16 files changed:
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/Makefile.am
mech_eap/accept_sec_context.c
mech_eap/dictionary.ukerna
mech_eap/gssapiP_eap.h
mech_eap/gsseap_err.et
mech_eap/init_sec_context.c
mech_eap/util.h
mech_eap/util_radius.cpp
mech_eap/util_radius.h

index 163e4ff..7b235c9 100644 (file)
@@ -64,37 +64,37 @@ 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
-
-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
-
-CFLAGS += -DIEEE8021X_EAPOL
-CFLAGS += -DCONFIG_IPV6
-
-CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH
-CFLAGS += -DCONFIG_CRYPTO_INTERNAL
-CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT
+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
+
+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
+
+AM_CFLAGS += -DIEEE8021X_EAPOL
+AM_CFLAGS += -DCONFIG_IPV6
+
+AM_CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH
+AM_CFLAGS += -DCONFIG_CRYPTO_INTERNAL
+AM_CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT
 
 UTILS_SRCS = src/utils/base64.c \
        src/utils/common.c \
@@ -105,6 +105,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 f2ec88b..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;
 
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 23de6af..860d914 100644 (file)
@@ -107,6 +107,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                            \
@@ -146,8 +147,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 6dfb9b7..9888097 100644 (file)
@@ -369,20 +369,11 @@ setAcceptorIdentity(OM_uint32 *minor,
 
     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, req,
                                    PW_GSS_ACCEPTOR_SERVICE_SPECIFICS,
                                    0,
index 2928151..235606f 100644 (file)
@@ -16,5 +16,6 @@ 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 dd008ed..19f1770 100644 (file)
@@ -177,6 +177,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
 
 struct gss_eap_initiator_ctx {
@@ -184,6 +185,8 @@ struct gss_eap_initiator_ctx {
     struct eap_peer_config eapPeerConfig;
     struct eap_sm *eap;
     struct wpabuf reqData;
+    struct wpabuf *chbindData;
+    unsigned int chbindReqFlags;
 };
 
 #ifdef GSSEAP_ENABLE_ACCEPTOR
index 7383601..2f0774b 100644 (file)
@@ -71,6 +71,7 @@ error_code GSSEAP_BAD_INITIATOR_NAME,           "Initiator identity must be a va
 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"
 
index 75af165..6cb4be0 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)
@@ -194,6 +197,145 @@ 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
+
+extern void TestFunc();
+
+static OM_uint32
+peerInitEapChannelBinding(OM_uint32 *minor, gss_ctx_id_t ctx)
+{
+    struct wpabuf *buf = NULL;
+    unsigned int chbindReqFlags = 0;
+    krb5_principal princ = NULL;
+    gss_buffer_desc nameBuf = GSS_C_EMPTY_BUFFER;
+    OM_uint32 major = GSS_S_COMPLETE;
+    krb5_context krbContext = NULL;
+
+    /* XXX is this check redundant? */
+    if (ctx->acceptorName == GSS_C_NO_NAME) {
+        major = GSS_S_BAD_NAME;
+        *minor = GSSEAP_NO_ACCEPTOR_NAME;
+        goto cleanup;
+    }
+
+    princ = ctx->acceptorName->krbPrincipal;
+
+    krbPrincComponentToGssBuffer(princ, 0, &nameBuf);
+    if (nameBuf.length > 0) {
+        major = gssEapRadiusAddAttr(minor, &buf, PW_GSS_ACCEPTOR_SERVICE_NAME,
+                                    0, &nameBuf);
+        if (GSS_ERROR(major))
+            goto cleanup;
+
+        chbindReqFlags |= CHBIND_SERVICE_NAME_FLAG;
+    }
+
+    krbPrincComponentToGssBuffer(princ, 1, &nameBuf);
+    if (nameBuf.length > 0) {
+        major = gssEapRadiusAddAttr(minor, &buf, PW_GSS_ACCEPTOR_HOST_NAME,
+                                    0, &nameBuf);
+        if (GSS_ERROR(major))
+            goto cleanup;
+
+        chbindReqFlags |= CHBIND_HOST_NAME_FLAG;
+    }
+
+    GSSEAP_KRB_INIT(&krbContext);
+
+    *minor = krbPrincUnparseServiceSpecifics(krbContext, princ, &nameBuf);
+    if (*minor != 0)
+        goto cleanup;
+
+    if (nameBuf.length > 0) {
+        major = gssEapRadiusAddAttr(minor, &buf,
+                                    PW_GSS_ACCEPTOR_SERVICE_SPECIFICS,
+                                    0, &nameBuf);
+        if (GSS_ERROR(major))
+            goto cleanup;
+
+        chbindReqFlags |= CHBIND_SERVICE_SPECIFIC_FLAG;
+    }
+
+    krbFreeUnparsedName(krbContext, &nameBuf);
+    krbPrincRealmToGssBuffer(princ, &nameBuf);
+
+    if (nameBuf.length > 0) {
+        major = gssEapRadiusAddAttr(minor, &buf,
+                                    PW_GSS_ACCEPTOR_REALM_NAME,
+                                    0, &nameBuf);
+        chbindReqFlags |= CHBIND_REALM_NAME_FLAG;
+    }
+
+    if (chbindReqFlags == 0) {
+        major = GSS_S_BAD_NAME;
+        *minor = GSSEAP_BAD_ACCEPTOR_NAME;
+        goto cleanup;
+    }
+
+    ctx->initiatorCtx.chbindData = buf;
+    ctx->initiatorCtx.chbindReqFlags = chbindReqFlags;
+
+    buf = NULL;
+
+    major = GSS_S_COMPLETE;
+    *minor = 0;
+
+cleanup:
+    krbFreeUnparsedName(krbContext, &nameBuf);
+    wpabuf_free(buf);
+
+    return major;
+}
+
+static void
+peerProcessChbindResponse(void *context, int code, int nsid,
+                          u8 *data, size_t len)
+{
+    radius_parser msg;
+    gss_ctx_id_t ctx = (gss_ctx_id_t )context;
+    void *vsadata;
+    u8 type;
+    u32 vendor_id;
+    u32 chbindRetFlags = 0;
+    size_t vsadata_len;
+
+    if (nsid != CHBIND_NSID_RADIUS)
+        return;
+
+    msg = radius_parser_start(data, len);
+    if (msg == NULL)
+        return;
+
+    while (radius_parser_parse_tlv(msg, &type, &vendor_id, &vsadata,
+                                   &vsadata_len) == 0) {
+        switch (type) {
+        case PW_GSS_ACCEPTOR_SERVICE_NAME:
+            chbindRetFlags |= CHBIND_SERVICE_NAME_FLAG;
+            break;
+        case PW_GSS_ACCEPTOR_HOST_NAME:
+            chbindRetFlags |= CHBIND_HOST_NAME_FLAG;
+            break;
+        case PW_GSS_ACCEPTOR_SERVICE_SPECIFICS:
+            chbindRetFlags |= CHBIND_SERVICE_SPECIFIC_FLAG;
+            break;
+        case PW_GSS_ACCEPTOR_REALM_NAME:
+            chbindRetFlags |= CHBIND_REALM_NAME_FLAG;
+            break;
+        }
+    }
+
+    radius_parser_finish(msg);
+
+    if (code == CHBIND_CODE_SUCCESS &&
+        ((chbindRetFlags & ctx->initiatorCtx.chbindReqFlags) == ctx->initiatorCtx.chbindReqFlags)) {
+        ctx->flags |= CTX_FLAG_EAP_CHBIND_ACCEPT;
+        ctx->gssFlags |= GSS_C_MUTUAL_FLAG;
+    } /* else log failures? */
+}
+
 static OM_uint32
 peerConfigInit(OM_uint32 *minor, gss_ctx_id_t ctx)
 {
@@ -258,6 +400,27 @@ peerConfigInit(OM_uint32 *minor, gss_ctx_id_t ctx)
     eapPeerConfig->subject_match = (unsigned char *)cred->subjectNameConstraint.value;
     eapPeerConfig->altsubject_match = (unsigned char *)cred->subjectAltNameConstraint.value;
 
+    /* eap channel binding */
+    if (ctx->initiatorCtx.chbindData != NULL) {
+        struct eap_peer_chbind_config *chbind_config =
+            (struct eap_peer_chbind_config *)GSSEAP_MALLOC(sizeof(struct eap_peer_chbind_config));
+        if (chbind_config == NULL) {
+            *minor = ENOMEM;
+            return GSS_S_FAILURE;
+        }
+
+        chbind_config->req_data = wpabuf_mhead_u8(ctx->initiatorCtx.chbindData);
+        chbind_config->req_data_len = wpabuf_len(ctx->initiatorCtx.chbindData);
+        chbind_config->nsid = CHBIND_NSID_RADIUS;
+        chbind_config->response_cb = &peerProcessChbindResponse;
+        chbind_config->ctx = ctx;
+        eapPeerConfig->chbind_config = chbind_config;
+        eapPeerConfig->chbind_config_len = 1;
+    } else {
+        eapPeerConfig->chbind_config = NULL;
+        eapPeerConfig->chbind_config_len = 0;
+    }
+
     *minor = 0;
     return GSS_S_COMPLETE;
 }
@@ -288,18 +451,12 @@ 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))
@@ -609,6 +766,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;
 }
 
@@ -709,7 +875,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;
 
@@ -1071,8 +1237,10 @@ gssEapInitSecContext(OM_uint32 *minor,
             goto cleanup;
         }
     }
+
     if (ret_flags != NULL)
         *ret_flags = ctx->gssFlags;
+
     if (time_rec != NULL)
         gssEapContextTime(&tmpMinor, ctx, time_rec);
 
index dfd2f44..9a606ab 100644 (file)
@@ -403,6 +403,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))
@@ -799,13 +801,20 @@ verifyTokenHeader(OM_uint32 *minor,
                   enum gss_eap_token_type *ret_tok_type);
 
 /* Helper macros */
-
 #ifndef GSSEAP_MALLOC
+#if _WIN32
+#include <gssapi/gssapi_alloc.h>
+#define GSSEAP_MALLOC                   gssalloc_malloc
+#define GSSEAP_CALLOC                   gssalloc_calloc
+#define GSSEAP_FREE                     gssalloc_free
+#define GSSEAP_REALLOC                  gssalloc_realloc
+#else
 #define GSSEAP_CALLOC                   calloc
 #define GSSEAP_MALLOC                   malloc
 #define GSSEAP_FREE                     free
 #define GSSEAP_REALLOC                  realloc
-#endif
+#endif /* _WIN32 */
+#endif /* !GSSEAP_MALLOC */
 
 #ifndef GSSAPI_CALLCONV
 #define GSSAPI_CALLCONV                 KRB5_CALLCONV
@@ -997,13 +1006,54 @@ 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)
+{
+    krb5_free_unparsed_name(krbContext, (char *)(nameBuf->value));
+    nameBuf->value = NULL;
+    nameBuf->length = 0;
 }
 
 static inline void
index 7d9b9e8..d8ec3df 100644 (file)
  */
 
 #include "gssapiP_eap.h"
+#include "util_radius.h"
+#include "utils/radius_utils.h"
+
+#ifdef GSSEAP_ENABLE_ACCEPTOR
 
 #define RS_MAP_ERROR(code)  (ERROR_TABLE_BASE_rse + (code))
 
@@ -934,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 4d1463d..d4f86ec 100644 (file)
@@ -40,6 +40,8 @@
 #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:
@@ -108,10 +110,14 @@ private:
     bool m_authenticated;
 };
 
+#endif /* GSSEAP_ENABLE_ACCEPTOR */
+
 /* For now */
 extern "C" {
 #endif
 
+#ifdef GSSEAP_ENABLE_ACCEPTOR
+
 OM_uint32
 gssEapRadiusAddAvp(OM_uint32 *minor,
                    struct rs_packet *pkt,
@@ -149,6 +155,18 @@ gssEapCreateRadiusContext(OM_uint32 *minor,
                           gss_cred_id_t cred,
                           struct rs_context **pRadContext);
 
+#endif /* GSSEAP_ENABLE_ACCEPTOR */
+
+/* initiator utilities that require only libeap, and not freeradius */
+struct wpabuf;
+
+OM_uint32
+gssEapRadiusAddAttr(OM_uint32 *minor,
+                    struct wpabuf **dst,
+                    uint16_t type,
+                    uint16_t vendor,
+                    gss_buffer_t value);
+
 /* This really needs to be a function call on Windows */
 #define RS_CONFIG_FILE      SYSCONFDIR "/radsec.conf"