changeset 360:2206c7f2945a

Added md5 code to check authenticator
author Sebastien Decugis <sdecugis@nict.go.jp>
date Wed, 20 May 2009 18:23:30 +0900
parents 9fd830f8621a
children fbe54f8d535e
files extensions/radius_gw/CMakeLists.txt extensions/radius_gw/md5.c extensions/radius_gw/md5.h.in extensions/radius_gw/radius_gw.h extensions/radius_gw/radius_gw_internal.h extensions/radius_gw/rgw_clients.c extensions/radius_gw/rgw_msg.c extensions/radius_gw/rgw_servers.c extensions/radius_gw/rgw_work.c
diffstat 9 files changed, 537 insertions(+), 21 deletions(-) [+]
line wrap: on
line diff
--- a/extensions/radius_gw/CMakeLists.txt	Wed May 20 15:19:14 2009 +0900
+++ b/extensions/radius_gw/CMakeLists.txt	Wed May 20 18:23:30 2009 +0900
@@ -11,6 +11,12 @@
 # Note : we should remove the STATIC here to avoid duplicating the code; but it is not tested yet.
 
 ########### Main radius_gw extension #############
+# md5
+INCLUDE (TestBigEndian)
+TEST_BIG_ENDIAN(HOST_BIG_ENDIAN)
+CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/md5.h.in ${CMAKE_CURRENT_BINARY_DIR}/md5.h)
+INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR})
+
 # Parser files
 BISON_FILE(radius_gw.y)
 FLEX_FILE(radius_gw.l)
@@ -27,6 +33,7 @@
 	rgw_servers.c
 	rgw_msg.c
 	rgw_work.c
