aboutsummaryrefslogtreecommitdiffstats
path: root/src/libcharon/encoding/payloads/eap_payload.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libcharon/encoding/payloads/eap_payload.c')
-rw-r--r--src/libcharon/encoding/payloads/eap_payload.c167
1 files changed, 151 insertions, 16 deletions
diff --git a/src/libcharon/encoding/payloads/eap_payload.c b/src/libcharon/encoding/payloads/eap_payload.c
index 1b9a5c802..dd2e25795 100644
--- a/src/libcharon/encoding/payloads/eap_payload.c
+++ b/src/libcharon/encoding/payloads/eap_payload.c
@@ -1,4 +1,5 @@
/*
+ * Copyright (C) 2012 Tobias Brunner
* Copyright (C) 2005-2010 Martin Willi
* Copyright (C) 2005 Jan Hutter
* Hochschule fuer Technik Rapperswil
@@ -20,6 +21,7 @@
#include <daemon.h>
#include <eap/eap.h>
+#include <bio/bio_writer.h>
typedef struct private_eap_payload_t private_eap_payload_t;
@@ -217,28 +219,93 @@ METHOD(eap_payload_t, get_identifier, u_int8_t,
return 0;
}
+/**
+ * Get the current type at the given offset into this->data.
+ * @return the new offset or 0 if failed
+ */
+static size_t extract_type(private_eap_payload_t *this, size_t offset,
+ eap_type_t *type, u_int32_t *vendor)
+{
+ if (this->data.len > offset)
+ {
+ *vendor = 0;
+ *type = this->data.ptr[offset];
+ if (*type != EAP_EXPANDED)
+ {
+ return offset + 1;
+ }
+ if (this->data.len >= offset + 8)
+ {
+ *vendor = untoh32(this->data.ptr + offset) & 0x00FFFFFF;
+ *type = untoh32(this->data.ptr + offset + 4);
+ return offset + 8;
+ }
+ }
+ return 0;
+}
+
METHOD(eap_payload_t, get_type, eap_type_t,
private_eap_payload_t *this, u_int32_t *vendor)
{
eap_type_t type;
*vendor = 0;
- if (this->data.len > 4)
+ if (extract_type(this, 4, &type, vendor))
{
- type = this->data.ptr[4];
- if (type != EAP_EXPANDED)
- {
- return type;
- }
- if (this->data.len >= 12)
- {
- *vendor = untoh32(this->data.ptr + 4) & 0x00FFFFFF;
- return untoh32(this->data.ptr + 8);
- }
+ return type;
}
return 0;
}
+/**
+ * Type enumerator
+ */
+typedef struct {
+ /** public interface */
+ enumerator_t public;
+ /** payload */
+ private_eap_payload_t *payload;
+ /** current offset in the data */
+ size_t offset;
+} type_enumerator_t;
+
+METHOD(enumerator_t, enumerate_types, bool,
+ type_enumerator_t *this, eap_type_t *type, u_int32_t *vendor)
+{
+ this->offset = extract_type(this->payload, this->offset, type, vendor);
+ return this->offset;
+}
+
+METHOD(eap_payload_t, get_types, enumerator_t*,
+ private_eap_payload_t *this)
+{
+ type_enumerator_t *enumerator;
+ eap_type_t type;
+ u_int32_t vendor;
+ size_t offset;
+
+ offset = extract_type(this, 4, &type, &vendor);
+ if (offset && type == EAP_NAK)
+ {
+ INIT(enumerator,
+ .public = {
+ .enumerate = (void*)_enumerate_types,
+ .destroy = (void*)free,
+ },
+ .payload = this,
+ .offset = offset,
+ );
+ return &enumerator->public;
+ }
+ return enumerator_create_empty();
+}
+
+METHOD(eap_payload_t, is_expanded, bool,
+ private_eap_payload_t *this)
+{
+ return this->data.len > 4 ? this->data.ptr[4] == EAP_EXPANDED : FALSE;
+}
+
METHOD2(payload_t, eap_payload_t, destroy, void,
private_eap_payload_t *this)
{
@@ -270,6 +337,8 @@ eap_payload_t *eap_payload_create()
.get_code = _get_code,
.get_identifier = _get_identifier,
.get_type = _get_type,
+ .get_types = _get_types,
+ .is_expanded = _is_expanded,
.destroy = _destroy,
},
.next_payload = NO_PAYLOAD,
@@ -313,15 +382,81 @@ eap_payload_t *eap_payload_create_code(eap_code_t code, u_int8_t identifier)
return eap_payload_create_data(data);
}
+/**
+ * Write the given type either expanded or not
+ */
+static void write_type(bio_writer_t *writer, eap_type_t type, u_int32_t vendor,
+ bool expanded)
+{
+ if (expanded)
+ {
+ writer->write_uint8(writer, EAP_EXPANDED);
+ writer->write_uint24(writer, vendor);
+ writer->write_uint32(writer, type);
+ }
+ else
+ {
+ writer->write_uint8(writer, type);
+ }
+}
+
/*
* Described in header
*/
-eap_payload_t *eap_payload_create_nak(u_int8_t identifier)
+eap_payload_t *eap_payload_create_nak(u_int8_t identifier, eap_type_t type,
+ u_int32_t vendor, bool expanded)
{
- chunk_t data;
+ enumerator_t *enumerator;
+ eap_type_t reg_type;
+ u_int32_t reg_vendor;
+ bio_writer_t *writer;
+ chunk_t length, data;
+ bool added_any = FALSE, found_vendor = FALSE;
+ eap_payload_t *payload;
+
+ writer = bio_writer_create(12);
+ writer->write_uint8(writer, EAP_RESPONSE);
+ writer->write_uint8(writer, identifier);
+ length = writer->skip(writer, 2);
+
+ write_type(writer, EAP_NAK, 0, expanded);
+
+ enumerator = charon->eap->create_enumerator(charon->eap, EAP_PEER);
+ while (enumerator->enumerate(enumerator, &reg_type, &reg_vendor))
+ {
+ if ((type && type != reg_type) ||
+ (type && vendor && vendor != reg_vendor))
+ { /* the preferred type is only sent if we actually find it */
+ continue;
+ }
+ if (!reg_vendor || expanded)
+ {
+ write_type(writer, reg_type, reg_vendor, expanded);
+ added_any = TRUE;
+ }
+ else if (reg_vendor)
+ { /* found vendor specifc method, but this is not an expanded Nak */
+ found_vendor = TRUE;
+ }
+ }
+ enumerator->destroy(enumerator);
- data = chunk_from_chars(EAP_RESPONSE, identifier, 0, 0, EAP_NAK);
- htoun16(data.ptr + 2, data.len);
- return eap_payload_create_data(data);
+ if (found_vendor)
+ { /* request an expanded authentication type */
+ write_type(writer, EAP_EXPANDED, 0, expanded);
+ added_any = TRUE;
+ }
+ if (!added_any)
+ { /* no methods added */
+ write_type(writer, 0, 0, expanded);
+ }
+
+ /* set length */
+ data = writer->get_buf(writer);
+ htoun16(length.ptr, data.len);
+
+ payload = eap_payload_create_data(data);
+ writer->destroy(writer);
+ return payload;
}