summaryrefslogtreecommitdiffstats
path: root/src/modules/rlm_eap/types/rlm_eap_md5
diff options
context:
space:
mode:
Diffstat (limited to 'src/modules/rlm_eap/types/rlm_eap_md5')
-rw-r--r--src/modules/rlm_eap/types/rlm_eap_md5/README.md12
-rw-r--r--src/modules/rlm_eap/types/rlm_eap_md5/all.mk12
-rw-r--r--src/modules/rlm_eap/types/rlm_eap_md5/eap_md5.c229
-rw-r--r--src/modules/rlm_eap/types/rlm_eap_md5/eap_md5.h52
-rw-r--r--src/modules/rlm_eap/types/rlm_eap_md5/rlm_eap_md5.c168
5 files changed, 473 insertions, 0 deletions
diff --git a/src/modules/rlm_eap/types/rlm_eap_md5/README.md b/src/modules/rlm_eap/types/rlm_eap_md5/README.md
new file mode 100644
index 0000000..dba25cc
--- /dev/null
+++ b/src/modules/rlm_eap/types/rlm_eap_md5/README.md
@@ -0,0 +1,12 @@
+# rlm_eap_md5
+## Metadata
+<dl>
+ <dt>category</dt><dd>authentication</dd>
+</dl>
+
+## Summary
+Implements [RFC 3748](https://tools.ietf.org/html/rfc3748) EAP-MD5 authentication. EAP-MD5 allows EAP authentication
+using a plaintext password.
+
+Does not provide keying material for 802.11i, so cannot be used for WPA/2-Enterprise authentication unless wrapped
+in another method such as EAP-TTLS.
diff --git a/src/modules/rlm_eap/types/rlm_eap_md5/all.mk b/src/modules/rlm_eap/types/rlm_eap_md5/all.mk
new file mode 100644
index 0000000..528ee82
--- /dev/null
+++ b/src/modules/rlm_eap/types/rlm_eap_md5/all.mk
@@ -0,0 +1,12 @@
+TARGETNAME := rlm_eap_md5
+
+ifneq "$(TARGETNAME)" ""
+TARGET := $(TARGETNAME).a
+endif
+
+SOURCES := $(TARGETNAME).c eap_md5.c
+
+SRC_CFLAGS :=
+TGT_LDLIBS :=
+SRC_INCDIRS := ../../ ../../libeap/
+TGT_PREREQS := libfreeradius-eap.a
diff --git a/src/modules/rlm_eap/types/rlm_eap_md5/eap_md5.c b/src/modules/rlm_eap/types/rlm_eap_md5/eap_md5.c
new file mode 100644
index 0000000..e8acb5c
--- /dev/null
+++ b/src/modules/rlm_eap/types/rlm_eap_md5/eap_md5.c
@@ -0,0 +1,229 @@
+/*
+ * eap_md5.c EAP MD5 functionality.
+ *
+ * Version: $Id$
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * 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
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Value-Size | Value ...
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Name ...
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ */
+
+RCSID("$Id$")
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "eap.h"
+
+#include "eap_md5.h"
+#include <freeradius-devel/md5.h>
+
+/*
+ * 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;
+ 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.num != PW_EAP_MD5 ||
+ !eap_ds->response->type.data ||
+ (eap_ds->response->length <= MD5_HEADER_LEN) ||
+ (eap_ds->response->type.data[0] <= 0)) {
+ ERROR("rlm_eap_md5: corrupted data");
+ return NULL;
+ }
+
+ packet = talloc_zero(eap_ds, MD5_PACKET);
+ if (!packet) return NULL;
+
+ /*
+ * 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 - (MD5_HEADER_LEN + 1);
+
+ /*
+ * Sanity check the EAP-MD5 packet sent to us
+ * by the client.
+ */
+ data = (md5_packet_t *)eap_ds->response->type.data;
+
+ /*
+ * Already checked the size above.
+ */
+ packet->value_size = data->value_size;
+
+ /*
+ * Allocate room for the data, and copy over the data.
+ */
+ packet->value = talloc_array(packet, uint8_t, packet->value_size);
+ if (!packet->value) {
+ talloc_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, as eapmd5_compose()
+ */
+ name_len = packet->length - (packet->value_size + 1);
+ if (name_len) {
+ packet->name = talloc_array(packet, char, name_len + 1);
+ if (!packet->name) {
+ talloc_free(packet);
+ return NULL;
+ }
+ memcpy(packet->name, data->value_name + packet->value_size,
+ name_len);
+ packet->name[name_len] = 0;
+ }
+
+ return packet;
+}
+
+
+/*
+ * verify = MD5(id+password+challenge_sent)
+ */
+int eapmd5_verify(MD5_PACKET *packet, VALUE_PAIR* password,
+ uint8_t *challenge)
+{
+ char *ptr;
+ char string[1 + MAX_STRING_LEN*2];
+ uint8_t digest[16];
+ unsigned short len;
+
+ /*
+ * Sanity check it.
+ */
+ if (packet->value_size != 16) {
+ ERROR("rlm_eap_md5: Expected 16 bytes of response to challenge, got %d", packet->value_size);
+ return 0;
+ }
+
+ len = 0;
+ ptr = string;
+
+ /*
+ * This is really rad_chap_pwencode()...
+ */
+ *ptr++ = packet->id;
+ len++;
+ memcpy(ptr, password->vp_strvalue, password->vp_length);
+ ptr += password->vp_length;
+ len += password->vp_length;
+
+ /*
+ * The challenge size is hard-coded.
+ */
+ memcpy(ptr, challenge, MD5_CHALLENGE_LEN);
+ len += MD5_CHALLENGE_LEN;
+
+ fr_md5_calc(digest, (u_char *)string, len);
+
+ /*
+ * The length of the response is always 16 for MD5.
+ */
+ if (rad_digest_cmp(digest, packet->value, 16) != 0) {
+ DEBUG("EAP-MD5 digests do not match.");
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * Compose the portions of the reply packet specific to the
+ * EAP-MD5 protocol, in the EAP reply typedata
+ */
+int eapmd5_compose(EAP_DS *eap_ds, MD5_PACKET *reply)
+{
+ uint8_t *ptr;
+ 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.num = PW_EAP_MD5;
+
+ rad_assert(reply->length > 0);
+
+ eap_ds->request->type.data = talloc_array(eap_ds->request,
+ uint8_t,
+ reply->length);
+ if (!eap_ds->request->type.data) {
+ talloc_free(reply);
+ return 0;
+ }
+ ptr = eap_ds->request->type.data;
+ *ptr++ = (uint8_t)(reply->value_size & 0xFF);
+ memcpy(ptr, reply->value, reply->value_size);
+
+ /* Just the Challenge length */
+ eap_ds->request->type.length = reply->value_size + 1;
+
+ /*
+ * 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);
+ /* Challenge length + Name length */
+ eap_ds->request->type.length += name_len;
+ }
+ } else {
+ eap_ds->request->type.length = 0;
+ /* TODO: In future we might add message here wrt rfc1994 */
+ }
+ eap_ds->request->code = reply->code;
+ talloc_free(reply);
+
+ return 1;
+}
diff --git a/src/modules/rlm_eap/types/rlm_eap_md5/eap_md5.h b/src/modules/rlm_eap/types/rlm_eap_md5/eap_md5.h
new file mode 100644
index 0000000..aafa407
--- /dev/null
+++ b/src/modules/rlm_eap/types/rlm_eap_md5/eap_md5.h
@@ -0,0 +1,52 @@
+#ifndef _EAP_MD5_H
+#define _EAP_MD5_H
+
+RCSIDH(eap_md5_h, "$Id$")
+
+#include "eap.h"
+
+#define PW_MD5_CHALLENGE 1
+#define PW_MD5_RESPONSE 2
+#define PW_MD5_SUCCESS 3
+#define PW_MD5_FAILURE 4
+#define PW_MD5_MAX_CODES 4
+
+#define MD5_HEADER_LEN 4
+#define MD5_CHALLENGE_LEN 16
+
+/*
+ ****
+ * EAP - MD5 does not specify code, id & length but chap specifies them,
+ * for generalization purpose, complete header should be sent
+ * and not just value_size, value and name.
+ * future implementation.
+ *
+ * Huh? What does that mean?
+ */
+
+/* eap packet structure */
+typedef struct md5_packet_t {
+/*
+ uint8_t code;
+ uint8_t id;
+ uint16_t length;
+*/
+ uint8_t value_size;
+ uint8_t value_name[1];
+} md5_packet_t;
+
+typedef struct md5_packet {
+ unsigned char code;
+ unsigned char id;
+ unsigned short length;
+ unsigned char value_size;
+ unsigned char *value;
+ char *name;
+} MD5_PACKET;
+
+/* function declarations here */
+
+int eapmd5_compose(EAP_DS *auth, MD5_PACKET *reply);
+MD5_PACKET *eapmd5_extract(EAP_DS *auth);
+int eapmd5_verify(MD5_PACKET *pkt, VALUE_PAIR* pwd, uint8_t *ch);
+#endif /*_EAP_MD5_H*/
diff --git a/src/modules/rlm_eap/types/rlm_eap_md5/rlm_eap_md5.c b/src/modules/rlm_eap/types/rlm_eap_md5/rlm_eap_md5.c
new file mode 100644
index 0000000..2fa0077
--- /dev/null
+++ b/src/modules/rlm_eap/types/rlm_eap_md5/rlm_eap_md5.c
@@ -0,0 +1,168 @@
+/*
+ * rlm_eap_md5.c Handles that are called from eap
+ *
+ * Version: $Id$
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * Copyright 2000,2001,2006 The FreeRADIUS server project
+ * Copyright 2001 hereUare Communications, Inc. <raghud@hereuare.com>
+ */
+
+RCSID("$Id$")
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "eap_md5.h"
+
+#include <freeradius-devel/rad_assert.h>
+#include <freeradius-devel/md5.h>
+
+/*
+ * Initiate the EAP-MD5 session by sending a challenge to the peer.
+ */
+static int mod_session_init(UNUSED void *instance, eap_handler_t *handler)
+{
+ int i;
+ MD5_PACKET *reply;
+ REQUEST *request = handler->request;
+
+ /*
+ * Allocate an EAP-MD5 packet.
+ */
+ reply = talloc(handler, MD5_PACKET);
+ if (!reply) {
+ return 0;
+ }
+
+ /*
+ * Fill it with data.
+ */
+ reply->code = PW_MD5_CHALLENGE;
+ reply->length = 1 + MD5_CHALLENGE_LEN; /* one byte of value size */
+ reply->value_size = MD5_CHALLENGE_LEN;
+
+ /*
+ * Allocate user data.
+ */
+ reply->value = talloc_array(reply, uint8_t, reply->value_size);
+ if (!reply->value) {
+ talloc_free(reply);
+ return 0;
+ }
+
+ /*
+ * Get a random challenge.
+ */
+ for (i = 0; i < reply->value_size; i++) {
+ reply->value[i] = fr_rand();
+ }
+ RDEBUG2("Issuing MD5 Challenge");
+
+ /*
+ * Keep track of the challenge.
+ */
+ handler->opaque = talloc_array(handler, uint8_t, reply->value_size);
+ rad_assert(handler->opaque != NULL);
+ memcpy(handler->opaque, reply->value, reply->value_size);
+ handler->free_opaque = NULL;
+
+ /*
+ * Compose the EAP-MD5 packet out of the data structure,
+ * and free it.
+ */
+ eapmd5_compose(handler->eap_ds, reply);
+
+ /*
+ * We don't need to authorize the user at this point.
+ *
+ * We also don't need to keep the challenge, as it's
+ * stored in 'handler->eap_ds', which will be given back
+ * to us...
+ */
+ handler->stage = PROCESS;
+
+ return 1;
+}
+
+/*
+ * Authenticate a previously sent challenge.
+ */
+static int mod_process(UNUSED void *arg, eap_handler_t *handler)
+{
+ MD5_PACKET *packet;
+ MD5_PACKET *reply;
+ VALUE_PAIR *password;
+ REQUEST *request = handler->request;
+
+ /*
+ * Get the Cleartext-Password for this user.
+ */
+ rad_assert(handler->request != NULL);
+ rad_assert(handler->stage == PROCESS);
+
+ password = fr_pair_find_by_num(handler->request->config, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY);
+ if (!password) {
+ REDEBUG2("Cleartext-Password is required for EAP-MD5 authentication");
+ return 0;
+ }
+
+ /*
+ * Extract the EAP-MD5 packet.
+ */
+ if (!(packet = eapmd5_extract(handler->eap_ds)))
+ return 0;
+
+ /*
+ * Create a reply, and initialize it.
+ */
+ reply = talloc(packet, MD5_PACKET);
+ if (!reply) {
+ talloc_free(packet);
+ return 0;
+ }
+ reply->id = handler->eap_ds->request->id;
+ reply->length = 0;
+
+ /*
+ * Verify the received packet against the previous packet
+ * (i.e. challenge) which we sent out.
+ */
+ if (eapmd5_verify(packet, password, handler->opaque)) {
+ reply->code = PW_MD5_SUCCESS;
+ } else {
+ reply->code = PW_MD5_FAILURE;
+ }
+
+ /*
+ * Compose the EAP-MD5 packet out of the data structure,
+ * and free it.
+ */
+ eapmd5_compose(handler->eap_ds, reply);
+ talloc_free(packet);
+ return 1;
+}
+
+/*
+ * The module name should be the only globally exported symbol.
+ * That is, everything else should be 'static'.
+ */
+extern rlm_eap_module_t rlm_eap_md5;
+rlm_eap_module_t rlm_eap_md5 = {
+ .name = "eap_md5",
+ .session_init = mod_session_init, /* Initialise a new EAP session */
+ .process = mod_process /* Process next round of EAP method */
+};