Eap channel binding support code
[freeradius.git] / src / modules / rlm_eap / libeap / eapcommon.c
index d918f1a..0ad65cc 100644 (file)
@@ -60,8 +60,6 @@
 #include <freeradius-devel/ident.h>
 RCSID("$Id$")
 
-#include <freeradius-devel/autoconf.h>
-#include <freeradius-devel/missing.h>
 #include <freeradius-devel/libradius.h>
 #include "eap_types.h"
 
@@ -95,9 +93,31 @@ static const char *eap_types[] = {
   "mschapv2",                  /* 26 */
   "27",
   "28",
-  "cisco_mschapv2"             /* 29 */
-};
-#define MAX_EAP_TYPE_NAME 29
+  "cisco_mschapv2",            /* 29 */
+  "30",
+  "31",
+  "32",
+  "33",
+  "34",
+  "35",
+  "36",
+  "37",
+  "tnc",                       /* 38 */
+  "39",
+  "40",
+  "41",
+  "42",
+  "fast",
+  "44",
+  "45",
+  "pax",
+  "psk",
+  "sake",
+  "ikev2",
+  "50",
+  "51",
+  "pwd"
+};                             /* MUST have PW_EAP_MAX_TYPES */
 
 /*
  *     Return an EAP-Type for a particular name.
@@ -122,12 +142,12 @@ const char *eaptype_type2name(unsigned int type, char *buffer, size_t buflen)
 {
        DICT_VALUE      *dval;
 
-       if (type > MAX_EAP_TYPE_NAME) {
+       if (type > PW_EAP_MAX_TYPES) {
                /*
                 *      Prefer the dictionary name over a number,
                 *      if it exists.
                 */
-               dval = dict_valbyattr(PW_EAP_TYPE, type);
+               dval = dict_valbyattr(PW_EAP_TYPE, 0, type);
                if (dval) {
                        snprintf(buffer, buflen, "%s", dval->name);
                }
@@ -138,7 +158,7 @@ const char *eaptype_type2name(unsigned int type, char *buffer, size_t buflen)
                /*
                 *      Prefer the dictionary name, if it exists.
                 */
