Free memory if out of memory
[freeradius.git] / src / modules / rlm_eap / types / rlm_eap_md5 / eap_md5.c
index 11793f8..f87c8de 100644 (file)
  *
  *   You should have received a copy of the GNU General Public License
  *   along with this program; if not, write to the Free Software
- *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  *
- * Copyright 2000,2001  The FreeRADIUS server project
+ * Copyright 2000,2001,2006  The FreeRADIUS server project
  * Copyright 2001  hereUare Communications, Inc. <raghud@hereuare.com>
  */
 
 /*
+ *
  *  MD5 Packet Format in EAP Type-Data
- *  --- ------ ------ -- --- --------- 
+ *  --- ------ ------ -- --- ---------
  *  0                   1                   2                   3
  *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  *
  */
 
+#include <freeradius-devel/ident.h>
+RCSID("$Id$")
+
 #include <stdio.h>
+#include <stdlib.h>
 #include "eap.h"
 
 #include "eap_md5.h"
 /*
  *      Allocate a new MD5_PACKET
  */
-MD5_PACKET *md5_alloc()
+MD5_PACKET *eapmd5_alloc(void)
 {
-        MD5_PACKET   *rp;
-
-        if ((rp = malloc(sizeof(MD5_PACKET))) == NULL) {
-                radlog(L_ERR, "rlm_eap_md5: out of memory");
-                return NULL;
-        }
-        memset(rp, 0, sizeof(MD5_PACKET));
-        return rp;
+       MD5_PACKET   *rp;
+
+       if ((rp = malloc(sizeof(MD5_PACKET))) == NULL) {
+               radlog(L_ERR, "rlm_eap_md5: out of memory");
+               return NULL;
+       }
+       memset(rp, 0, sizeof(MD5_PACKET));
+       return rp;
 }
 
 /*
  *      Free MD5_PACKET
  */
-void md5_free(MD5_PACKET **md5_packet_ptr)
+void eapmd5_free(MD5_PACKET **md5_packet_ptr)
 {
-        MD5_PACKET *md5_packet;
+       MD5_PACKET *md5_packet;
 
-        if (!md5_packet_ptr) return;
-        md5_packet = *md5_packet_ptr;
-        if (md5_packet == NULL) return;
+       if (!md5_packet_ptr) return;
+       md5_packet = *md5_packet_ptr;
+       if (md5_packet == NULL) return;
 
-        if (md5_packet->value) free(md5_packet->value);
-        if (md5_packet->name) free(md5_packet->name);
+       if (md5_packet->value) free(md5_packet->value);
+       if (md5_packet->name) free(md5_packet->name);
 
-        free(md5_packet);
+       free(md5_packet);
 
-        *md5_packet_ptr = NULL;
+       *md5_packet_ptr = NULL;
 }
 