+	md5.c
 )
 
 SET( RGW_DEFAULT_HEADER
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extensions/radius_gw/md5.c	Wed May 20 18:23:30 2009 +0900
@@ -0,0 +1,389 @@
+/* Copyright:
+ * The content from this file comes for the most part from the hostapd project.
+ * That project is licensed under GNU GPL v2 or BSD.
+ * We reproduce these bits here under the terms of the BSD license.
+ * A part of this file is also public domain.
+ */
+ 
+#include "md5.h"
+
+#include <string.h>
+
+
+/* forward declarations */
+void md5_vector(size_t num_elem, const uint8_t *addr[], const size_t *len, uint8_t *mac);
+
+/* the following might come from openssl also */
+struct MD5Context;
+void MD5Init(struct MD5Context *context);
+void MD5Update(struct MD5Context *context, unsigned char const *buf, unsigned len);
+void MD5Final(unsigned char digest[16], struct MD5Context *context);
+
+static void MD5Transform(uint32_t buf[4], uint32_t const in[16]);
+
+
+/**
+ * hmac_md5_vector - HMAC-MD5 over data vector (RFC 2104)
+ * @key: Key for HMAC operations
+ * @key_len: Length of the key in bytes
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash (16 bytes)
+ */
+void hmac_md5_vector(const uint8_t *key, size_t key_len, size_t num_elem, const uint8_t *addr[], const size_t *len, uint8_t *mac)
+{
+	uint8_t k_pad[64]; /* padding - key XORd with ipad/opad */
+	uint8_t tk[16];
+	const uint8_t *_addr[6];
+	size_t i, _len[6];
+
+	if (num_elem > 5) {
+		/*
+		 * Fixed limit on the number of fragments to avoid having to
+		 * allocate memory (which could fail).
+		 */
+		return;
+	}
+
+        /* if key is longer than 64 bytes reset it to key = MD5(key) */
+        if (key_len > 64) {
+		md5_vector(1, &key, &key_len, tk);
+		key = tk;
+		key_len = 16;
+        }
+
+	/* the HMAC_MD5 transform looks like:
+	 *
+	 * MD5(K XOR opad, MD5(K XOR ipad, text))
+	 *
+	 * where K is an n byte key
+	 * ipad is the byte 0x36 repeated 64 times
+	 * opad is the byte 0x5c repeated 64 times
+	 * and text is the data being protected */
+
+	/* start out by storing key in ipad */
+	memset(k_pad, 0, sizeof(k_pad));
+	memcpy(k_pad, key, key_len);
+
+	/* XOR key with ipad values */
+	for (i = 0; i < 64; i++)
+		k_pad[i] ^= 0x36;
+
+	/* perform inner MD5 */
+	_addr[0] = k_pad;
+	_len[0] = 64;
+	for (i = 0; i < num_elem; i++) {
+		_addr[i + 1] = addr[i];
+		_len[i + 1] = len[i];
+	}
+	md5_vector(1 + num_elem, _addr, _len, mac);
+
+	memset(k_pad, 0, sizeof(k_pad));
+	memcpy(k_pad, key, key_len);
+	/* XOR key with opad values */
+	for (i = 0; i < 64; i++)
+		k_pad[i] ^= 0x5c;
+
+	/* perform outer MD5 */
+	_addr[0] = k_pad;
+	_len[0] = 64;
+	_addr[1] = mac;
+	_len[1] = MD5_MAC_LEN;
+	md5_vector(2, _addr, _len, mac);
+}
+
+
+/**
+ * hmac_md5 - HMAC-MD5 over data buffer (RFC 2104)
+ * @key: Key for HMAC operations
+ * @key_len: Length of the key in bytes
+ * @data: Pointers to the data area
+ * @data_len: Length of the data area
+ * @mac: Buffer for the hash (16 bytes)
+ */
+void hmac_md5(const uint8_t *key, size_t key_len, const uint8_t *data, size_t data_len, uint8_t *mac)
+{
+	hmac_md5_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+
+struct MD5Context {
+	uint32_t buf[4];
+	uint32_t bits[2];
+	uint8_t in[64];
+};
+
+
+
+typedef struct MD5Context MD5_CTX;
+
+
+/**
+ * md5_vector - MD5 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ */
+void md5_vector(size_t num_elem, const uint8_t *addr[], const size_t *len, uint8_t *mac)
+{
+	MD5_CTX ctx;
+	size_t i;
+
+	MD5Init(&ctx);
+	for (i = 0; i < num_elem; i++)
+		MD5Update(&ctx, addr[i], len[i]);
+	MD5Final(mac, &ctx);
+}
+
+
+
+
+
+
+
+/* ===== start - public domain MD5 implementation ===== */
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest.  This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+
+#ifndef HOST_BIG_ENDIAN
+#define byteReverse(buf, len)	/* Nothing */
+#else
+/*
+ * Note: this code is harmless on little-endian machines.
+ */
+static void byteReverse(unsigned char *buf, unsigned longs)
+{
+    uint32_t t;
+    do {
+	t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
+	    ((unsigned) buf[1] << 8 | buf[0]);
+	*(uint32_t *) buf = t;
+	buf += 4;
+    } while (--longs);
+}
+#endif
+
+/*
+ * Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+void MD5Init(struct MD5Context *ctx)
+{
+    ctx->buf[0] = 0x67452301;
+    ctx->buf[1] = 0xefcdab89;
+    ctx->buf[2] = 0x98badcfe;
+    ctx->buf[3] = 0x10325476;
+
+    ctx->bits[0] = 0;
+    ctx->bits[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len)
+{
+    uint32_t t;
+
+    /* Update bitcount */
+
+    t = ctx->bits[0];
+    if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t)
+	ctx->bits[1]++;		/* Carry from low to high */
+    ctx->bits[1] += len >> 29;
+
+    t = (t >> 3) & 0x3f;	/* Bytes already in shsInfo->data */
+
+    /* Handle any leading odd-sized chunks */
+
+    if (t) {
+	unsigned char *p = (unsigned char *) ctx->in + t;
+
+	t = 64 - t;
+	if (len < t) {
+	    memcpy(p, buf, len);
+	    return;
+	}
+	memcpy(p, buf, t);
+	byteReverse(ctx->in, 16);
+	MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+	buf += t;
+	len -= t;
+    }
+    /* Process data in 64-byte chunks */
+
+    while (len >= 64) {
+	memcpy(ctx->in, buf, 64);
+	byteReverse(ctx->in, 16);
+	MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+	buf += 64;
+	len -= 64;
+    }
+
+    /* Handle any remaining bytes of data. */
+
+    memcpy(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+void MD5Final(unsigned char digest[16], struct MD5Context *ctx)
+{
+    unsigned count;
+    unsigned char *p;
+
+    /* Compute number of bytes mod 64 */
+    count = (ctx->bits[0] >> 3) & 0x3F;
+
+    /* Set the first char of padding to 0x80.  This is safe since there is
+       always at least one byte free */
+    p = ctx->in + count;
+    *p++ = 0x80;
+
+    /* Bytes of padding needed to make 64 bytes */
+    count = 64 - 1 - count;
+
+    /* Pad out to 56 mod 64 */
+    if (count < 8) {
+	/* Two lots of padding:  Pad the first block to 64 bytes */
+	memset(p, 0, count);
+	byteReverse(ctx->in, 16);
+	MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+
+	/* Now fill the next block with 56 bytes */
+	memset(ctx->in, 0, 56);
+    } else {
+	/* Pad block to 56 bytes */
+	memset(p, 0, count - 8);
+    }
+    byteReverse(ctx->in, 14);
+
+    /* Append length in bits and transform */
+    ((uint32_t *) ctx->in)[14] = ctx->bits[0];
+    ((uint32_t *) ctx->in)[15] = ctx->bits[1];
+
+    MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+    byteReverse((unsigned char *) ctx->buf, 4);
+    memcpy(digest, ctx->buf, 16);
+    memset(ctx, 0, sizeof(ctx));	/* In case it's sensitive */
+}
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+	( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data.  MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+static void MD5Transform(uint32_t buf[4], uint32_t const in[16])
+{
+    register uint32_t a, b, c, d;
+
+    a = buf[0];
+    b = buf[1];
+    c = buf[2];
+    d = buf[3];
+
+    MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+    MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+    MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+    MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+    MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+    MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+    MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+    MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+    MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+    MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+    MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+    MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+    MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+    MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+    MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+    MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+    MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+    MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+    MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+    MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+    MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+    MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+    MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+    MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+    MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+    MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+    MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+    MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+    MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+    MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+    MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+    MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+    MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+    MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+    MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+    MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+    MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+    MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+    MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+    MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+    MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+    MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+    MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+    MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+    MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+    MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+    MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+    MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+    MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+    MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+    MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+    MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+    MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+    MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+    MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+    MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+    MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+    MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+    MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+    MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+    MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+    MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+    MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+    MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+    buf[0] += a;
+    buf[1] += b;
+    buf[2] += c;
+    buf[3] += d;
+}
+/* ===== end - public domain MD5 implementation ===== */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extensions/radius_gw/md5.h.in	Wed May 20 18:23:30 2009 +0900
@@ -0,0 +1,16 @@
+/* See Copyright note in md5.c */
+
+#ifndef _MD5_H
+#define _MD5_H
+
+#include <sys/types.h>
+#include <stdint.h>
+
+#cmakedefine HOST_BIG_ENDIAN @HOST_BIG_ENDIAN@
+
+/* MD5 stuff for authenticators */
+#define MD5_MAC_LEN 16
+void hmac_md5_vector(const uint8_t *key, size_t key_len, size_t num_elem, const uint8_t *addr[], const size_t *len, uint8_t *mac);
+void hmac_md5(const uint8_t *key, size_t key_len, const uint8_t *data, size_t data_len, uint8_t *mac);
+
+#endif /* _MD5_H */
--- a/extensions/radius_gw/radius_gw.h	Wed May 20 15:19:14 2009 +0900
+++ b/extensions/radius_gw/radius_gw.h	Wed May 20 18:23:30 2009 +0900
@@ -43,6 +43,8 @@
 #include <stdint.h>	/* uint8_t, etc... */
 #include <waaad/waaad.h> /* session_t, etc... */
 
+#include "md5.h"
+
 /* This type is used for all lists in this extension */
 struct rg_list {
 	struct rg_list *next;
@@ -54,6 +56,11 @@
 /*                  RADIUS messages                           */
 /**************************************************************/
 
+#define RADIUS_MAX_MSG_LEN 3000
+
+#define RADIUS_ATTR_MESSAGE_AUTHENTICATOR	80
+
+
 /* Note on design: the parsing of RADIUS message is not very efficient since we have to duplicate the memory of all
  attributes, instead of pointing back to them in the original message. Anyway, it makes adding / removing attributes simpler. */
 
--- a/extensions/radius_gw/radius_gw_internal.h	Wed May 20 15:19:14 2009 +0900
+++ b/extensions/radius_gw/radius_gw_internal.h	Wed May 20 18:23:30 2009 +0900
@@ -81,6 +81,9 @@
 /* The clients allowed to connect to these servers */
 int rgw_clients_init(void);
 int rgw_clients_add( struct sockaddr * ip_port, unsigned char ** key, size_t keylen );
+int rgw_clients_getkey(void * cli, unsigned char **key, size_t *key_len);
+int rgw_clients_search(struct sockaddr * ip_port, void ** ref);
+void rgw_clients_dispose(void ** ref);
 void rgw_clients_dump(void);
 void rgw_clients_fini(void);
 
@@ -105,7 +108,7 @@
 
 /* Worker module that handle incoming RADIUS messages */
 int rgw_work_start(void);
-int rgw_work_add(rad_t * msg, void * client);
+int rgw_work_add(rad_t * msg, void * client, uint16_t port);
 void rgw_work_fini(void);
 
 
--- a/extensions/radius_gw/rgw_clients.c	Wed May 20 15:19:14 2009 +0900
+++ b/extensions/radius_gw/rgw_clients.c	Wed May 20 18:23:30 2009 +0900
@@ -217,6 +217,16 @@
 	}
 }
 
+int rgw_clients_getkey(void * cli, unsigned char **key, size_t *key_len)
+{
+	struct cli_info * cliinfo = (struct cli_info *) cli;
+	
+	CHECK_PARAMS( cli && key && key_len );
+	*key = cliinfo->key.data;
+	*key_len = cliinfo->key.len;
+	return 0;
+}
+
 int rgw_clients_search(struct sockaddr * ip_port, void ** ref)
 {
 	int ret = 0;
--- a/extensions/radius_gw/rgw_msg.c	Wed May 20 15:19:14 2009 +0900
+++ b/extensions/radius_gw/rgw_msg.c	Wed May 20 18:23:30 2009 +0900
@@ -135,11 +135,11 @@
 	return 0;
 }
 
+/* Alloc new buffer if *len == 0 on entry */
 int rgw_msg_gen(rad_t *msg, unsigned char **buf, size_t *len)
 {
 	size_t mylen;
 	struct rg_list * li;
-	unsigned char * mybuf = NULL;
 	struct radius_hdr * hdr;
 	struct radius_attr_hdr *attr;
 	
@@ -155,11 +155,17 @@
 	
 	CHECK_PARAMS( mylen < (1<<16) );
 	
-	CHECK_MALLOC( mybuf = malloc(mylen) );
-	memset(mybuf, 0, mylen);
+	if (*len && (*len < mylen)) {
+		TRACE_DEBUG(INFO, "Buffer too short");
+		return ENOSPC;
+	}
+	if (*len == 0) {
+		CHECK_MALLOC( *buf = malloc(mylen) );
+	}
+	memset(*buf, 0, mylen);
 	
 	/* Now write the header */
-	hdr = (struct radius_hdr *)mybuf;
+	hdr = (struct radius_hdr *)*buf;
 	hdr->code = msg->code;
 	hdr->identifier = msg->identifier;
 	hdr->length = htons( (unsigned short)mylen );
@@ -180,8 +186,8 @@
 	}
 	
 	/* Done */
-	*buf = mybuf;
 	*len = mylen;
+	
 	return 0;
 }
 
--- a/extensions/radius_gw/rgw_servers.c	Wed May 20 15:19:14 2009 +0900
+++ b/extensions/radius_gw/rgw_servers.c	Wed May 20 18:23:30 2009 +0900
@@ -37,8 +37,6 @@
 
 #include "radius_gw_internal.h"
 
-#define RADIUS_MAX_MSG_LEN 3000
-
 
 /* Declare the rgw_servers */
 struct rgw_servs rgw_servers;
@@ -109,22 +107,19 @@
 	/* Now loop on this socket, parse and queue each message received, until thread is cancelled. */
 	while (1) {
 		struct sockaddr_storage from;
-		unsigned char * buf = NULL;
 		socklen_t fromlen = sizeof(from);
 		int len;
 		rad_t * msg = NULL;
 		void * nas_info = NULL;
+		uint16_t port;
+		unsigned char buf[RADIUS_MAX_MSG_LEN];
 		
 		pthread_testcancel();
 		
-		CHECK_MALLOC_DO( buf = malloc(RADIUS_MAX_MSG_LEN), break );
-		memset(buf, 0, RADIUS_MAX_MSG_LEN);
-		
 		/* read the next message */
-		CHECK_SYS_DO( len = recvfrom( me->sock, buf, RADIUS_MAX_MSG_LEN, 0, (struct sockaddr *) &from, &fromlen),  break );
+		CHECK_SYS_DO( len = recvfrom( me->sock, &buf[0], sizeof(buf), 0, (struct sockaddr *) &from, &fromlen),  break );
 		{
 			char ipstr[INET6_ADDRSTRLEN];
-			uint16_t port;
 			
 			switch (from.ss_family) {
 				case AF_INET:
@@ -143,17 +138,16 @@
 			TRACE_DEBUG(FULL, "Received %d bytes from [%s]:%hu", len, ipstr, ntohs(port));
 		}
 		
+		if (!port)
+			continue;
+		
 		/* parse the message or loop if message is bad */
-		CHECK_FCT_DO( rgw_msg_parse(buf, len, &msg), 
+		CHECK_FCT_DO( rgw_msg_parse(&buf[0], len, &msg), 
 			{ 
 				TRACE_DEBUG(INFO, "Discarding invalid RADIUS message");
-				free(buf); 
 				continue; 
 			} );
 		
-		/* Free the buffer, we don't need it anymore */
-		free(buf);
-		
 		if (TRACE_BOOL(FULL))
 			rg_msg_dump(FULL, msg);
 		
@@ -167,7 +161,7 @@
 				
 		
 		/* queue the message for a worker thread */
-		CHECK_FCT_DO( rgw_work_add(msg, nas_info), break );
+		CHECK_FCT_DO( rgw_work_add(msg, nas_info, port), break );
 		
 		/* Then wait for next incoming message */
 	}
--- a/extensions/radius_gw/rgw_work.c	Wed May 20 15:19:14 2009 +0900
+++ b/extensions/radius_gw/rgw_work.c	Wed May 20 18:23:30 2009 +0900
@@ -49,6 +49,7 @@
 	struct rg_list chain;
 	rad_t * msg;
 	void * cli;
+	uint16_t port;
 };
 
 static pthread_t workers[NB_WORKERS];
@@ -57,7 +58,78 @@
 {
 	CHECK_POSIX_DO( pthread_mutex_unlock((pthread_mutex_t *)mtx), );
 }
+
+/* This function is strongly inspired from radius_msg_verify_msg_auth in hostap package (BSD licensed) */
+static int work_auth_check(rad_t * rad, void * cli, unsigned char * req_auth)
+{
+	unsigned char buf[RADIUS_MAX_MSG_LEN];
+	unsigned char * key;
+	size_t key_len;
+	struct rg_list * attr_li;
+	struct rad_attr * msg_mac = NULL;
+	unsigned char msg_mac_save[MD5_MAC_LEN];
+	unsigned char msg_mac_calc[MD5_MAC_LEN];
+	unsigned char msg_auth_orig[16];
+	size_t buflen = RADIUS_MAX_MSG_LEN;
 	
+	TRACE_ENTRY("%p %p", rad, cli);
+	
+	CHECK_FCT( rgw_clients_getkey(cli, &key, &key_len) );
+	
+	memset(buf, 0, sizeof(buf));
+	
+	/* Find the authenticator attribute */
+	for (attr_li = rad->attributes.next; attr_li != &rad->attributes; attr_li = attr_li->next) {
+		struct rad_attr * attr = (struct rad_attr *)attr_li;
+		
+		if (attr->type == RADIUS_ATTR_MESSAGE_AUTHENTICATOR) {
+			if (msg_mac != NULL) {
+				TRACE_DEBUG(INFO, "RADIUS message with several Message-Authenticator attributes, invalid.");
+				return EINVAL;
+			}
+			msg_mac = attr;
+		}
+	}
+	
+	if (msg_mac == NULL) {
+		TRACE_DEBUG(INFO, "RADIUS message without Message-Authenticator attribute, invalid.");
+		return EINVAL;
+	}
+	
+	/* Save and clear the authenticator value */
+	if (msg_mac->length != (2 + sizeof(msg_mac_save))) {
+		TRACE_DEBUG(INFO, "Unexpected size of Message-Authenticator attribute, discarding.");
+		return EINVAL;
+	}
+	memcpy( &msg_mac_save[0], &msg_mac->data.buf[0], sizeof(msg_mac_save) );
+	memset( &msg_mac->data.buf[0], 0, sizeof(msg_mac_save) );
+	if (req_auth) {
+		/* compute the mac with the authenticator from the request */
+		memcpy( &msg_auth_orig[0], &rad->authenticator[0], 16 );
+		memcpy( &rad->authenticator[0], req_auth, 16 );
+	}
+	
+	/* Recreate a false message to compute the md5 sum. This is very bad design! Optimization possible here... */
+	CHECK_FCT( rgw_msg_gen(rad, (unsigned char **)&buf, &buflen) );
+	
+	/* Now compute the MAC with our known shared key */
+	hmac_md5( key, key_len, &buf[0], buflen, &msg_mac_calc[0] );
+	
+	/* Restore the original msg */
+	memcpy( &msg_mac->data.buf[0], &msg_mac_save[0], sizeof(msg_mac_save) );
+	if (req_auth) {
+		/* compute the mac with the authenticator from the request */
+		memcpy( &rad->authenticator[0], &msg_auth_orig[0], 16 );
+	}
+	
+	/* Finally, check if the MAC is correct (validates the common shared secret) */
+	if (memcmp(&msg_mac_save[0], &msg_mac_calc[0], sizeof(msg_mac_save) )) {
+		TRACE_DEBUG(INFO, "Invalid Message-Authenticator attribute, discarding.");
+		return EINVAL;
+	}
+	
+	return 0;
+}	
 
 static void * work_th(void * arg)
 {
@@ -72,6 +144,7 @@
 		
 		rad_t * msg;
 		void * cli;
+		uint16_t port;
 		struct work_item * wi;
 	
 		/* Pick the next message */
@@ -93,6 +166,7 @@
 		rg_list_unlink(&wi->chain);
 		msg = wi->msg;
 		cli = wi->cli;
+		port = wi->port;
 		free(wi);
 		
 		/* Release the mutex */
@@ -104,7 +178,16 @@
 		/* process the data */
 		
 		/* Check authenticator */
+		CHECK_FCT_DO( work_auth_check(msg, cli, NULL),
+			{
+				/* Invalid authenticator, discard message */
+				TRACE_DEBUG(INFO, "Discarding message with invalid authenticator");
+				rg_msg_free(msg);
+				rgw_clients_dispose(&cli);
+				continue;
+			}  );
 		
+		TRACE_DEBUG(FULL, "Message-Authenticator is valid");
 		
 		
 	}
@@ -129,7 +212,7 @@
 	return 0;
 }
 
-int rgw_work_add(rad_t * msg, void * client)
+int rgw_work_add(rad_t * msg, void * client, uint16_t port)
 {
 	struct work_item * new;
 	
@@ -139,6 +222,7 @@
 	
 	new->msg = msg;
 	new->cli = client;
+	new->port = port;
 	
 	CHECK_POSIX( pthread_mutex_lock(&work_mtx) );
 	rg_list_insert_before(&work_data, &new->chain);
"Welcome to our mercurial repository"