From: Kevin Wasserman Date: Thu, 2 Feb 2012 12:44:29 +0000 (-0500) Subject: eap channel binding support. X-Git-Tag: 0.9.2~43^2~16 X-Git-Url: http://www.project-moonshot.org/gitweb/?a=commitdiff_plain;ds=sidebyside;h=3c5bdb4c98d5a2212b51d4f8e865c6b5a5780bd4;p=mech_eap.git eap channel binding support. --- diff --git a/libeap/Makefile.am b/libeap/Makefile.am index 163e4ff..8b73133 100644 --- a/libeap/Makefile.am +++ b/libeap/Makefile.am @@ -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 \ diff --git a/libeap/src/eap_common/eap_ttls.h b/libeap/src/eap_common/eap_ttls.h index 09b6083..7cb45fc 100644 --- a/libeap/src/eap_common/eap_ttls.h +++ b/libeap/src/eap_common/eap_ttls.h @@ -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 diff --git a/libeap/src/eap_peer/eap_config.h b/libeap/src/eap_peer/eap_config.h index 4373532..c78fba1 100644 --- a/libeap/src/eap_peer/eap_config.h +++ b/libeap/src/eap_peer/eap_config.h @@ -19,6 +19,30 @@ 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) /** diff --git a/libeap/src/eap_peer/eap_ttls.c b/libeap/src/eap_peer/eap_ttls.c index 2573780..f5f17ff 100644 --- a/libeap/src/eap_peer/eap_ttls.c +++ b/libeap/src/eap_peer/eap_ttls.c @@ -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; ichbind_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; ichbind_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; ichbind_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 index 0000000..8bdba6c --- /dev/null +++ b/libeap/src/utils/radius_utils.c @@ -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 index 0000000..46ab65d --- /dev/null +++ b/libeap/src/utils/radius_utils.h @@ -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