-/* 
- * We expect only RESPONSE for which CHALLENGE, SUCCESS or FAILURE is sent back
- */ 
-MD5_PACKET *extract_md5(EAP_DS *eap_ds)
+/*
+ *     We expect only RESPONSE for which SUCCESS or FAILURE is sent back
+ */
+MD5_PACKET *eapmd5_extract(EAP_DS *eap_ds)
 {
-        md5_packet_t   *data;
-        MD5_PACKET     *packet;
-       int             name_len;
-
-       if (!eap_ds                                             || 
-               !eap_ds->response                               || 
-               (eap_ds->response->code != PW_MD5_RESPONSE)     ||
-               eap_ds->response->type != PW_EAP_MD5            ||
-               !eap_ds->response->typedata                     ||
-               (eap_ds->response->length < MD5_HEADER_LEN)     ||
-               (eap_ds->response->typedata[0] <= 0)    ) {
-                radlog(L_ERR, "rlm_eap_md5: corrupted data");
+       md5_packet_t    *data;
+       MD5_PACKET      *packet;
+       unsigned short  name_len;
+
+       /*
+        *      We need a response, of type EAP-MD5, with at least
+        *      one byte of type data (EAP-MD5) following the 4-byte
+        *      EAP-Packet header.
+        */
+       if (!eap_ds                                      ||
+           !eap_ds->response                            ||
+           (eap_ds->response->code != PW_MD5_RESPONSE)  ||
+           eap_ds->response->type.type != PW_EAP_MD5    ||
+           !eap_ds->response->type.data                 ||
+           (eap_ds->response->length <= MD5_HEADER_LEN) ||
+           (eap_ds->response->type.data[0] <= 0)) {
+               radlog(L_ERR, "rlm_eap_md5: corrupted data");
                return NULL;
        }
 
-       packet = md5_alloc();
+       packet = eapmd5_alloc();
        if (!packet) return NULL;
 
        /*
-        * Code, id & length for MD5 & EAP are same
-        * but md5_length = eap_length - 1(Type = 1 octet)
+        *      Code & id for MD5 & EAP are same
+        *
+        *      but md5_length = length of the EAP-MD5 data, which
+        *      doesn't include the EAP header, or the octet saying
+        *      EAP-MD5.
         */
        packet->code = eap_ds->response->code;
        packet->id = eap_ds->response->id;
-       packet->length = eap_ds->response->length - 1;
-       packet->value_size = 0;
-       packet->value = NULL;
-       packet->name = NULL;
+       packet->length = eap_ds->response->length - (MD5_HEADER_LEN + 1);
 
-        data = (md5_packet_t *)eap_ds->response->typedata;
+       /*
+        *      Sanity check the EAP-MD5 packet sent to us
+        *      by the client.
+        */
+       data = (md5_packet_t *)eap_ds->response->type.data;
 
-        packet->value_size = data->value_size;
+       /*
+        *      Already checked the size above.
+        */
+       packet->value_size = data->value_size;
 
+       /*
+        *      Allocate room for the data, and copy over the data.
+        */
        packet->value = malloc(packet->value_size);
        if (packet->value == NULL) {
-                radlog(L_ERR, "rlm_eap_md5: out of memory");
-               md5_free(&packet);
+               radlog(L_ERR, "rlm_eap_md5: out of memory");
+               eapmd5_free(&packet);
                return NULL;
        }
        memcpy(packet->value, data->value_name, packet->value_size);
 
        /*
-        * Name is optional and is present after Value, but we need to check for it
+        *      Name is optional and is present after Value, but we
+        *      need to check for it, as eapmd5_compose()
         */
-       name_len =  packet->length - (packet->value_size + 5);
+       name_len =  packet->length - (packet->value_size + 1);
        if (name_len) {
-               packet->name = malloc(name_len+1);
+               packet->name = malloc(name_len + 1);
                if (!packet->name) {
                        radlog(L_ERR, "rlm_eap_md5: out of memory");
-                       md5_free(&packet);
+                       eapmd5_free(&packet);
                        return NULL;
                }
-               memset(packet->name, 0, name_len+1);
-               memcpy(packet->name, data->value_name+packet->value_size, name_len);
+               memcpy(packet->name, data->value_name + packet->value_size,
+                      name_len);
+               packet->name[name_len] = 0;
        }
 
        return packet;
 }
 
-/*
- * Identify whether the response that you got is either the
- * response to the challenge that we sent or a new one.
- * If it is a response to the request then issue success/failure
- * else issue a challenge
- */
-MD5_PACKET *process_md5(MD5_PACKET *packet, int id,
-               VALUE_PAIR *username, VALUE_PAIR* password, md5_packet_t *request)
-{
-       char output[MAX_STRING_LEN];
-       MD5_PACKET *reply;
-
-       if (!username || !password || !packet)
-               return NULL;
-
-       reply = md5_alloc();
-       if (!reply) return NULL;
-       memset(output, 0, MAX_STRING_LEN);
-       reply->id = id;
-       
-       if (request) {
-               /* verify and issue Success/failure */
-               chap_challenge(packet->id, password->strvalue, password->length,
-                       request->value_name, request->value_size, output);
-
-               if (memcmp(output, packet->value, packet->value_size) != 0) {
-                       radlog(L_INFO, "rlm_eap_md5: Challenge failed");
-                       reply->code = PW_MD5_FAILURE;
-               }
-               else {
-                       reply->code = PW_MD5_SUCCESS;
-               }
-       } else {
-               /*
-                * Issue a challenge, value is a random number.
-                * If no value then generate some random number
-                */
-               chap_challenge(id, password->strvalue, password->length,
-                               packet->value, packet->value_size, output);
-               radlog(L_INFO, "rlm_eap_md5: Issuing Challenge to the user - %s",
-                       (char *)username->strvalue);
-               reply->code = PW_MD5_CHALLENGE;
-       }
-
-       /* fill reply packet */
-       if (reply->code == PW_MD5_CHALLENGE) {
-               reply->value_size = packet->value_size;
-               reply->value = malloc(reply->value_size);
-               if (reply->value == NULL) {
-                       radlog(L_ERR, "rlm_eap_md5: out of memory");
-                       md5_free(&reply);
-                       return NULL;
-               }
-               memcpy(reply->value, output, reply->value_size);
-               reply->length = packet->length;
-       } else {
-               reply->length = MD5_HEADER_LEN;
-       }
-       
-       return reply;
-}
 
 /*
- * If an EAP MD5 request needs to be initiated then 
- * create such a packet.
+ * verify = MD5(id+password+challenge_sent)
  */
-MD5_PACKET *md5_initial_request(EAP_DS *eap_ds)
+int eapmd5_verify(MD5_PACKET *packet, VALUE_PAIR* password,
+                 uint8_t *challenge)
 {
-       MD5_PACKET      *reply;
+       char    *ptr;
+       char    string[1 + MAX_STRING_LEN*2];
+       unsigned char output[MAX_STRING_LEN];
+       unsigned short len;
 
-
-       reply = md5_alloc();
-       if (reply == NULL)  {
-                radlog(L_ERR, "rlm_eap_md5: out of memory");
-               return NULL;
-       }
-
-       reply->code = PW_MD5_CHALLENGE;
-       reply->length = MD5_HEADER_LEN + 1 + MD5_LEN;
-       reply->value_size = MD5_LEN;
-
-       reply->value = malloc(reply->value_size);
-       if (reply->value == NULL) {
-               radlog(L_ERR, "rlm_eap_md5: out of memory");
-               md5_free(&reply);
-               return NULL;
+       /*
+        *      Sanity check it.
+        */
+       if (packet->value_size != 16) {
+               radlog(L_ERR, "rlm_eap_md5: Expected 16 bytes of response to challenge, got %d", packet->value_size);
+               return 0;
        }
 
-        /*
-         * generate some random challenge value
-        *
-        * TODO: Make sure Challenge is always unique,
-        *      no matter how many times it is called
-         */
-        librad_md5_calc((uint8_t *)reply->value, (uint8_t *)reply->value, MD5_LEN);
-
-       return reply;
-}
-
-/* 
- * challenge = MD5(id+password+MD5(random))
- */
-int chap_challenge(int id, char *password, int pass_len, char *challenge, int challenge_len, char *output)
-{
-        int             len;
-        char            *ptr;
-        char            string[MAX_STRING_LEN];
+       len = 0;
+       ptr = string;
 
-        if ((password == NULL) || (challenge == NULL)) {
-                return 0;
-        }
-
-        len = 0;
-        ptr = string;
-
-        *ptr++ = id;
-        len++;
-        memcpy(ptr, password, pass_len);
-        ptr += pass_len;
-        len += pass_len;
+       /*
+        *      This is really rad_chap_pwencode()...
+        */
+       *ptr++ = packet->id;
+       len++;
+       memcpy(ptr, password->vp_strvalue, password->length);
+       ptr += password->length;
+       len += password->length;
 
-       memcpy(ptr, challenge, challenge_len);
-       len += challenge_len;
+       /*
+        *      The challenge size is hard-coded.
+        */
+       memcpy(ptr, challenge, MD5_CHALLENGE_LEN);
+       len += MD5_CHALLENGE_LEN;
 
-        librad_md5_calc((u_char *)output, (u_char *)string, len);
+       fr_md5_calc((u_char *)output, (u_char *)string, len);
 
-        return 1;
+       /*
+        *      The length of the response is always 16 for MD5.
+        */
+       if (memcmp(output, packet->value, 16) != 0) {
+               return 0;
+       }
+       return 1;
 }
 
-/* 
- * compose the MD5 reply packet in the EAP reply typedata
+/*
+ *     Compose the portions of the reply packet specific to the
+ *     EAP-MD5 protocol, in the EAP reply typedata
  */
-int compose_md5(EAP_DS *eap_ds, MD5_PACKET *reply)
+int eapmd5_compose(EAP_DS *eap_ds, MD5_PACKET *reply)
 {
        uint8_t *ptr;
-       int name_len;
+       unsigned short name_len;
 
+       /*
+        *      We really only send Challenge (EAP-Identity),
+        *      and EAP-Success, and EAP-Failure.
+        */
        if (reply->code < 3) {
+               eap_ds->request->type.type = PW_EAP_MD5;
 
-               eap_ds->request->type = PW_EAP_MD5;
+               rad_assert(reply->length > 0);
 
-               eap_ds->request->typedata = malloc(reply->length - 4);
-               if (eap_ds->request->typedata == NULL) {
+               eap_ds->request->type.data = malloc(reply->length);
+               if (eap_ds->request->type.data == NULL) {
+                       eapmd5_free(&reply);
                        radlog(L_ERR, "rlm_eap_md5: out of memory");
                        return 0;
                }
-               ptr = eap_ds->request->typedata;
+               ptr = eap_ds->request->type.data;
                *ptr++ = (uint8_t)(reply->value_size & 0xFF);
                memcpy(ptr, reply->value, reply->value_size);
 
-               eap_ds->request->type_len = reply->value_size + 1;
+               /* Just the Challenge length */
+               eap_ds->request->type.length = reply->value_size + 1;
 
-               name_len = reply->length - (reply->value_size + 5);
-               if (reply->name  && name_len) {
+               /*
+                *      Return the name, if necessary.
+                *
+                *      Don't see why this is *ever* necessary...
+                */
+               name_len = reply->length - (reply->value_size + 1);
+               if (name_len && reply->name) {
                        ptr += reply->value_size;
                        memcpy(ptr, reply->name, name_len);
-                       eap_ds->request->type_len += name_len;
+                       /* Challenge length + Name length */
+                       eap_ds->request->type.length += name_len;
                }
-
        } else {
-               eap_ds->request->type_len = 0;
+               eap_ds->request->type.length = 0;
                /* TODO: In future we might add message here wrt rfc1994 */
        }
        eap_ds->request->code = reply->code;
 
+       eapmd5_free(&reply);
+
        return 1;
 }