eap channel binding support.
authorKevin Wasserman <kevin.wasserman@painless-security.com>
Thu, 2 Feb 2012 12:44:29 +0000 (07:44 -0500)
committerSam Hartman <hartmans@debian.org>
Sun, 5 Feb 2012 22:12:12 +0000 (17:12 -0500)
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/radius_utils.c [new file with mode: 0644]
libeap/src/utils/radius_utils.h [new file with mode: 0644]

index 163e4ff..8b73133 100644 (file)
@@ -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..7cb45fc 100644 (file)
@@ -65,6 +65,9 @@ do { \
 #define RADIUS_ATTR_MS_CHAP2_SUCCESS 26
 #define RADIUS_ATTR_MS_CHAP2_CPW 27
 
+/* draft TTLS-EAP-CHBIND */
+#define DIAMETER_ATTR_CHBIND_MESSAGE 0xFFFFFE /* experimental, for now */
+
 #define EAP_TTLS_MSCHAPV2_CHALLENGE_LEN 16
 #define EAP_TTLS_MSCHAPV2_RESPONSE_LEN 50
 #define EAP_TTLS_MSCHAP_CHALLENGE_LEN 8
index 4373532..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
  */
@@ -629,14 +653,14 @@ struct eap_peer_config {
        int fragment_size;
 
     /**
-     * chbind_data - eap channel binding data
+     * chbind_config - eap channel binding config data
      */
-    u8 *chbind_data;
+    struct eap_peer_chbind_config *chbind_config;
 
     /**
-     * chbind_data_len - length of eap channel binding data
+     * chbind_config_len - channel binding config data count
      */
-    size_t chbind_data_len;
+    size_t chbind_config_len;
 
 #define EAP_CONFIG_FLAGS_PASSWORD_NTHASH BIT(0)
        /**
index 2573780..f5f17ff 100644 (file)
@@ -73,7 +73,7 @@ struct eap_ttls_data {
        u8 *key_data;
 
        struct wpabuf *pending_phase2_req;
-
+       int chbind_req_sent; /* channel binding request was sent */
 #ifdef EAP_TNC
        int ready_for_tnc;
        int tnc_started;
@@ -81,6 +81,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;
+};
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+
+
 static void * eap_ttls_init(struct eap_sm *sm)
 {
        struct eap_ttls_data *data;
@@ -1061,6 +1078,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 +1113,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 +1195,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 == 0 &&
+                  avp_code == DIAMETER_ATTR_CHBIND_MESSAGE) {
+               /* 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 +1321,99 @@ 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_encapsulate(&chbind_req,
+               DIAMETER_ATTR_CHBIND_MESSAGE, 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,
@@ -1477,16 +1626,33 @@ 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;
+       }
+
+       /* 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;
+       }
+       /* 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 (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;
diff --git a/libeap/src/utils/radius_utils.c b/libeap/src/utils/radius_utils.c
new file mode 100644 (file)
index 0000000..8bdba6c
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * 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"
+
+struct radius_vendor_attr_struct
+{
+    struct wpabuf *buf;
+    u8 *len_pos;
+    size_t start;
+};
+
+radius_vendor_attr radius_vendor_attr_start(struct wpabuf *buf, u32 vendor)
+{
+    radius_vendor_attr attr = (radius_vendor_attr )os_zalloc(sizeof(*attr));
+    if (!attr)
+        return attr;
+    attr->buf = buf;
+    attr->start = wpabuf_len(buf);
+    wpabuf_put_u8(buf, 26);
+    attr->len_pos = (u8*)wpabuf_put(buf, 1);
+    /* @TODO: Verify high 8 bits of vendor are 0? */
+    wpabuf_put_be32(buf, vendor);
+    return attr;
+}
+
+radius_vendor_attr radius_vendor_attr_add_subtype(radius_vendor_attr attr,
+                                                  u8 type,
+                                                  u8 *data,
+                                                  size_t len)
+{
+    if (attr == VENDOR_ATTR_INVALID)
+        return attr;
+    if (len + 2 + (wpabuf_len(attr->buf) - attr->start) > 255) {
+        os_free(attr);
+        return VENDOR_ATTR_INVALID;
+    }
+    wpabuf_put_u8(attr->buf, type);
+    wpabuf_put_u8(attr->buf, len + 2);
+    wpabuf_put_data(attr->buf, data, len);
+    return attr;
+}
+
+radius_vendor_attr radius_vendor_attr_finish(radius_vendor_attr attr)
+{
+    /* poke size into correct place and free attr */
+    size_t len;
+    radius_vendor_attr ret = VENDOR_ATTR_INVALID;
+    if (attr == ret)
+        return ret;
+
+    len = wpabuf_len(attr->buf) - attr->start;
+    if (len < 255) {
+        ret = attr;
+        *(attr->len_pos) = (u8 )len;
+    }
+    os_free(attr);
+    return ret;
+}
+
+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..46ab65d
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * RADIUS tlv construction 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_vendor_attr_struct;
+typedef struct radius_vendor_attr_struct *radius_vendor_attr;
+#define VENDOR_ATTR_INVALID NULL
+radius_vendor_attr radius_vendor_attr_start(struct wpabuf *buf, u32 vendor);
+radius_vendor_attr radius_vendor_attr_add_subtype(radius_vendor_attr attr,
+                                                 u8 type,
+                                                 u8 *data, size_t len);
+radius_vendor_attr radius_vendor_attr_finish(radius_vendor_attr attr);
+
+struct radius_parser_struct;
+typedef struct radius_parser_struct *radius_parser;
+radius_parser radius_parser_start(void *tlvdata, size_t len);
+int radius_parser_parse_tlv(radius_parser parser, u8 *type, u32 *vendor_id,
+                           void **value, size_t *len);
+int radius_parser_parse_vendor_specific(radius_parser parser, u8 *vendor_type,
+                                       void **value, size_t *len);
+void radius_parser_finish(radius_parser parser);
+
+
+#endif /* RADIUS_UTILS_H */
\ No newline at end of file