-               dval = dict_valbyattr(PW_EAP_TYPE, type);
+               dval = dict_valbyattr(PW_EAP_TYPE, 0, type);
                if (dval) {
                        snprintf(buffer, buflen, "%s", dval->name);
                        return buffer;
@@ -165,22 +185,18 @@ const char *eaptype_type2name(unsigned int type, char *buffer, size_t buflen)
  *                      be malloc()'ed to the right size.
  *
  */
-static int eap_wireformat(EAP_PACKET *reply)
+int eap_wireformat(EAP_PACKET *reply)
 {
-
        eap_packet_t    *hdr;
        uint16_t total_length = 0;
 
        if (reply == NULL) return EAP_INVALID;
 
        /*
-        * if reply->packet is set, then the wire format
-        * has already been calculated, just succeed!
+        *      If reply->packet is set, then the wire format
+        *      has already been calculated, just succeed.
         */
-       if(reply->packet != NULL)
-       {
-               return EAP_VALID;
-       }
+       if(reply->packet != NULL) return EAP_VALID;
 
        total_length = EAP_HEADER_LEN;
        if (reply->code < 3) {
@@ -200,7 +216,7 @@ static int eap_wireformat(EAP_PACKET *reply)
        hdr->code = (reply->code & 0xFF);
        hdr->id = (reply->id & 0xFF);
        total_length = htons(total_length);
-       memcpy(hdr->length, &total_length, sizeof(uint16_t));
+       memcpy(hdr->length, &total_length, sizeof(total_length));
 
        /*
         *      Request and Response packets are special.
@@ -227,17 +243,15 @@ static int eap_wireformat(EAP_PACKET *reply)
        return EAP_VALID;
 }
 
+
 /*
  *     compose EAP reply packet in EAP-Message attr of RADIUS.  If
  *     EAP exceeds 253, frame it in multiple EAP-Message attrs.
  */
 int eap_basic_compose(RADIUS_PACKET *packet, EAP_PACKET *reply)
 {
-       uint16_t eap_len, len;
-       VALUE_PAIR *eap_msg;
        VALUE_PAIR *vp;
        eap_packet_t *eap_packet;
-       unsigned char   *ptr;
        int rcode;
 
        if (eap_wireformat(reply) == EAP_INVALID) {
@@ -245,32 +259,11 @@ int eap_basic_compose(RADIUS_PACKET *packet, EAP_PACKET *reply)
        }
        eap_packet = (eap_packet_t *)reply->packet;
 
-       memcpy(&eap_len, &(eap_packet->length), sizeof(uint16_t));
-       len = eap_len = ntohs(eap_len);
-       ptr = (unsigned char *)eap_packet;
+       pairdelete(&(packet->vps), PW_EAP_MESSAGE, 0, TAG_ANY);
 
-       pairdelete(&(packet->vps), PW_EAP_MESSAGE);
-
-       do {
-               if (eap_len > 253) {
-                       len = 253;
-                       eap_len -= 253;
-               } else {
-                       len = eap_len;
-                       eap_len = 0;
-               }
-
-               /*
-                * create a value pair & append it to the packet list
-                * This memory gets freed up when packet is freed up
-                */
-               eap_msg = paircreate(PW_EAP_MESSAGE, PW_TYPE_OCTETS);
-               memcpy(eap_msg->vp_strvalue, ptr, len);
-               eap_msg->length = len;
-               pairadd(&(packet->vps), eap_msg);
-               ptr += len;
-               eap_msg = NULL;
-       } while (eap_len);
+       vp = eap_packet2vp(eap_packet);
+       if (!vp) return RLM_MODULE_INVALID;
+       pairadd(&(packet->vps), vp);
 
        /*
         *      EAP-Message is always associated with
@@ -279,9 +272,9 @@ int eap_basic_compose(RADIUS_PACKET *packet, EAP_PACKET *reply)
         *      Don't add a Message-Authenticator if it's already
         *      there.
         */
-       vp = pairfind(packet->vps, PW_MESSAGE_AUTHENTICATOR);
+       vp = pairfind(packet->vps, PW_MESSAGE_AUTHENTICATOR, 0, TAG_ANY);
        if (!vp) {
-               vp = paircreate(PW_MESSAGE_AUTHENTICATOR, PW_TYPE_OCTETS);
+               vp = paircreate(PW_MESSAGE_AUTHENTICATOR, 0);
                memset(vp->vp_strvalue, 0, AUTH_VECTOR_LEN);
                vp->length = AUTH_VECTOR_LEN;
                pairadd(&(packet->vps), vp);
@@ -313,87 +306,42 @@ int eap_basic_compose(RADIUS_PACKET *packet, EAP_PACKET *reply)
        return rcode;
 }
 
-/*
- * given a radius request with some attributes in the EAP range, build
- * them all into a single EAP-Message body.
- *
- * Note that this function will build multiple EAP-Message bodies
- * if there are multiple eligible EAP-types. This is incorrect, as the
- * recipient will in fact concatenate them.
- *
- * XXX - we could break the loop once we process one type. Maybe this
- *       just deserves an assert?
- *
- */
-void map_eap_types(RADIUS_PACKET *req)
+
+VALUE_PAIR *eap_packet2vp(const eap_packet_t *packet)
 {
-       VALUE_PAIR *vp, *vpnext;
-       int id, eapcode;
-       EAP_PACKET ep;
-       int eap_type;
-
-       vp = pairfind(req->vps, ATTRIBUTE_EAP_ID);
-       if(vp == NULL) {
-               id = ((int)getpid() & 0xff);
-       } else {
-               id = vp->lvalue;
-       }
+       int             total, size;
+       const uint8_t   *ptr;
+       VALUE_PAIR      *head = NULL;
+       VALUE_PAIR      **tail = &head;
+       VALUE_PAIR      *vp;
 
-       vp = pairfind(req->vps, ATTRIBUTE_EAP_CODE);
-       if(vp == NULL) {
-               eapcode = PW_EAP_REQUEST;
-       } else {
-               eapcode = vp->lvalue;
-       }
+       total = packet->length[0] * 256 + packet->length[1];
 
+       ptr = (const uint8_t *) packet;
 
-       for(vp = req->vps; vp != NULL; vp = vpnext) {
-               /* save it in case it changes! */
-               vpnext = vp->next;
+       do {
+               size = total;
+               if (size > 253) size = 253;
 
-               if(vp->attribute >= ATTRIBUTE_EAP_BASE &&
-                  vp->attribute < ATTRIBUTE_EAP_BASE+256) {
-                       break;
+               vp = paircreate(PW_EAP_MESSAGE, 0);
+               if (!vp) {
+                       pairfree(&head);
+                       return NULL;
                }
-       }
+               memcpy(vp->vp_octets, ptr, size);
+               vp->length = size;
 
-       if(vp == NULL) {
-               return;
-       }
+               *tail = vp;
+               tail = &(vp->next);
 
-       eap_type = vp->attribute - ATTRIBUTE_EAP_BASE;
-
-       switch(eap_type) {
-       case PW_EAP_IDENTITY:
-       case PW_EAP_NOTIFICATION:
-       case PW_EAP_NAK:
-       case PW_EAP_MD5:
-       case PW_EAP_OTP:
-       case PW_EAP_GTC:
-       case PW_EAP_TLS:
-       case PW_EAP_LEAP:
-       case PW_EAP_TTLS:
-       case PW_EAP_PEAP:
-       default:
-               /*
-                * no known special handling, it is just encoded as an
-                * EAP-message with the given type.
-                */
+               ptr += size;
+               total -= size;
+       } while (total > 0);
 
-               /* nuke any existing EAP-Messages */
-               pairdelete(&req->vps, PW_EAP_MESSAGE);
-
-               memset(&ep, 0, sizeof(ep));
-               ep.code = eapcode;
-               ep.id   = id;
-               ep.type.type = eap_type;
-               ep.type.length = vp->length;
-               ep.type.data = malloc(vp->length);
-               memcpy(ep.type.data,vp->vp_octets, vp->length);
-               eap_basic_compose(req, &ep);
-       }
+       return head;
 }
 
+
 /*
  * Handles multiple EAP-Message attrs
  * ie concatenates all to get the complete EAP packet.
@@ -401,7 +349,7 @@ void map_eap_types(RADIUS_PACKET *req)
  * NOTE: Sometimes Framed-MTU might contain the length of EAP-Message,
  *      refer fragmentation in rfc2869.
  */
-eap_packet_t *eap_attribute(VALUE_PAIR *vps)
+eap_packet_t *eap_vp2packet(VALUE_PAIR *vps)
 {
        VALUE_PAIR *first, *vp;
        eap_packet_t *eap_packet;
@@ -412,9 +360,9 @@ eap_packet_t *eap_attribute(VALUE_PAIR *vps)
        /*
         *      Get only EAP-Message attribute list
         */
-       first = pairfind(vps, PW_EAP_MESSAGE);
+       first = pairfind(vps, PW_EAP_MESSAGE, 0, TAG_ANY);
        if (first == NULL) {
-               radlog(L_ERR, "rlm_eap: EAP-Message not found");
+               DEBUG("rlm_eap: EAP-Message not found");
                return NULL;
        }
 
@@ -422,7 +370,7 @@ eap_packet_t *eap_attribute(VALUE_PAIR *vps)
         *      Sanity check the length before doing anything.
         */
        if (first->length < 4) {
-               radlog(L_ERR, "rlm_eap: EAP packet is too short.");
+               DEBUG("rlm_eap: EAP packet is too short.");
                return NULL;
        }
 
@@ -437,7 +385,7 @@ eap_packet_t *eap_attribute(VALUE_PAIR *vps)
         *      Take out even more weird things.
         */
        if (len < 4) {
-               radlog(L_ERR, "rlm_eap: EAP packet has invalid length.");
+               DEBUG("rlm_eap: EAP packet has invalid length.");
                return NULL;
        }
 
@@ -445,11 +393,11 @@ eap_packet_t *eap_attribute(VALUE_PAIR *vps)
         *      Sanity check the length, BEFORE malloc'ing memory.
         */
        total_len = 0;
-       for (vp = first; vp; vp = pairfind(vp->next, PW_EAP_MESSAGE)) {
+       for (vp = first; vp; vp = pairfind(vp->next, PW_EAP_MESSAGE, 0, TAG_ANY)) {
                total_len += vp->length;
 
                if (total_len > len) {
-                       radlog(L_ERR, "rlm_eap: Malformed EAP packet.  Length in packet header does not match actual length");
+                       DEBUG("rlm_eap: Malformed EAP packet.  Length in packet header does not match actual length");
                        return NULL;
                }
        }
@@ -458,7 +406,7 @@ eap_packet_t *eap_attribute(VALUE_PAIR *vps)
         *      If the length is SMALLER, die, too.
         */
        if (total_len < len) {
-               radlog(L_ERR, "rlm_eap: Malformed EAP packet.  Length in packet header does not match actual length");
+               DEBUG("rlm_eap: Malformed EAP packet.  Length in packet header does not match actual length");
                return NULL;
        }
 
@@ -472,12 +420,12 @@ eap_packet_t *eap_attribute(VALUE_PAIR *vps)
        }
 
        /*
-        *      Copy the data from EAP-Message's over to out EAP packet.
+        *      Copy the data from EAP-Message's over to our EAP packet.
         */
        ptr = (unsigned char *)eap_packet;
 
        /* RADIUS ensures order of attrs, so just concatenate all */
-       for (vp = first; vp; vp = pairfind(vp->next, PW_EAP_MESSAGE)) {
+       for (vp = first; vp; vp = pairfind(vp->next, PW_EAP_MESSAGE, 0, TAG_ANY)) {
                memcpy(ptr, vp->vp_strvalue, vp->length);
                ptr += vp->length;
        }
@@ -485,73 +433,101 @@ eap_packet_t *eap_attribute(VALUE_PAIR *vps)
        return eap_packet;
 }
 
-/*
- * given a radius request with an EAP-Message body, decode it specific
- * attributes.
- */
-void unmap_eap_types(RADIUS_PACKET *rep)
+VALUE_PAIR *eap_chbind_packet2vp(const eap_chbind_packet_t *packet, size_t len)
 {
-       VALUE_PAIR *eap1;
-       eap_packet_t *e;
-       int len;
-       int type;
+       size_t          size;
+       const uint8_t   *ptr;
+       VALUE_PAIR      *head = NULL;
+       VALUE_PAIR      **tail = &head;
+       VALUE_PAIR      *vp;
 
-       /* find eap message */
-       e = eap_attribute(rep->vps);
+       ptr = (const uint8_t *) packet;
 
-       /* nothing to do! */
-       if(e == NULL) return;
+       do {
+               size = len;
+               if (size > 247) size = 247;
 
-       /* create EAP-ID and EAP-CODE attributes to start */
-       eap1 = paircreate(ATTRIBUTE_EAP_ID, PW_TYPE_INTEGER);
-       eap1->lvalue = e->id;
-       pairadd(&(rep->vps), eap1);
+               vp = paircreate(PW_VENDOR_SPECIFIC, VENDORPEC_UKERNA,
+                               PW_TYPE_OCTETS);
+               if (!vp) {
+                       pairfree(&head);
+                       return NULL;
+               }
+               vp->vp_octets[0] = PW_UKERNA_CHBIND;
+               vp->vp_octets[1] = size;
+               memcpy(&vp->vp_octets[2], ptr, size);
+               vp->length = size + 2;
 
-       eap1 = paircreate(ATTRIBUTE_EAP_CODE, PW_TYPE_INTEGER);
-       eap1->lvalue = e->code;
-       pairadd(&(rep->vps), eap1);
+               *tail = vp;
+               tail = &(vp->next);
 
-       switch(e->code)
-       {
-       default:
-       case PW_EAP_SUCCESS:
-       case PW_EAP_FAILURE:
-               /* no data */
-               break;
+               ptr += size;
+               len -= size;
+       } while (len > 0);
 
-       case PW_EAP_REQUEST:
-       case PW_EAP_RESPONSE:
-               /* there is a type field, which we use to create
-                * a new attribute */
+       return head;
+}
 
-               /* the length was decode already into the attribute
-                * length, and was checked already. Network byte
-                * order, just pull it out using math.
-                */
-               len = e->length[0]*256 + e->length[1];
 
-               /* verify the length is big enough to hold type */
-               if(len < 5)
-               {
-                       return;
-               }
+/*
+ * Find the next EAP-CHANNEL-BINDING message in the 
+ * pair list
+ */
+static VALUE_PAIR *eap_chbind_find_pair(VALUE_PAIR *vps)
+{
+       VALUE_PAIR *result = pairfind(vps, PW_VENDOR_SPECIFIC, 
+                                     VENDORPEC_UKERNA);
+        while (result && (result->vp_octets[0] != PW_UKERNA_CHBIND))
+               result = result->next;
+       return result;
+}
 
-               type = e->data[0];
+/*
+ * Handles multiple EAP-channel-binding Message attrs
+ * ie concatenates all to get the complete EAP-channel-binding packet.
+ */
+size_t eap_chbind_vp2packet(VALUE_PAIR *vps, eap_chbind_packet_t **result)
+{
+       VALUE_PAIR *first, *vp;
+       eap_chbind_packet_t *eap_chbind_packet;
+       unsigned char *ptr;
+       size_t len;
 
-               type += ATTRIBUTE_EAP_BASE;
-               len -= 5;
+       first = eap_chbind_find_pair(vps);
 
-               if(len > MAX_STRING_LEN) {
-                       len = MAX_STRING_LEN;
+       /*
+        *      Sanity check the length, BEFORE malloc'ing memory.
+        */
+       len = 0;
+       for (vp = first; vp; vp = eap_chbind_find_pair(vp)) {
+               if ((vp->length < 2) ||
+                   (vp->length != vp->vp_octets[1]+2)) {
+                       DEBUG("rlm_eap: Malformed EAP channel binding value pair.  Length in pair header does not match actual length");
+                       return 0;
                }
+               len += vp->vp_octets[1];
+       }
 
-               eap1 = paircreate(type, PW_TYPE_OCTETS);
-               memcpy(eap1->vp_strvalue, &e->data[1], len);
-               eap1->length = len;
-               pairadd(&(rep->vps), eap1);
-               break;
+       /*
+        *      Now that we know the lengths are OK, allocate memory.
+        */
+       eap_chbind_packet = (eap_chbind_packet_t *) malloc(len);
+       if (eap_chbind_packet == NULL) {
+               radlog(L_ERR, "rlm_eap: out of memory");
+               return 0;
        }
 
-       return;
-}
+       /*
+        *      Copy the data from EAP-Message's over to our EAP packet.
+        */
+       ptr = (unsigned char *)eap_chbind_packet;
 
+       /* RADIUS ensures order of attrs, so just concatenate all */
+       for (vp = first; vp; vp = eap_chbind_find_pair(vp->next)) {
+               memcpy(ptr, vp->vp_octets+2, vp->length-2);
+               ptr += vp->length-2;
+       }
+
+       *result = eap_chbind_packet;
+       return len;
+}