# HG changeset patch # User Sebastien Decugis # Date 1271237422 -32400 # Node ID a857024cb48ba87fe8da9f2aa4f9149ad51762b1 # Parent ad6c0118fb50ca133b9da3fa1e0f2b345f7863b9 Ported the RADIUS/Diameter translation code from waaad project. Not tested yet. Gateway plugins to come later. diff -r ad6c0118fb50 -r a857024cb48b doc/app_radgw.conf.sample --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/app_radgw.conf.sample Wed Apr 14 18:30:22 2010 +0900 @@ -0,0 +1,89 @@ +# This file contains information for configuring the app_radgw extension. +# To find how to have freeDiameter load this extension, please refer to the freeDiameter documentation. +# +# The app_radgw extension allows a freeDiameter agent to serve as a +# RADIUS/Diameter gateway. Typically, a RADIUS client (e.g. a NAS) will connect to +# this agent, and the message will be converted to Diameter and sent to a Diameter server. +# +# Note that this extension does not provide a fully functionnal RADIUS/Diameter gateway. +# You need to load plugins to handle specific RADIUS messages and convert them to +# Diameter apps such as NASREQ, EAP, ... See the next section for information. + + +########### +# PLUGINS # +########### + +# Additional plugins must be loaded to support specific RADIUS messages and attributes. + +# Plugins are registered either for every message, or by port (auth or acct), or by port and code. +# The general format is: +# RGWX = plugin [: conf_file] [: port] [: code(s)] ; +# Where: +# plugin is the quoted file name (relative or absolute) of the plugin to load (.rgwx files). +# conf_file (optional) is the quoted name of the configuration file. +# port (optional), either auth or acct. +# If not specified, extension is called for messages incoming on both ports +# code(s): space-separated list of command codes for which this extension must be called. +# If not specified, the extension is called for all incoming messages. +# The values are interpreted as hexadecimal. +# +# The plugins are called in the order they appear in this file. +# Here are some explained examples: +# RGWX = "3579.rgwx"; Load this extension and call it for all messages. No configuration file. +# RGWX = "3579.rgwx" : "3579.conf"; Same as previous but with a configuration file specified. +# RGWX = "3579.rgwx" : auth; No configuration file, but called only for RADIUS messages received on authentication port. +# RGWX = "3579.rgwx" : 4 8 b; Called for messages with command code 4, 8, or 11 only. +# RGWX = "3579.rgwx" : "3579.conf" : auth : 4 8 b; All parameters combined. + +# Once the list of extensions for an incoming message has been called (or if the list is empty), +# an error is logged if some RADIUS attributes of the message have not been handled. + + +################## +# RADIUS Clients # +################## + +# Each RADIUS client must be declared in the form: cli = IP / shared-secret ; +# IP can be ipv4 or ipv6 +# port can be additionaly restricted with brackets: IP[port] (ex: 192.168.0.1[1812]) +# shared-secret can be a quoted string, or a list of hexadecimal values. +# examples: +# cli = 192.168.100.1 / "secret key" ; # the shared secret buffer is 0x736563726574206b6579 (length 10 bytes) +# cli = fe00::1 / 73 65 63 72 65 74 20 6b 65 79; # same shared secret as previously +# When a packet is received from an IP not declared here, it is discarded. + + +#################### +# Authentication # +# Authorization # +#################### + +# Enable the RADIUS/Diameter authentication/authorization gateway? +# auth_server_enable = 1; + +# The port on which the accounting server listens +# auth_server_port = 1812; + +# The IPv4 on which to bind the server, or "disable" if IPv4 must not be used. +# auth_server_ip4 = 0.0.0.0; + +# The IPv6 address to which the server is bound, or "disable" +# auth_server_ip6 = :: ; + + +################ +# Accounting # +################ + +# Enable the RADIUS/Diameter accounting gateway? +# acct_server_enable = 1; + +# The port on which the accounting server listens +# acct_server_port = 1813; + +# The IPv4 on which to bind the server, or "disable" if no IPv4 is wanted. +# acct_server_ip4 = 0.0.0.0; + +# The IPv6 address to which the server is bound, or "disable" +# acct_server_ip6 = :: ; diff -r ad6c0118fb50 -r a857024cb48b extensions/CMakeLists.txt --- a/extensions/CMakeLists.txt Tue Apr 13 14:50:54 2010 +0900 +++ b/extensions/CMakeLists.txt Wed Apr 14 18:30:22 2010 +0900 @@ -26,6 +26,7 @@ #### # Diameter applications dictionary + OPTION(BUILD_DICT_NASREQ "Build NASREQ (RFC4005) Dictionary definitions?" ON) IF (BUILD_DICT_NASREQ) SUBDIRS(dict_nasreq) @@ -39,11 +40,13 @@ #### -# Radius / Diameter gateway extension(s) -# OPTION(BUILD_RADIUS_GW "Build radius_gw? (one-way RADIUS/Diameter gateway - RADIUS NAS <-> Diameter server)" ON) -# IF (BUILD_RADIUS_GW) -# SUBDIRS(radius_gw) -# ENDIF (BUILD_RADIUS_GW) +# Diameter applications + +OPTION(BUILD_APP_RADGW "Build app_radgw? (one-way RADIUS/Diameter gateway - RADIUS NAS <-> Diameter server)" OFF) + IF (BUILD_APP_RADGW) + SUBDIRS(app_radgw) + ENDIF (BUILD_APP_RADGW) + #### @@ -64,8 +67,9 @@ ENDIF (BUILD_ACL_WL) + #### -# Debug / test extensions +# Debug & test extensions OPTION(BUILD_SAMPLE "Build sample.fdx? (Simple extension to demonstrate extension mechanism, for developpers only)" OFF) IF (BUILD_SAMPLE) diff -r ad6c0118fb50 -r a857024cb48b extensions/app_radgw/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/app_radgw/CMakeLists.txt Wed Apr 14 18:30:22 2010 +0900 @@ -0,0 +1,106 @@ +# The app_radgw extension +PROJECT("RADIUS/Diameter extensible gateway application for freeDiameter" C) + + +# Overwrite the debug level for the extension code if configured +OPTION(DEBUG_LEVEL_APP_RADGW "Overwrite debug level for the extension app_radgw if defined" OFF) +IF (DEBUG_LEVEL_APP_RADGW) + ADD_DEFINITIONS(-DTRACE_LEVEL=${DEBUG_LEVEL_APP_RADGW}) +ENDIF (DEBUG_LEVEL_APP_RADGW) + + +########### Utility library ############# +# utility libray for the extension and its plugins +# See rgw_common.h for detail + +SET( RG_COMMON_SRC + radius.c + md5.c +) +SET( RG_COMMON_HEADER + rgw_common.h + radius.h + md5.h + hostap_compat.h +) +ADD_LIBRARY(rgw_common ${RG_COMMON_SRC}) + + + +########### Main app_radgw extension ############# + +# Parser files +BISON_FILE(rgw_conf.y) +FLEX_FILE(rgw_conf.l) +SET_SOURCE_FILES_PROPERTIES(lex.rgw_conf.c rgw_conf.tab.c PROPERTIES COMPILE_FLAGS "-I ${CMAKE_CURRENT_SOURCE_DIR}") + +# List of source files +SET( RGW_DEFAULT_SRC + rgw_main.c + lex.rgw_conf.c + rgw_conf.tab.c + rgw_conf.tab.h + rgw_clients.c + rgw_plugins.c + rgw_msg.c + rgw_servers.c + rgw_worker.c +) + +# Compile these files as a freeDiameter extension. +FD_ADD_EXTENSION(app_radgw ${RGW_DEFAULT_SRC} ${RG_COMMON_HEADER}) +TARGET_LINK_LIBRARIES(app_radgw rgw_common) + + + + + + + + + + + + +########### Sub extensions ############# +# Example of support extension: +# OPTION(BUILD_RADIUS_GW_SAMPLE "Build sample sub-extension? (for debug only)" OFF) +# IF (BUILD_RADIUS_GW_SAMPLE) +# ADD_LIBRARY(sub_sample MODULE ${RG_COMMON_HEADER} sub_sample.c) +# TARGET_LINK_LIBRARIES(sub_sample rg_common) +# ENDIF (BUILD_RADIUS_GW_SAMPLE) + +# OPTION(BUILD_SUB_DEBUG "Build debug sub-extension? (display status of RADIUS and Diameter messages)" ON) +# IF (BUILD_SUB_DEBUG) +# ADD_LIBRARY(sub_debug MODULE ${RG_COMMON_HEADER} sub_debug.c) +# TARGET_LINK_LIBRARIES(sub_debug rg_common) +# ENDIF (BUILD_SUB_DEBUG) +# +# OPTION(BUILD_SUB_ECHO_DROP "Build 'echo/drop' sub-extension? (echo or drop specific RADIUS attributes, no Diameter translation)" ON) +# IF (BUILD_SUB_ECHO_DROP) +# ADD_DEFINITIONS(-DSUB_ECHO_DROP_VERBO=2) +# BISON_FILE(sub_echo_drop.y) +# FLEX_FILE(sub_echo_drop.l) +# SET_SOURCE_FILES_PROPERTIES(lex.sub_echo_drop.c sub_echo_drop.tab.c PROPERTIES COMPILE_FLAGS "-I ${CMAKE_CURRENT_SOURCE_DIR}") +# ADD_LIBRARY(sub_echo_drop MODULE ${RG_COMMON_HEADER} +# sub_echo_drop.c +# sub_echo_drop.h +# lex.sub_echo_drop.c +# sub_echo_drop.tab.c +# sub_echo_drop.tab.h) +# TARGET_LINK_LIBRARIES(sub_echo_drop rg_common) +# ENDIF (BUILD_SUB_ECHO_DROP) +# +# OPTION(BUILD_SUB_AUTH "Build RADIUS Authentication & Authorization sub-extension? (RFC2865, RFC3579)" ON) +# IF (BUILD_SUB_AUTH) +# ADD_DEFINITIONS(-DSUB_AUTH_VERBO=2) +# ADD_LIBRARY(sub_auth MODULE ${RG_COMMON_HEADER} sub_auth.c) +# TARGET_LINK_LIBRARIES(sub_auth rg_common) +# ENDIF (BUILD_SUB_AUTH) +# +# OPTION(BUILD_SUB_ACCT "Build RADIUS Accounting sub-extension? (RFC2866)" ON) +# IF (BUILD_SUB_ACCT) +# ADD_DEFINITIONS(-DSUB_ACCT_VERBO=2) +# ADD_LIBRARY(sub_acct MODULE ${RG_COMMON_HEADER} sub_acct.c) +# TARGET_LINK_LIBRARIES(sub_acct rg_common) +# ENDIF (BUILD_SUB_ACCT) diff -r ad6c0118fb50 -r a857024cb48b extensions/app_radgw/hostap_compat.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/app_radgw/hostap_compat.h Wed Apr 14 18:30:22 2010 +0900 @@ -0,0 +1,194 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis * +* * +* Copyright (c) 2009, WIDE Project and NICT * +* All rights reserved. * +* * +* Redistribution and use of this software in source and binary forms, with or without modification, are * +* permitted provided that the following conditions are met: * +* * +* * Redistributions of source code must retain the above * +* copyright notice, this list of conditions and the * +* following disclaimer. * +* * +* * Redistributions in binary form must reproduce the above * +* copyright notice, this list of conditions and the * +* following disclaimer in the documentation and/or other * +* materials provided with the distribution. * +* * +* * Neither the name of the WIDE Project or NICT nor the * +* names of its contributors may be used to endorse or * +* promote products derived from this software without * +* specific prior written permission of WIDE Project and * +* NICT. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED * +* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * +* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * +* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * +* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +*********************************************************************************************************/ + +/* This file contains compatibility bindings for hostap files. + Most of the definitions here come from different files from the hostap project. + + We don't care for OS-specific definitions since we are only compatible with POSIX systems. + + */ + +#ifndef _HOSTAP_COMPAT_H +#define _HOSTAP_COMPAT_H + +#include + +typedef uint64_t u64; +typedef uint32_t u32; +typedef uint16_t u16; +typedef uint8_t u8; +typedef int64_t s64; +typedef int32_t s32; +typedef int16_t s16; +typedef int8_t s8; + +/* md5.c uses a different macro name than freeDiameter for endianness */ +#ifdef HOST_BIG_ENDIAN +# define WORDS_BIGENDIAN HOST_BIG_ENDIAN +#else /* HOST_BIG_ENDIAN */ +# undef WORDS_BIGENDIAN +#endif /* HOST_BIG_ENDIAN */ + +/* freeDiameter uses the POSIX API, so we don't provide alternatives. This may be changed later as needed */ +#define os_malloc(s) malloc((s)) +#define os_realloc(p, s) realloc((p), (s)) +#define os_free(p) free((p)) + +#define os_memcpy(d, s, n) memcpy((d), (s), (n)) +#define os_memmove(d, s, n) memmove((d), (s), (n)) +#define os_memset(s, c, n) memset(s, c, n) +#define os_memcmp(s1, s2, n) memcmp((s1), (s2), (n)) + +#define os_strdup(s) strdup(s) +#define os_strlen(s) strlen(s) +#define os_strcasecmp(s1, s2) strcasecmp((s1), (s2)) +#define os_strncasecmp(s1, s2, n) strncasecmp((s1), (s2), (n)) +#define os_strchr(s, c) strchr((s), (c)) +#define os_strcmp(s1, s2) strcmp((s1), (s2)) +#define os_strncmp(s1, s2, n) strncmp((s1), (s2), (n)) +#define os_strncpy(d, s, n) strncpy((d), (s), (n)) +#define os_strrchr(s, c) strrchr((s), (c)) +#define os_strstr(h, n) strstr((h), (n)) +#define os_snprintf snprintf + +#define os_random() random() + +static __inline__ void * os_zalloc(size_t size) +{ + void *n = os_malloc(size); + if (n) + os_memset(n, 0, size); + return n; +} + +typedef long os_time_t; +struct os_time { + os_time_t sec; + os_time_t usec; +}; + +static __inline__ int os_get_time(struct os_time *t) +{ + int res; + struct timeval tv; + res = gettimeofday(&tv, NULL); + t->sec = tv.tv_sec; + t->usec = tv.tv_usec; + return res; +} + +/* Macros for handling unaligned memory accesses */ +#define WPA_GET_BE16(a) ((u16) (((a)[0] << 8) | (a)[1])) +#define WPA_PUT_BE16(a, val) \ + do { \ + (a)[0] = ((u16) (val)) >> 8; \ + (a)[1] = ((u16) (val)) & 0xff; \ + } while (0) + +#define WPA_GET_LE16(a) ((u16) (((a)[1] << 8) | (a)[0])) +#define WPA_PUT_LE16(a, val) \ + do { \ + (a)[1] = ((u16) (val)) >> 8; \ + (a)[0] = ((u16) (val)) & 0xff; \ + } while (0) + +#define WPA_GET_BE24(a) ((((u32) (a)[0]) << 16) | (((u32) (a)[1]) << 8) | \ + ((u32) (a)[2])) +#define WPA_PUT_BE24(a, val) \ + do { \ + (a)[0] = (u8) ((((u32) (val)) >> 16) & 0xff); \ + (a)[1] = (u8) ((((u32) (val)) >> 8) & 0xff); \ + (a)[2] = (u8) (((u32) (val)) & 0xff); \ + } while (0) + +#define WPA_GET_BE32(a) ((((u32) (a)[0]) << 24) | (((u32) (a)[1]) << 16) | \ + (((u32) (a)[2]) << 8) | ((u32) (a)[3])) +#define WPA_PUT_BE32(a, val) \ + do { \ + (a)[0] = (u8) ((((u32) (val)) >> 24) & 0xff); \ + (a)[1] = (u8) ((((u32) (val)) >> 16) & 0xff); \ + (a)[2] = (u8) ((((u32) (val)) >> 8) & 0xff); \ + (a)[3] = (u8) (((u32) (val)) & 0xff); \ + } while (0) + +#define WPA_GET_LE32(a) ((((u32) (a)[3]) << 24) | (((u32) (a)[2]) << 16) | \ + (((u32) (a)[1]) << 8) | ((u32) (a)[0])) +#define WPA_PUT_LE32(a, val) \ + do { \ + (a)[3] = (u8) ((((u32) (val)) >> 24) & 0xff); \ + (a)[2] = (u8) ((((u32) (val)) >> 16) & 0xff); \ + (a)[1] = (u8) ((((u32) (val)) >> 8) & 0xff); \ + (a)[0] = (u8) (((u32) (val)) & 0xff); \ + } while (0) + +#define WPA_GET_BE64(a) ((((u64) (a)[0]) << 56) | (((u64) (a)[1]) << 48) | \ + (((u64) (a)[2]) << 40) | (((u64) (a)[3]) << 32) | \ + (((u64) (a)[4]) << 24) | (((u64) (a)[5]) << 16) | \ + (((u64) (a)[6]) << 8) | ((u64) (a)[7])) +#define WPA_PUT_BE64(a, val) \ + do { \ + (a)[0] = (u8) (((u64) (val)) >> 56); \ + (a)[1] = (u8) (((u64) (val)) >> 48); \ + (a)[2] = (u8) (((u64) (val)) >> 40); \ + (a)[3] = (u8) (((u64) (val)) >> 32); \ + (a)[4] = (u8) (((u64) (val)) >> 24); \ + (a)[5] = (u8) (((u64) (val)) >> 16); \ + (a)[6] = (u8) (((u64) (val)) >> 8); \ + (a)[7] = (u8) (((u64) (val)) & 0xff); \ + } while (0) + +#define WPA_GET_LE64(a) ((((u64) (a)[7]) << 56) | (((u64) (a)[6]) << 48) | \ + (((u64) (a)[5]) << 40) | (((u64) (a)[4]) << 32) | \ + (((u64) (a)[3]) << 24) | (((u64) (a)[2]) << 16) | \ + (((u64) (a)[1]) << 8) | ((u64) (a)[0])) + + +/* Packing structures to avoid padding */ +#ifdef __GNUC__ +#define STRUCT_PACKED __attribute__ ((packed)) +#else +#define STRUCT_PACKED +#endif + +/* For md5.c file */ +#define INTERNAL_MD5 +#define CONFIG_CRYPTO_INTERNAL + +/* For radius.c file */ +#define CONFIG_IPV6 +#include + + +#endif /* _HOSTAP_COMPAT_H */ diff -r ad6c0118fb50 -r a857024cb48b extensions/app_radgw/md5.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/app_radgw/md5.c Wed Apr 14 18:30:22 2010 +0900 @@ -0,0 +1,401 @@ +/*********************************************************************************/ +/* freeDiameter author note: + * The content from this file comes directly from the hostap project. + * It is redistributed under the terms of the BSD license, as allowed + * by the original copyright reproduced bellow. + * In addition to this notice, only the #include directives have been modified. + */ +#include "rgw_common.h" +/* Forward declaration: */ +void md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); +/*********************************************************************************/ + + +/* + * MD5 hash implementation and interface functions + * Copyright (c) 2003-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + + +/** + * 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 u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + u8 k_pad[64]; /* padding - key XORd with ipad/opad */ + u8 tk[16]; + const u8 *_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 */ + os_memset(k_pad, 0, sizeof(k_pad)); + os_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); + + os_memset(k_pad, 0, sizeof(k_pad)); + os_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 u8 *key, size_t key_len, const u8 *data, size_t data_len, + u8 *mac) +{ + hmac_md5_vector(key, key_len, 1, &data, &data_len, mac); +} + + +#ifdef INTERNAL_MD5 + +struct MD5Context { + u32 buf[4]; + u32 bits[2]; + u8 in[64]; +}; + +#ifndef CONFIG_CRYPTO_INTERNAL +static void MD5Init(struct MD5Context *context); +static void MD5Update(struct MD5Context *context, unsigned char const *buf, + unsigned len); +static void MD5Final(unsigned char digest[16], struct MD5Context *context); +#endif /* CONFIG_CRYPTO_INTERNAL */ +static void MD5Transform(u32 buf[4], u32 const in[16]); + + +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 u8 *addr[], const size_t *len, u8 *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 WORDS_BIGENDIAN +#define byteReverse(buf, len) /* Nothing */ +#else +/* + * Note: this code is harmless on little-endian machines. + */ +static void byteReverse(unsigned char *buf, unsigned longs) +{ + u32 t; + do { + t = (u32) ((unsigned) buf[3] << 8 | buf[2]) << 16 | + ((unsigned) buf[1] << 8 | buf[0]); + *(u32 *) 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) +{ + u32 t; + + /* Update bitcount */ + + t = ctx->bits[0]; + if ((ctx->bits[0] = t + ((u32) 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) { + os_memcpy(p, buf, len); + return; + } + os_memcpy(p, buf, t); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (u32 *) ctx->in); + buf += t; + len -= t; + } + /* Process data in 64-byte chunks */ + + while (len >= 64) { + os_memcpy(ctx->in, buf, 64); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (u32 *) ctx->in); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + + os_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 */ + os_memset(p, 0, count); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (u32 *) ctx->in); + + /* Now fill the next block with 56 bytes */ + os_memset(ctx->in, 0, 56); + } else { + /* Pad block to 56 bytes */ + os_memset(p, 0, count - 8); + } + byteReverse(ctx->in, 14); + + /* Append length in bits and transform */ + ((u32 *) ctx->in)[14] = ctx->bits[0]; + ((u32 *) ctx->in)[15] = ctx->bits[1]; + + MD5Transform(ctx->buf, (u32 *) ctx->in); + byteReverse((unsigned char *) ctx->buf, 4); + os_memcpy(digest, ctx->buf, 16); + os_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<>(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(u32 buf[4], u32 const in[16]) +{ + register u32 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 ===== */ + +#endif /* INTERNAL_MD5 */ diff -r ad6c0118fb50 -r a857024cb48b extensions/app_radgw/md5.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/app_radgw/md5.h Wed Apr 14 18:30:22 2010 +0900 @@ -0,0 +1,43 @@ +/*********************************************************************************/ +/* freeDiameter author note: + * The content from this file comes directly from the hostap project. + * It is redistributed under the terms of the BSD license, as allowed + * by the original copyright reproduced bellow. + * The file has not been modified, except for this notice. + */ +/*********************************************************************************/ + +/* + * MD5 hash implementation and interface functions + * Copyright (c) 2003-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef MD5_H +#define MD5_H + +#define MD5_MAC_LEN 16 + +void hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac); +void hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len, + u8 *mac); + +#ifdef CONFIG_CRYPTO_INTERNAL +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); +#endif /* CONFIG_CRYPTO_INTERNAL */ + +#endif /* MD5_H */ diff -r ad6c0118fb50 -r a857024cb48b extensions/app_radgw/radius.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/app_radgw/radius.c Wed Apr 14 18:30:22 2010 +0900 @@ -0,0 +1,1282 @@ +/*********************************************************************************/ +/* freeDiameter author note: + * The content from this file comes directly from the hostap project. + * It is redistributed under the terms of the BSD license, as allowed + * by the original copyright reproduced bellow. + * In addition to this notice, only the #include directives have been modified. + */ +#include "rgw_common.h" + +/* Overwrite printf */ +#define printf(args...) fd_log_debug(args) + +/*********************************************************************************/ + + +/* + * hostapd / RADIUS message processing + * Copyright (c) 2002-2008, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +static struct radius_attr_hdr * +radius_get_attr_hdr(struct radius_msg *msg, int idx) +{ + return (struct radius_attr_hdr *) (msg->buf + msg->attr_pos[idx]); +} + + +struct radius_msg *radius_msg_new(u8 code, u8 identifier) +{ + struct radius_msg *msg; + + msg = os_malloc(sizeof(*msg)); + if (msg == NULL) + return NULL; + + if (radius_msg_initialize(msg, RADIUS_DEFAULT_MSG_SIZE)) { + os_free(msg); + return NULL; + } + + radius_msg_set_hdr(msg, code, identifier); + + return msg; +} + + +int radius_msg_initialize(struct radius_msg *msg, size_t init_len) +{ + if (msg == NULL || init_len < sizeof(struct radius_hdr)) + return -1; + + os_memset(msg, 0, sizeof(*msg)); + msg->buf = os_zalloc(init_len); + if (msg->buf == NULL) + return -1; + + msg->buf_size = init_len; + msg->hdr = (struct radius_hdr *) msg->buf; + msg->buf_used = sizeof(*msg->hdr); + + msg->attr_pos = + os_zalloc(RADIUS_DEFAULT_ATTR_COUNT * sizeof(*msg->attr_pos)); + if (msg->attr_pos == NULL) { + os_free(msg->buf); + msg->buf = NULL; + msg->hdr = NULL; + return -1; + } + + msg->attr_size = RADIUS_DEFAULT_ATTR_COUNT; + msg->attr_used = 0; + + return 0; +} + + +void radius_msg_set_hdr(struct radius_msg *msg, u8 code, u8 identifier) +{ + msg->hdr->code = code; + msg->hdr->identifier = identifier; +} + + +void radius_msg_free(struct radius_msg *msg) +{ + os_free(msg->buf); + msg->buf = NULL; + msg->hdr = NULL; + msg->buf_size = msg->buf_used = 0; + + os_free(msg->attr_pos); + msg->attr_pos = NULL; + msg->attr_size = msg->attr_used = 0; +} + + +static const char *radius_code_string(u8 code) +{ + switch (code) { + case RADIUS_CODE_ACCESS_REQUEST: return "Access-Request"; + case RADIUS_CODE_ACCESS_ACCEPT: return "Access-Accept"; + case RADIUS_CODE_ACCESS_REJECT: return "Access-Reject"; + case RADIUS_CODE_ACCOUNTING_REQUEST: return "Accounting-Request"; + case RADIUS_CODE_ACCOUNTING_RESPONSE: return "Accounting-Response"; + case RADIUS_CODE_ACCESS_CHALLENGE: return "Access-Challenge"; + case RADIUS_CODE_STATUS_SERVER: return "Status-Server"; + case RADIUS_CODE_STATUS_CLIENT: return "Status-Client"; + case RADIUS_CODE_RESERVED: return "Reserved"; + default: return "?Unknown?"; + } +} + + +struct radius_attr_type { + u8 type; + char *name; + enum { + RADIUS_ATTR_UNDIST, RADIUS_ATTR_TEXT, RADIUS_ATTR_IP, + RADIUS_ATTR_HEXDUMP, RADIUS_ATTR_INT32, RADIUS_ATTR_IPV6 + } data_type; +}; + +static struct radius_attr_type radius_attrs[] = +{ + { RADIUS_ATTR_USER_NAME, "User-Name", RADIUS_ATTR_TEXT }, + { RADIUS_ATTR_USER_PASSWORD, "User-Password", RADIUS_ATTR_UNDIST }, + { RADIUS_ATTR_NAS_IP_ADDRESS, "NAS-IP-Address", RADIUS_ATTR_IP }, + { RADIUS_ATTR_NAS_PORT, "NAS-Port", RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_FRAMED_MTU, "Framed-MTU", RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_REPLY_MESSAGE, "Reply-Message", RADIUS_ATTR_TEXT }, + { RADIUS_ATTR_STATE, "State", RADIUS_ATTR_UNDIST }, + { RADIUS_ATTR_CLASS, "Class", RADIUS_ATTR_UNDIST }, + { RADIUS_ATTR_VENDOR_SPECIFIC, "Vendor-Specific", RADIUS_ATTR_UNDIST }, + { RADIUS_ATTR_SESSION_TIMEOUT, "Session-Timeout", RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_IDLE_TIMEOUT, "Idle-Timeout", RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_TERMINATION_ACTION, "Termination-Action", + RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_CALLED_STATION_ID, "Called-Station-Id", + RADIUS_ATTR_TEXT }, + { RADIUS_ATTR_CALLING_STATION_ID, "Calling-Station-Id", + RADIUS_ATTR_TEXT }, + { RADIUS_ATTR_NAS_IDENTIFIER, "NAS-Identifier", RADIUS_ATTR_TEXT }, + { RADIUS_ATTR_PROXY_STATE, "Proxy-State", RADIUS_ATTR_UNDIST }, + { RADIUS_ATTR_ACCT_STATUS_TYPE, "Acct-Status-Type", + RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_ACCT_DELAY_TIME, "Acct-Delay-Time", RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_ACCT_INPUT_OCTETS, "Acct-Input-Octets", + RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_ACCT_OUTPUT_OCTETS, "Acct-Output-Octets", + RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_ACCT_SESSION_ID, "Acct-Session-Id", RADIUS_ATTR_TEXT }, + { RADIUS_ATTR_ACCT_AUTHENTIC, "Acct-Authentic", RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_ACCT_SESSION_TIME, "Acct-Session-Time", + RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_ACCT_INPUT_PACKETS, "Acct-Input-Packets", + RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_ACCT_OUTPUT_PACKETS, "Acct-Output-Packets", + RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_ACCT_TERMINATE_CAUSE, "Acct-Terminate-Cause", + RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_ACCT_MULTI_SESSION_ID, "Acct-Multi-Session-Id", + RADIUS_ATTR_TEXT }, + { RADIUS_ATTR_ACCT_LINK_COUNT, "Acct-Link-Count", RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_ACCT_INPUT_GIGAWORDS, "Acct-Input-Gigawords", + RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS, "Acct-Output-Gigawords", + RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_EVENT_TIMESTAMP, "Event-Timestamp", + RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_NAS_PORT_TYPE, "NAS-Port-Type", RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_TUNNEL_TYPE, "Tunnel-Type", RADIUS_ATTR_HEXDUMP }, + { RADIUS_ATTR_TUNNEL_MEDIUM_TYPE, "Tunnel-Medium-Type", + RADIUS_ATTR_HEXDUMP }, + { RADIUS_ATTR_CONNECT_INFO, "Connect-Info", RADIUS_ATTR_TEXT }, + { RADIUS_ATTR_EAP_MESSAGE, "EAP-Message", RADIUS_ATTR_UNDIST }, + { RADIUS_ATTR_MESSAGE_AUTHENTICATOR, "Message-Authenticator", + RADIUS_ATTR_UNDIST }, + { RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID, "Tunnel-Private-Group-Id", + RADIUS_ATTR_HEXDUMP }, + { RADIUS_ATTR_ACCT_INTERIM_INTERVAL, "Acct-Interim-Interval", + RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, "Chargeable-User-Identity", + RADIUS_ATTR_TEXT }, + { RADIUS_ATTR_NAS_IPV6_ADDRESS, "NAS-IPv6-Address", RADIUS_ATTR_IPV6 }, +}; +#define RADIUS_ATTRS (sizeof(radius_attrs) / sizeof(radius_attrs[0])) + + +static struct radius_attr_type *radius_get_attr_type(u8 type) +{ + size_t i; + + for (i = 0; i < RADIUS_ATTRS; i++) { + if (type == radius_attrs[i].type) + return &radius_attrs[i]; + } + + return NULL; +} + + +static void print_char(char c) +{ + if (c >= 32 && c < 127) + printf("%c", c); + else + printf("<%02x>", c); +} + + +static void radius_msg_dump_attr(struct radius_attr_hdr *hdr) +{ + struct radius_attr_type *attr; + int i, len; + unsigned char *pos; + + attr = radius_get_attr_type(hdr->type); + + printf(" Attribute %d (%s) length=%d\n", + hdr->type, attr ? attr->name : "?Unknown?", hdr->length); + + if (attr == NULL) + return; + + len = hdr->length - sizeof(struct radius_attr_hdr); + pos = (unsigned char *) (hdr + 1); + + switch (attr->data_type) { + case RADIUS_ATTR_TEXT: + printf(" Value: '"); + for (i = 0; i < len; i++) + print_char(pos[i]); + printf("'\n"); + break; + + case RADIUS_ATTR_IP: + if (len == 4) { + struct in_addr addr; + os_memcpy(&addr, pos, 4); + printf(" Value: %s\n", inet_ntoa(addr)); + } else + printf(" Invalid IP address length %d\n", len); + break; + +#ifdef CONFIG_IPV6 + case RADIUS_ATTR_IPV6: + if (len == 16) { + char buf[128]; + const char *atxt; + struct in6_addr *addr = (struct in6_addr *) pos; + atxt = inet_ntop(AF_INET6, addr, buf, sizeof(buf)); + printf(" Value: %s\n", atxt ? atxt : "?"); + } else + printf(" Invalid IPv6 address length %d\n", len); + break; +#endif /* CONFIG_IPV6 */ + + case RADIUS_ATTR_HEXDUMP: + case RADIUS_ATTR_UNDIST: + printf(" Value:"); + for (i = 0; i < len; i++) + printf(" %02x", pos[i]); + printf("\n"); + break; + + case RADIUS_ATTR_INT32: + if (len == 4) + printf(" Value: %u\n", WPA_GET_BE32(pos)); + else + printf(" Invalid INT32 length %d\n", len); + break; + + default: + break; + } +} + + +void radius_msg_dump(struct radius_msg *msg) +{ + size_t i; + + printf("RADIUS message: code=%d (%s) identifier=%d length=%d\n", + msg->hdr->code, radius_code_string(msg->hdr->code), + msg->hdr->identifier, ntohs(msg->hdr->length)); + + for (i = 0; i < msg->attr_used; i++) { + struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i); + radius_msg_dump_attr(attr); + } +} + + +int radius_msg_finish(struct radius_msg *msg, const u8 *secret, + size_t secret_len) +{ + if (secret) { + u8 auth[MD5_MAC_LEN]; + struct radius_attr_hdr *attr; + + os_memset(auth, 0, MD5_MAC_LEN); + attr = radius_msg_add_attr(msg, + RADIUS_ATTR_MESSAGE_AUTHENTICATOR, + auth, MD5_MAC_LEN); + if (attr == NULL) { + printf("WARNING: Could not add " + "Message-Authenticator\n"); + return -1; + } + msg->hdr->length = htons(msg->buf_used); + hmac_md5(secret, secret_len, msg->buf, msg->buf_used, + (u8 *) (attr + 1)); + } else + msg->hdr->length = htons(msg->buf_used); + + if (msg->buf_used > 0xffff) { + printf("WARNING: too long RADIUS message (%lu)\n", + (unsigned long) msg->buf_used); + return -1; + } + return 0; +} + + +int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret, + size_t secret_len, const u8 *req_authenticator) +{ + u8 auth[MD5_MAC_LEN]; + struct radius_attr_hdr *attr; + const u8 *addr[4]; + size_t len[4]; + + os_memset(auth, 0, MD5_MAC_LEN); + attr = radius_msg_add_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, + auth, MD5_MAC_LEN); + if (attr == NULL) { + printf("WARNING: Could not add Message-Authenticator\n"); + return -1; + } + msg->hdr->length = htons(msg->buf_used); + os_memcpy(msg->hdr->authenticator, req_authenticator, + sizeof(msg->hdr->authenticator)); + hmac_md5(secret, secret_len, msg->buf, msg->buf_used, + (u8 *) (attr + 1)); + + /* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */ + addr[0] = (u8 *) msg->hdr; + len[0] = 1 + 1 + 2; + addr[1] = req_authenticator; + len[1] = MD5_MAC_LEN; + addr[2] = (u8 *) (msg->hdr + 1); + len[2] = msg->buf_used - sizeof(*msg->hdr); + addr[3] = secret; + len[3] = secret_len; + md5_vector(4, addr, len, msg->hdr->authenticator); + + if (msg->buf_used > 0xffff) { + printf("WARNING: too long RADIUS message (%lu)\n", + (unsigned long) msg->buf_used); + return -1; + } + return 0; +} + + +void radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret, + size_t secret_len) +{ + const u8 *addr[2]; + size_t len[2]; + + msg->hdr->length = htons(msg->buf_used); + os_memset(msg->hdr->authenticator, 0, MD5_MAC_LEN); + addr[0] = msg->buf; + len[0] = msg->buf_used; + addr[1] = secret; + len[1] = secret_len; + md5_vector(2, addr, len, msg->hdr->authenticator); + + if (msg->buf_used > 0xffff) { + printf("WARNING: too long RADIUS messages (%lu)\n", + (unsigned long) msg->buf_used); + } +} + + +static int radius_msg_add_attr_to_array(struct radius_msg *msg, + struct radius_attr_hdr *attr) +{ + if (msg->attr_used >= msg->attr_size) { + size_t *nattr_pos; + int nlen = msg->attr_size * 2; + + nattr_pos = os_realloc(msg->attr_pos, + nlen * sizeof(*msg->attr_pos)); + if (nattr_pos == NULL) + return -1; + + msg->attr_pos = nattr_pos; + msg->attr_size = nlen; + } + + msg->attr_pos[msg->attr_used++] = (unsigned char *) attr - msg->buf; + + return 0; +} + + +struct radius_attr_hdr *radius_msg_add_attr(struct radius_msg *msg, u8 type, + const u8 *data, size_t data_len) +{ + size_t buf_needed; + struct radius_attr_hdr *attr; + + if (data_len > RADIUS_MAX_ATTR_LEN) { + printf("radius_msg_add_attr: too long attribute (%lu bytes)\n", + (unsigned long) data_len); + return NULL; + } + + buf_needed = msg->buf_used + sizeof(*attr) + data_len; + + if (msg->buf_size < buf_needed) { + /* allocate more space for message buffer */ + unsigned char *nbuf; + size_t nlen = msg->buf_size; + + while (nlen < buf_needed) + nlen *= 2; + nbuf = os_realloc(msg->buf, nlen); + if (nbuf == NULL) + return NULL; + msg->buf = nbuf; + msg->hdr = (struct radius_hdr *) msg->buf; + os_memset(msg->buf + msg->buf_size, 0, nlen - msg->buf_size); + msg->buf_size = nlen; + } + + attr = (struct radius_attr_hdr *) (msg->buf + msg->buf_used); + attr->type = type; + attr->length = sizeof(*attr) + data_len; + if (data_len > 0) + os_memcpy(attr + 1, data, data_len); + + msg->buf_used += sizeof(*attr) + data_len; + + if (radius_msg_add_attr_to_array(msg, attr)) + return NULL; + + return attr; +} + + +struct radius_msg *radius_msg_parse(const u8 *data, size_t len) +{ + struct radius_msg *msg; + struct radius_hdr *hdr; + struct radius_attr_hdr *attr; + size_t msg_len; + unsigned char *pos, *end; + + if (data == NULL || len < sizeof(*hdr)) + return NULL; + + hdr = (struct radius_hdr *) data; + + msg_len = ntohs(hdr->length); + if (msg_len < sizeof(*hdr) || msg_len > len) { + printf("Invalid RADIUS message length\n"); + return NULL; + } + + if (msg_len < len) { + printf("Ignored %lu extra bytes after RADIUS message\n", + (unsigned long) len - msg_len); + } + + msg = os_malloc(sizeof(*msg)); + if (msg == NULL) + return NULL; + + if (radius_msg_initialize(msg, msg_len)) { + os_free(msg); + return NULL; + } + + os_memcpy(msg->buf, data, msg_len); + msg->buf_size = msg->buf_used = msg_len; + + /* parse attributes */ + pos = (unsigned char *) (msg->hdr + 1); + end = msg->buf + msg->buf_used; + while (pos < end) { + if ((size_t) (end - pos) < sizeof(*attr)) + goto fail; + + attr = (struct radius_attr_hdr *) pos; + + if (pos + attr->length > end || attr->length < sizeof(*attr)) + goto fail; + + /* TODO: check that attr->length is suitable for attr->type */ + + if (radius_msg_add_attr_to_array(msg, attr)) + goto fail; + + pos += attr->length; + } + + return msg; + + fail: + radius_msg_free(msg); + os_free(msg); + return NULL; +} + + +int radius_msg_add_eap(struct radius_msg *msg, const u8 *data, size_t data_len) +{ + const u8 *pos = data; + size_t left = data_len; + + while (left > 0) { + int len; + if (left > RADIUS_MAX_ATTR_LEN) + len = RADIUS_MAX_ATTR_LEN; + else + len = left; + + if (!radius_msg_add_attr(msg, RADIUS_ATTR_EAP_MESSAGE, + pos, len)) + return 0; + + pos += len; + left -= len; + } + + return 1; +} + + +u8 *radius_msg_get_eap(struct radius_msg *msg, size_t *eap_len) +{ + u8 *eap, *pos; + size_t len, i; + struct radius_attr_hdr *attr; + + if (msg == NULL) + return NULL; + + len = 0; + for (i = 0; i < msg->attr_used; i++) { + attr = radius_get_attr_hdr(msg, i); + if (attr->type == RADIUS_ATTR_EAP_MESSAGE) + len += attr->length - sizeof(struct radius_attr_hdr); + } + + if (len == 0) + return NULL; + + eap = os_malloc(len); + if (eap == NULL) + return NULL; + + pos = eap; + for (i = 0; i < msg->attr_used; i++) { + attr = radius_get_attr_hdr(msg, i); + if (attr->type == RADIUS_ATTR_EAP_MESSAGE) { + int flen = attr->length - sizeof(*attr); + os_memcpy(pos, attr + 1, flen); + pos += flen; + } + } + + if (eap_len) + *eap_len = len; + + return eap; +} + + +int radius_msg_verify_msg_auth(struct radius_msg *msg, const u8 *secret, + size_t secret_len, const u8 *req_auth) +{ + u8 auth[MD5_MAC_LEN], orig[MD5_MAC_LEN]; + u8 orig_authenticator[16]; + struct radius_attr_hdr *attr = NULL, *tmp; + size_t i; + + for (i = 0; i < msg->attr_used; i++) { + tmp = radius_get_attr_hdr(msg, i); + if (tmp->type == RADIUS_ATTR_MESSAGE_AUTHENTICATOR) { + if (attr != NULL) { + printf("Multiple Message-Authenticator " + "attributes in RADIUS message\n"); + return 1; + } + attr = tmp; + } + } + + if (attr == NULL) { + printf("No Message-Authenticator attribute found\n"); + return 1; + } + + os_memcpy(orig, attr + 1, MD5_MAC_LEN); + os_memset(attr + 1, 0, MD5_MAC_LEN); + if (req_auth) { + os_memcpy(orig_authenticator, msg->hdr->authenticator, + sizeof(orig_authenticator)); + os_memcpy(msg->hdr->authenticator, req_auth, + sizeof(msg->hdr->authenticator)); + } + hmac_md5(secret, secret_len, msg->buf, msg->buf_used, auth); + os_memcpy(attr + 1, orig, MD5_MAC_LEN); + if (req_auth) { + os_memcpy(msg->hdr->authenticator, orig_authenticator, + sizeof(orig_authenticator)); + } + + if (os_memcmp(orig, auth, MD5_MAC_LEN) != 0) { + printf("Invalid Message-Authenticator!\n"); + return 1; + } + + return 0; +} + + +int radius_msg_verify(struct radius_msg *msg, const u8 *secret, + size_t secret_len, struct radius_msg *sent_msg, int auth) +{ + const u8 *addr[4]; + size_t len[4]; + u8 hash[MD5_MAC_LEN]; + + if (sent_msg == NULL) { + printf("No matching Access-Request message found\n"); + return 1; + } + + if (auth && + radius_msg_verify_msg_auth(msg, secret, secret_len, + sent_msg->hdr->authenticator)) { + return 1; + } + + /* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */ + addr[0] = (u8 *) msg->hdr; + len[0] = 1 + 1 + 2; + addr[1] = sent_msg->hdr->authenticator; + len[1] = MD5_MAC_LEN; + addr[2] = (u8 *) (msg->hdr + 1); + len[2] = msg->buf_used - sizeof(*msg->hdr); + addr[3] = secret; + len[3] = secret_len; + md5_vector(4, addr, len, hash); + if (os_memcmp(hash, msg->hdr->authenticator, MD5_MAC_LEN) != 0) { + printf("Response Authenticator invalid!\n"); + return 1; + } + + return 0; +} + + +int radius_msg_copy_attr(struct radius_msg *dst, struct radius_msg *src, + u8 type) +{ + struct radius_attr_hdr *attr; + size_t i; + int count = 0; + + for (i = 0; i < src->attr_used; i++) { + attr = radius_get_attr_hdr(src, i); + if (attr->type == type) { + if (!radius_msg_add_attr(dst, type, (u8 *) (attr + 1), + attr->length - sizeof(*attr))) + return -1; + count++; + } + } + + return count; +} + + +/* Create Request Authenticator. The value should be unique over the lifetime + * of the shared secret between authenticator and authentication server. + * Use one-way MD5 hash calculated from current timestamp and some data given + * by the caller. */ +void radius_msg_make_authenticator(struct radius_msg *msg, + const u8 *data, size_t len) +{ + struct os_time tv; + long int l; + const u8 *addr[3]; + size_t elen[3]; + + os_get_time(&tv); + l = os_random(); + addr[0] = (u8 *) &tv; + elen[0] = sizeof(tv); + addr[1] = data; + elen[1] = len; + addr[2] = (u8 *) &l; + elen[2] = sizeof(l); + md5_vector(3, addr, elen, msg->hdr->authenticator); +} + + +/* Get Vendor-specific RADIUS Attribute from a parsed RADIUS message. + * Returns the Attribute payload and sets alen to indicate the length of the + * payload if a vendor attribute with subtype is found, otherwise returns NULL. + * The returned payload is allocated with os_malloc() and caller must free it + * by calling os_free(). + */ +static u8 *radius_msg_get_vendor_attr(struct radius_msg *msg, u32 vendor, + u8 subtype, size_t *alen) +{ + u8 *data, *pos; + size_t i, len; + + if (msg == NULL) + return NULL; + + for (i = 0; i < msg->attr_used; i++) { + struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i); + size_t left; + u32 vendor_id; + struct radius_attr_vendor *vhdr; + + if (attr->type != RADIUS_ATTR_VENDOR_SPECIFIC) + continue; + + left = attr->length - sizeof(*attr); + if (left < 4) + continue; + + pos = (u8 *) (attr + 1); + + os_memcpy(&vendor_id, pos, 4); + pos += 4; + left -= 4; + + if (ntohl(vendor_id) != vendor) + continue; + + while (left >= sizeof(*vhdr)) { + vhdr = (struct radius_attr_vendor *) pos; + if (vhdr->vendor_length > left || + vhdr->vendor_length < sizeof(*vhdr)) { + left = 0; + break; + } + if (vhdr->vendor_type != subtype) { + pos += vhdr->vendor_length; + left -= vhdr->vendor_length; + continue; + } + + len = vhdr->vendor_length - sizeof(*vhdr); + data = os_malloc(len); + if (data == NULL) + return NULL; + os_memcpy(data, pos + sizeof(*vhdr), len); + if (alen) + *alen = len; + return data; + } + } + + return NULL; +} + + +static u8 * decrypt_ms_key(const u8 *key, size_t len, + const u8 *req_authenticator, + const u8 *secret, size_t secret_len, size_t *reslen) +{ + u8 *plain, *ppos, *res; + const u8 *pos; + size_t left, plen; + u8 hash[MD5_MAC_LEN]; + int i, first = 1; + const u8 *addr[3]; + size_t elen[3]; + + /* key: 16-bit salt followed by encrypted key info */ + + if (len < 2 + 16) + return NULL; + + pos = key + 2; + left = len - 2; + if (left % 16) { + printf("Invalid ms key len %lu\n", (unsigned long) left); + return NULL; + } + + plen = left; + ppos = plain = os_malloc(plen); + if (plain == NULL) + return NULL; + plain[0] = 0; + + while (left > 0) { + /* b(1) = MD5(Secret + Request-Authenticator + Salt) + * b(i) = MD5(Secret + c(i - 1)) for i > 1 */ + + addr[0] = secret; + elen[0] = secret_len; + if (first) { + addr[1] = req_authenticator; + elen[1] = MD5_MAC_LEN; + addr[2] = key; + elen[2] = 2; /* Salt */ + } else { + addr[1] = pos - MD5_MAC_LEN; + elen[1] = MD5_MAC_LEN; + } + md5_vector(first ? 3 : 2, addr, elen, hash); + first = 0; + + for (i = 0; i < MD5_MAC_LEN; i++) + *ppos++ = *pos++ ^ hash[i]; + left -= MD5_MAC_LEN; + } + + if (plain[0] == 0 || plain[0] > plen - 1) { + printf("Failed to decrypt MPPE key\n"); + os_free(plain); + return NULL; + } + + res = os_malloc(plain[0]); + if (res == NULL) { + os_free(plain); + return NULL; + } + os_memcpy(res, plain + 1, plain[0]); + if (reslen) + *reslen = plain[0]; + os_free(plain); + return res; +} + + +static void encrypt_ms_key(const u8 *key, size_t key_len, u16 salt, + const u8 *req_authenticator, + const u8 *secret, size_t secret_len, + u8 *ebuf, size_t *elen) +{ + int i, len, first = 1; + u8 hash[MD5_MAC_LEN], saltbuf[2], *pos; + const u8 *addr[3]; + size_t _len[3]; + + WPA_PUT_BE16(saltbuf, salt); + + len = 1 + key_len; + if (len & 0x0f) { + len = (len & 0xf0) + 16; + } + os_memset(ebuf, 0, len); + ebuf[0] = key_len; + os_memcpy(ebuf + 1, key, key_len); + + *elen = len; + + pos = ebuf; + while (len > 0) { + /* b(1) = MD5(Secret + Request-Authenticator + Salt) + * b(i) = MD5(Secret + c(i - 1)) for i > 1 */ + addr[0] = secret; + _len[0] = secret_len; + if (first) { + addr[1] = req_authenticator; + _len[1] = MD5_MAC_LEN; + addr[2] = saltbuf; + _len[2] = sizeof(saltbuf); + } else { + addr[1] = pos - MD5_MAC_LEN; + _len[1] = MD5_MAC_LEN; + } + md5_vector(first ? 3 : 2, addr, _len, hash); + first = 0; + + for (i = 0; i < MD5_MAC_LEN; i++) + *pos++ ^= hash[i]; + + len -= MD5_MAC_LEN; + } +} + + +struct radius_ms_mppe_keys * +radius_msg_get_ms_keys(struct radius_msg *msg, struct radius_msg *sent_msg, + const u8 *secret, size_t secret_len) +{ + u8 *key; + size_t keylen; + struct radius_ms_mppe_keys *keys; + + if (msg == NULL || sent_msg == NULL) + return NULL; + + keys = os_zalloc(sizeof(*keys)); + if (keys == NULL) + return NULL; + + key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_MICROSOFT, + RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY, + &keylen); + if (key) { + keys->send = decrypt_ms_key(key, keylen, + sent_msg->hdr->authenticator, + secret, secret_len, + &keys->send_len); + os_free(key); + } + + key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_MICROSOFT, + RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY, + &keylen); + if (key) { + keys->recv = decrypt_ms_key(key, keylen, + sent_msg->hdr->authenticator, + secret, secret_len, + &keys->recv_len); + os_free(key); + } + + return keys; +} + + +struct radius_ms_mppe_keys * +radius_msg_get_cisco_keys(struct radius_msg *msg, struct radius_msg *sent_msg, + const u8 *secret, size_t secret_len) +{ + u8 *key; + size_t keylen; + struct radius_ms_mppe_keys *keys; + + if (msg == NULL || sent_msg == NULL) + return NULL; + + keys = os_zalloc(sizeof(*keys)); + if (keys == NULL) + return NULL; + + key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_CISCO, + RADIUS_CISCO_AV_PAIR, &keylen); + if (key && keylen == 51 && + os_memcmp(key, "leap:session-key=", 17) == 0) { + keys->recv = decrypt_ms_key(key + 17, keylen - 17, + sent_msg->hdr->authenticator, + secret, secret_len, + &keys->recv_len); + } + os_free(key); + + return keys; +} + + +int radius_msg_add_mppe_keys(struct radius_msg *msg, + const u8 *req_authenticator, + const u8 *secret, size_t secret_len, + const u8 *send_key, size_t send_key_len, + const u8 *recv_key, size_t recv_key_len) +{ + struct radius_attr_hdr *attr; + u32 vendor_id = htonl(RADIUS_VENDOR_ID_MICROSOFT); + u8 *buf; + struct radius_attr_vendor *vhdr; + u8 *pos; + size_t elen; + int hlen; + u16 salt; + + hlen = sizeof(vendor_id) + sizeof(*vhdr) + 2; + + /* MS-MPPE-Send-Key */ + buf = os_malloc(hlen + send_key_len + 16); + if (buf == NULL) { + return 0; + } + pos = buf; + os_memcpy(pos, &vendor_id, sizeof(vendor_id)); + pos += sizeof(vendor_id); + vhdr = (struct radius_attr_vendor *) pos; + vhdr->vendor_type = RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY; + pos = (u8 *) (vhdr + 1); + salt = os_random() | 0x8000; + WPA_PUT_BE16(pos, salt); + pos += 2; + encrypt_ms_key(send_key, send_key_len, salt, req_authenticator, secret, + secret_len, pos, &elen); + vhdr->vendor_length = hlen + elen - sizeof(vendor_id); + + attr = radius_msg_add_attr(msg, RADIUS_ATTR_VENDOR_SPECIFIC, + buf, hlen + elen); + os_free(buf); + if (attr == NULL) { + return 0; + } + + /* MS-MPPE-Recv-Key */ + buf = os_malloc(hlen + send_key_len + 16); + if (buf == NULL) { + return 0; + } + pos = buf; + os_memcpy(pos, &vendor_id, sizeof(vendor_id)); + pos += sizeof(vendor_id); + vhdr = (struct radius_attr_vendor *) pos; + vhdr->vendor_type = RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY; + pos = (u8 *) (vhdr + 1); + salt ^= 1; + WPA_PUT_BE16(pos, salt); + pos += 2; + encrypt_ms_key(recv_key, recv_key_len, salt, req_authenticator, secret, + secret_len, pos, &elen); + vhdr->vendor_length = hlen + elen - sizeof(vendor_id); + + attr = radius_msg_add_attr(msg, RADIUS_ATTR_VENDOR_SPECIFIC, + buf, hlen + elen); + os_free(buf); + if (attr == NULL) { + return 0; + } + + return 1; +} + + +/* Add User-Password attribute to a RADIUS message and encrypt it as specified + * in RFC 2865, Chap. 5.2 */ +struct radius_attr_hdr * +radius_msg_add_attr_user_password(struct radius_msg *msg, + const u8 *data, size_t data_len, + const u8 *secret, size_t secret_len) +{ + u8 buf[128]; + int padlen, i; + size_t buf_len, pos; + const u8 *addr[2]; + size_t len[2]; + u8 hash[16]; + + if (data_len > 128) + return NULL; + + os_memcpy(buf, data, data_len); + buf_len = data_len; + + padlen = data_len % 16; + if (padlen) { + padlen = 16 - padlen; + os_memset(buf + data_len, 0, padlen); + buf_len += padlen; + } + + addr[0] = secret; + len[0] = secret_len; + addr[1] = msg->hdr->authenticator; + len[1] = 16; + md5_vector(2, addr, len, hash); + + for (i = 0; i < 16; i++) + buf[i] ^= hash[i]; + pos = 16; + + while (pos < buf_len) { + addr[0] = secret; + len[0] = secret_len; + addr[1] = &buf[pos - 16]; + len[1] = 16; + md5_vector(2, addr, len, hash); + + for (i = 0; i < 16; i++) + buf[pos + i] ^= hash[i]; + + pos += 16; + } + + return radius_msg_add_attr(msg, RADIUS_ATTR_USER_PASSWORD, + buf, buf_len); +} + + +int radius_msg_get_attr(struct radius_msg *msg, u8 type, u8 *buf, size_t len) +{ + struct radius_attr_hdr *attr = NULL, *tmp; + size_t i, dlen; + + for (i = 0; i < msg->attr_used; i++) { + tmp = radius_get_attr_hdr(msg, i); + if (tmp->type == type) { + attr = tmp; + break; + } + } + + if (!attr) + return -1; + + dlen = attr->length - sizeof(*attr); + if (buf) + os_memcpy(buf, (attr + 1), dlen > len ? len : dlen); + return dlen; +} + + +int radius_msg_get_attr_ptr(struct radius_msg *msg, u8 type, u8 **buf, + size_t *len, const u8 *start) +{ + size_t i; + struct radius_attr_hdr *attr = NULL, *tmp; + + for (i = 0; i < msg->attr_used; i++) { + tmp = radius_get_attr_hdr(msg, i); + if (tmp->type == type && + (start == NULL || (u8 *) tmp > start)) { + attr = tmp; + break; + } + } + + if (!attr) + return -1; + + *buf = (u8 *) (attr + 1); + *len = attr->length - sizeof(*attr); + return 0; +} + + +int radius_msg_count_attr(struct radius_msg *msg, u8 type, int min_len) +{ + size_t i; + int count; + + for (count = 0, i = 0; i < msg->attr_used; i++) { + struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i); + if (attr->type == type && + attr->length >= sizeof(struct radius_attr_hdr) + min_len) + count++; + } + + return count; +} + + +struct radius_tunnel_attrs { + int tag_used; + int type; /* Tunnel-Type */ + int medium_type; /* Tunnel-Medium-Type */ + int vlanid; +}; + + +/** + * radius_msg_get_vlanid - Parse RADIUS attributes for VLAN tunnel information + * @msg: RADIUS message + * Returns: VLAN ID for the first tunnel configuration of -1 if none is found + */ +int radius_msg_get_vlanid(struct radius_msg *msg) +{ + struct radius_tunnel_attrs tunnel[RADIUS_TUNNEL_TAGS], *tun; + size_t i; + struct radius_attr_hdr *attr = NULL; + const u8 *data; + char buf[10]; + size_t dlen; + + os_memset(&tunnel, 0, sizeof(tunnel)); + + for (i = 0; i < msg->attr_used; i++) { + attr = radius_get_attr_hdr(msg, i); + data = (const u8 *) (attr + 1); + dlen = attr->length - sizeof(*attr); + if (attr->length < 3) + continue; + if (data[0] >= RADIUS_TUNNEL_TAGS) + tun = &tunnel[0]; + else + tun = &tunnel[data[0]]; + + switch (attr->type) { + case RADIUS_ATTR_TUNNEL_TYPE: + if (attr->length != 6) + break; + tun->tag_used++; + tun->type = WPA_GET_BE24(data + 1); + break; + case RADIUS_ATTR_TUNNEL_MEDIUM_TYPE: + if (attr->length != 6) + break; + tun->tag_used++; + tun->medium_type = WPA_GET_BE24(data + 1); + break; + case RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID: + if (data[0] < RADIUS_TUNNEL_TAGS) { + data++; + dlen--; + } + if (dlen >= sizeof(buf)) + break; + os_memcpy(buf, data, dlen); + buf[dlen] = '\0'; + tun->tag_used++; + tun->vlanid = atoi(buf); + break; + } + } + + for (i = 0; i < RADIUS_TUNNEL_TAGS; i++) { + tun = &tunnel[i]; + if (tun->tag_used && + tun->type == RADIUS_TUNNEL_TYPE_VLAN && + tun->medium_type == RADIUS_TUNNEL_MEDIUM_TYPE_802 && + tun->vlanid > 0) + return tun->vlanid; + } + + return -1; +} + + +void radius_free_class(struct radius_class_data *c) +{ + size_t i; + if (c == NULL) + return; + for (i = 0; i < c->count; i++) + os_free(c->attr[i].data); + os_free(c->attr); + c->attr = NULL; + c->count = 0; +} + + +int radius_copy_class(struct radius_class_data *dst, + const struct radius_class_data *src) +{ + size_t i; + + if (src->attr == NULL) + return 0; + + dst->attr = os_zalloc(src->count * sizeof(struct radius_attr_data)); + if (dst->attr == NULL) + return -1; + + dst->count = 0; + + for (i = 0; i < src->count; i++) { + dst->attr[i].data = os_malloc(src->attr[i].len); + if (dst->attr[i].data == NULL) + break; + dst->count++; + os_memcpy(dst->attr[i].data, src->attr[i].data, + src->attr[i].len); + dst->attr[i].len = src->attr[i].len; + } + + return 0; +} diff -r ad6c0118fb50 -r a857024cb48b extensions/app_radgw/radius.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/app_radgw/radius.h Wed Apr 14 18:30:22 2010 +0900 @@ -0,0 +1,296 @@ +/*********************************************************************************/ +/* freeDiameter author note: + * The content from this file comes directly from the hostap project. + * It is redistributed under the terms of the BSD license, as allowed + * by the original copyright reproduced bellow. + * The file has not been modified, except for this notice. + */ +/*********************************************************************************/ + +/* + * hostapd / RADIUS message processing + * Copyright (c) 2002-2007, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef RADIUS_H +#define RADIUS_H + +/* RFC 2865 - RADIUS */ + +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif /* _MSC_VER */ + +struct radius_hdr { + u8 code; + u8 identifier; + u16 length; /* including this header */ + u8 authenticator[16]; + /* followed by length-20 octets of attributes */ +} STRUCT_PACKED; + +enum { RADIUS_CODE_ACCESS_REQUEST = 1, + RADIUS_CODE_ACCESS_ACCEPT = 2, + RADIUS_CODE_ACCESS_REJECT = 3, + RADIUS_CODE_ACCOUNTING_REQUEST = 4, + RADIUS_CODE_ACCOUNTING_RESPONSE = 5, + RADIUS_CODE_ACCESS_CHALLENGE = 11, + RADIUS_CODE_STATUS_SERVER = 12, + RADIUS_CODE_STATUS_CLIENT = 13, + RADIUS_CODE_RESERVED = 255 +}; + +struct radius_attr_hdr { + u8 type; + u8 length; /* including this header */ + /* followed by length-2 octets of attribute value */ +} STRUCT_PACKED; + +#define RADIUS_MAX_ATTR_LEN (255 - sizeof(struct radius_attr_hdr)) + +enum { RADIUS_ATTR_USER_NAME = 1, + RADIUS_ATTR_USER_PASSWORD = 2, + RADIUS_ATTR_NAS_IP_ADDRESS = 4, + RADIUS_ATTR_NAS_PORT = 5, + RADIUS_ATTR_FRAMED_MTU = 12, + RADIUS_ATTR_REPLY_MESSAGE = 18, + RADIUS_ATTR_STATE = 24, + RADIUS_ATTR_CLASS = 25, + RADIUS_ATTR_VENDOR_SPECIFIC = 26, + RADIUS_ATTR_SESSION_TIMEOUT = 27, + RADIUS_ATTR_IDLE_TIMEOUT = 28, + RADIUS_ATTR_TERMINATION_ACTION = 29, + RADIUS_ATTR_CALLED_STATION_ID = 30, + RADIUS_ATTR_CALLING_STATION_ID = 31, + RADIUS_ATTR_NAS_IDENTIFIER = 32, + RADIUS_ATTR_PROXY_STATE = 33, + RADIUS_ATTR_ACCT_STATUS_TYPE = 40, + RADIUS_ATTR_ACCT_DELAY_TIME = 41, + RADIUS_ATTR_ACCT_INPUT_OCTETS = 42, + RADIUS_ATTR_ACCT_OUTPUT_OCTETS = 43, + RADIUS_ATTR_ACCT_SESSION_ID = 44, + RADIUS_ATTR_ACCT_AUTHENTIC = 45, + RADIUS_ATTR_ACCT_SESSION_TIME = 46, + RADIUS_ATTR_ACCT_INPUT_PACKETS = 47, + RADIUS_ATTR_ACCT_OUTPUT_PACKETS = 48, + RADIUS_ATTR_ACCT_TERMINATE_CAUSE = 49, + RADIUS_ATTR_ACCT_MULTI_SESSION_ID = 50, + RADIUS_ATTR_ACCT_LINK_COUNT = 51, + RADIUS_ATTR_ACCT_INPUT_GIGAWORDS = 52, + RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS = 53, + RADIUS_ATTR_EVENT_TIMESTAMP = 55, + RADIUS_ATTR_NAS_PORT_TYPE = 61, + RADIUS_ATTR_TUNNEL_TYPE = 64, + RADIUS_ATTR_TUNNEL_MEDIUM_TYPE = 65, + RADIUS_ATTR_CONNECT_INFO = 77, + RADIUS_ATTR_EAP_MESSAGE = 79, + RADIUS_ATTR_MESSAGE_AUTHENTICATOR = 80, + RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID = 81, + RADIUS_ATTR_ACCT_INTERIM_INTERVAL = 85, + RADIUS_ATTR_CHARGEABLE_USER_IDENTITY = 89, + RADIUS_ATTR_NAS_IPV6_ADDRESS = 95 +}; + + +/* Termination-Action */ +#define RADIUS_TERMINATION_ACTION_DEFAULT 0 +#define RADIUS_TERMINATION_ACTION_RADIUS_REQUEST 1 + +/* NAS-Port-Type */ +#define RADIUS_NAS_PORT_TYPE_IEEE_802_11 19 + +/* Acct-Status-Type */ +#define RADIUS_ACCT_STATUS_TYPE_START 1 +#define RADIUS_ACCT_STATUS_TYPE_STOP 2 +#define RADIUS_ACCT_STATUS_TYPE_INTERIM_UPDATE 3 +#define RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_ON 7 +#define RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_OFF 8 + +/* Acct-Authentic */ +#define RADIUS_ACCT_AUTHENTIC_RADIUS 1 +#define RADIUS_ACCT_AUTHENTIC_LOCAL 2 +#define RADIUS_ACCT_AUTHENTIC_REMOTE 3 + +/* Acct-Terminate-Cause */ +#define RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST 1 +#define RADIUS_ACCT_TERMINATE_CAUSE_LOST_CARRIER 2 +#define RADIUS_ACCT_TERMINATE_CAUSE_LOST_SERVICE 3 +#define RADIUS_ACCT_TERMINATE_CAUSE_IDLE_TIMEOUT 4 +#define RADIUS_ACCT_TERMINATE_CAUSE_SESSION_TIMEOUT 5 +#define RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_RESET 6 +#define RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_REBOOT 7 +#define RADIUS_ACCT_TERMINATE_CAUSE_PORT_ERROR 8 +#define RADIUS_ACCT_TERMINATE_CAUSE_NAS_ERROR 9 +#define RADIUS_ACCT_TERMINATE_CAUSE_NAS_REQUEST 10 +#define RADIUS_ACCT_TERMINATE_CAUSE_NAS_REBOOT 11 +#define RADIUS_ACCT_TERMINATE_CAUSE_PORT_UNNEEDED 12 +#define RADIUS_ACCT_TERMINATE_CAUSE_PORT_PREEMPTED 13 +#define RADIUS_ACCT_TERMINATE_CAUSE_PORT_SUSPENDED 14 +#define RADIUS_ACCT_TERMINATE_CAUSE_SERVICE_UNAVAILABLE 15 +#define RADIUS_ACCT_TERMINATE_CAUSE_CALLBACK 16 +#define RADIUS_ACCT_TERMINATE_CAUSE_USER_ERROR 17 +#define RADIUS_ACCT_TERMINATE_CAUSE_HOST_REQUEST 18 + +#define RADIUS_TUNNEL_TAGS 32 + +/* Tunnel-Type */ +#define RADIUS_TUNNEL_TYPE_PPTP 1 +#define RADIUS_TUNNEL_TYPE_L2TP 3 +#define RADIUS_TUNNEL_TYPE_IPIP 7 +#define RADIUS_TUNNEL_TYPE_GRE 10 +#define RADIUS_TUNNEL_TYPE_VLAN 13 + +/* Tunnel-Medium-Type */ +#define RADIUS_TUNNEL_MEDIUM_TYPE_IPV4 1 +#define RADIUS_TUNNEL_MEDIUM_TYPE_IPV6 2 +#define RADIUS_TUNNEL_MEDIUM_TYPE_802 6 + + +struct radius_attr_vendor { + u8 vendor_type; + u8 vendor_length; +} STRUCT_PACKED; + +#define RADIUS_VENDOR_ID_CISCO 9 +#define RADIUS_CISCO_AV_PAIR 1 + +/* RFC 2548 - Microsoft Vendor-specific RADIUS Attributes */ +#define RADIUS_VENDOR_ID_MICROSOFT 311 + +enum { RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY = 16, + RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY = 17 +}; + +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ + +struct radius_ms_mppe_keys { + u8 *send; + size_t send_len; + u8 *recv; + size_t recv_len; +}; + + +/* RADIUS message structure for new and parsed messages */ +struct radius_msg { + unsigned char *buf; + size_t buf_size; /* total size allocated for buf */ + size_t buf_used; /* bytes used in buf */ + + struct radius_hdr *hdr; + + size_t *attr_pos; /* array of indexes to attributes (number of bytes + * from buf to the beginning of + * struct radius_attr_hdr). */ + size_t attr_size; /* total size of the attribute pointer array */ + size_t attr_used; /* total number of attributes in the array */ +}; + + +/* Default size to be allocated for new RADIUS messages */ +#define RADIUS_DEFAULT_MSG_SIZE 1024 + +/* Default size to be allocated for attribute array */ +#define RADIUS_DEFAULT_ATTR_COUNT 16 + + +/* MAC address ASCII format for IEEE 802.1X use + * (draft-congdon-radius-8021x-20.txt) */ +#define RADIUS_802_1X_ADDR_FORMAT "%02X-%02X-%02X-%02X-%02X-%02X" +/* MAC address ASCII format for non-802.1X use */ +#define RADIUS_ADDR_FORMAT "%02x%02x%02x%02x%02x%02x" + +struct radius_msg *radius_msg_new(u8 code, u8 identifier); +int radius_msg_initialize(struct radius_msg *msg, size_t init_len); +void radius_msg_set_hdr(struct radius_msg *msg, u8 code, u8 identifier); +void radius_msg_free(struct radius_msg *msg); +void radius_msg_dump(struct radius_msg *msg); +int radius_msg_finish(struct radius_msg *msg, const u8 *secret, + size_t secret_len); +int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret, + size_t secret_len, const u8 *req_authenticator); +void radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret, + size_t secret_len); +struct radius_attr_hdr *radius_msg_add_attr(struct radius_msg *msg, u8 type, + const u8 *data, size_t data_len); +struct radius_msg *radius_msg_parse(const u8 *data, size_t len); +int radius_msg_add_eap(struct radius_msg *msg, const u8 *data, + size_t data_len); +u8 *radius_msg_get_eap(struct radius_msg *msg, size_t *len); +int radius_msg_verify(struct radius_msg *msg, const u8 *secret, + size_t secret_len, struct radius_msg *sent_msg, + int auth); +int radius_msg_verify_msg_auth(struct radius_msg *msg, const u8 *secret, + size_t secret_len, const u8 *req_auth); +int radius_msg_copy_attr(struct radius_msg *dst, struct radius_msg *src, + u8 type); +void radius_msg_make_authenticator(struct radius_msg *msg, + const u8 *data, size_t len); +struct radius_ms_mppe_keys * +radius_msg_get_ms_keys(struct radius_msg *msg, struct radius_msg *sent_msg, + const u8 *secret, size_t secret_len); +struct radius_ms_mppe_keys * +radius_msg_get_cisco_keys(struct radius_msg *msg, struct radius_msg *sent_msg, + const u8 *secret, size_t secret_len); +int radius_msg_add_mppe_keys(struct radius_msg *msg, + const u8 *req_authenticator, + const u8 *secret, size_t secret_len, + const u8 *send_key, size_t send_key_len, + const u8 *recv_key, size_t recv_key_len); +struct radius_attr_hdr * +radius_msg_add_attr_user_password(struct radius_msg *msg, + const u8 *data, size_t data_len, + const u8 *secret, size_t secret_len); +int radius_msg_get_attr(struct radius_msg *msg, u8 type, u8 *buf, size_t len); +int radius_msg_get_vlanid(struct radius_msg *msg); + +static inline int radius_msg_add_attr_int32(struct radius_msg *msg, u8 type, + u32 value) +{ + u32 val = htonl(value); + return radius_msg_add_attr(msg, type, (u8 *) &val, 4) != NULL; +} + +static inline int radius_msg_get_attr_int32(struct radius_msg *msg, u8 type, + u32 *value) +{ + u32 val; + int res; + res = radius_msg_get_attr(msg, type, (u8 *) &val, 4); + if (res != 4) + return -1; + + *value = ntohl(val); + return 0; +} +int radius_msg_get_attr_ptr(struct radius_msg *msg, u8 type, u8 **buf, + size_t *len, const u8 *start); +int radius_msg_count_attr(struct radius_msg *msg, u8 type, int min_len); + + +struct radius_attr_data { + u8 *data; + size_t len; +}; + +struct radius_class_data { + struct radius_attr_data *attr; + size_t count; +}; + +void radius_free_class(struct radius_class_data *c); +int radius_copy_class(struct radius_class_data *dst, + const struct radius_class_data *src); + +#endif /* RADIUS_H */ diff -r ad6c0118fb50 -r a857024cb48b extensions/app_radgw/rgw.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/app_radgw/rgw.h Wed Apr 14 18:30:22 2010 +0900 @@ -0,0 +1,133 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis * +* * +* Copyright (c) 2009, WIDE Project and NICT * +* All rights reserved. * +* * +* Redistribution and use of this software in source and binary forms, with or without modification, are * +* permitted provided that the following conditions are met: * +* * +* * Redistributions of source code must retain the above * +* copyright notice, this list of conditions and the * +* following disclaimer. * +* * +* * Redistributions in binary form must reproduce the above * +* copyright notice, this list of conditions and the * +* following disclaimer in the documentation and/or other * +* materials provided with the distribution. * +* * +* * Neither the name of the WIDE Project or NICT nor the * +* names of its contributors may be used to endorse or * +* promote products derived from this software without * +* specific prior written permission of WIDE Project and * +* NICT. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED * +* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * +* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * +* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * +* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +*********************************************************************************************************/ + +/* This file contains the definitions for the app_radgw internal use. */ + +#ifndef _RGW_H +#define _RGW_H + +/* include the common definitions */ +#include "rgw_common.h" + +/* RADIUS messages + metadata */ +struct rgw_radius_msg_meta { + + /* The RADIUS message */ + struct radius_msg radius; + + /* Metadata */ + struct { + /* The port it was sent from, in network byte order */ + unsigned port :16; + + /* received on ACCT or AUTH port? */ + unsigned serv_type :2; + + /* The message has a valid Message-Authenticator attribute */ + unsigned valid_mac :1; + + /* The message has a valid NAS-IP(v6)-Address (1) and/or NAS-Identifier (2) attribute */ + unsigned valid_nas_info :2; + }; + +}; +void rgw_msg_free(struct rgw_radius_msg_meta ** msg); +int rgw_msg_parse(unsigned char * buf, size_t len, struct rgw_radius_msg_meta ** msg); +void rgw_msg_dump(struct rgw_radius_msg_meta * msg); +int rgw_msg_auth_check(struct rgw_radius_msg_meta * msg, struct rgw_client * cli, uint8_t * req_auth); +int rgw_msg_create_base(struct rgw_radius_msg_meta * msg, struct rgw_client * cli, struct session ** session, struct msg ** diam); +int rgw_msg_init(void); + +/* Local RADIUS server(s) configuration */ +struct rgw_serv { + unsigned disabled :1; + unsigned ip_disabled :1; + unsigned ip6_disabled :1; + unsigned :13; /* padding */ + + uint16_t port; /* stored in network byte order */ + + struct in_addr ip_endpoint; + struct in6_addr ip6_endpoint; +}; + +extern struct rgw_servs { + struct rgw_serv auth_serv; + struct rgw_serv acct_serv; +} rgw_servers; + +int rgw_servers_init(void); +int rgw_servers_start(void); +void rgw_servers_dump(void); +int rgw_servers_send(int type, unsigned char *buf, size_t buflen, struct sockaddr *to, uint16_t to_port); +void rgw_servers_fini(void); + + +/* Clients management */ +int rgw_clients_add( struct sockaddr * ip_port, unsigned char ** key, size_t keylen ); +int rgw_clients_getkey(struct rgw_client * cli, unsigned char **key, size_t *key_len); +int rgw_clients_search(struct sockaddr * ip_port, struct rgw_client ** ref); +int rgw_clients_check_dup(struct rgw_radius_msg_meta **msg, struct rgw_client *cli); +int rgw_clients_check_origin(struct rgw_radius_msg_meta *msg, struct rgw_client *cli); +int rgw_clients_get_origin(struct rgw_client *cli, char **fqdn, char **realm); +int rgw_client_finish_send(struct radius_msg ** msg, struct rgw_radius_msg_meta * req, struct rgw_client * cli); +void rgw_clients_dispose(struct rgw_client ** ref); +void rgw_clients_dump(void); +void rgw_clients_fini(void); +int rgw_client_session_add(struct rgw_client * cli, struct session *sess, char * dest_realm, char * dest_host, application_id_t appid); +int rgw_client_session_stop(struct rgw_client * cli, struct session * sess, int32_t reason); + + +/* Management of plugins */ +int rgw_plg_add( char * plgfile, char * conffile, int port, unsigned char ** codes_array, size_t codes_sz ); +void rgw_plg_dump(void); +void rgw_plg_start_cache(void); +int rgw_plg_loop_req(struct rgw_radius_msg_meta **rad, struct session **session, struct msg **diam_msg, struct rgw_client * cli); +int rgw_plg_loop_ans(struct rgw_radius_msg_meta *req, struct session *session, struct msg **diam_ans, struct radius_msg ** rad_ans, struct rgw_client * cli); +void rgw_plg_fini(void); + + +/* Parse configuration file */ +int rgw_conf_handle(char * conffile); + + +/* Worker module, process incoming RADIUS messages (in separated threads) */ +int rgw_work_start(void); +int rgw_work_add(struct rgw_radius_msg_meta * msg, struct rgw_client * client); +void rgw_work_fini(void); + + +#endif /* _RGW_H */ + diff -r ad6c0118fb50 -r a857024cb48b extensions/app_radgw/rgw_clients.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/app_radgw/rgw_clients.c Wed Apr 14 18:30:22 2010 +0900 @@ -0,0 +1,610 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis * +* * +* Copyright (c) 2009, WIDE Project and NICT * +* All rights reserved. * +* * +* Redistribution and use of this software in source and binary forms, with or without modification, are * +* permitted provided that the following conditions are met: * +* * +* * Redistributions of source code must retain the above * +* copyright notice, this list of conditions and the * +* following disclaimer. * +* * +* * Redistributions in binary form must reproduce the above * +* copyright notice, this list of conditions and the * +* following disclaimer in the documentation and/or other * +* materials provided with the distribution. * +* * +* * Neither the name of the WIDE Project or NICT nor the * +* names of its contributors may be used to endorse or * +* promote products derived from this software without * +* specific prior written permission of WIDE Project and * +* NICT. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED * +* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * +* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * +* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * +* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +*********************************************************************************************************/ + +/* Manage the list of RADIUS clients, along with their shared secrets. */ + +/* Probably some changes are needed to support RADIUS Proxies */ + +#include "rgw.h" + +/* Ordered lists of clients. The order relationship is a memcmp on the address zone. + For same addresses, the port is compared. + The same address cannot be added twice, once with a 0-port and once with another port value. + */ +static struct fd_list cli_ip = FD_LIST_INITIALIZER(cli_ip); +static struct fd_list cli_ip6 = FD_LIST_INITIALIZER(cli_ip6); + +/* Mutex to protect the previous lists */ +static pthread_mutex_t cli_mtx = PTHREAD_MUTEX_INITIALIZER; + +/* Structure describing one client */ +struct rgw_client { + /* Link information in global list */ + struct fd_list chain; + + /* Reference count */ + int refcount; + + /* The address and optional port (alloc'd during configuration file parsing). */ + union { + struct sockaddr *sa; /* generic pointer */ + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + }; + + /* The FQDN, realm, and optional aliases */ + char *fqdn; + char *realm; + char **aliases; + size_t aliases_nb; + + /* The secret key data. */ + struct { + unsigned char * data; + size_t len; + } key; + + /* information of previous msg received, for duplicate checks. */ + struct { + uint16_t port; + uint8_t id; + struct radius_msg * ans; /* to be able to resend a lost answer */ + } last[2]; /*[0] for auth, [1] for acct. */ +}; + + + +/* create a new rgw_client. the arguments are moved into the structure (to limit malloc & free calls). */ +static int client_create(struct rgw_client ** res, struct sockaddr ** ip_port, unsigned char ** key, size_t keylen ) +{ + struct rgw_client *tmp = NULL; + char buf[255]; + int ret; + + /* Search FQDN for the client */ + ret = getnameinfo( *ip_port, sizeof(struct sockaddr_storage), &buf[0], sizeof(buf), NULL, 0, 0 ); + if (ret) { + TRACE_DEBUG(INFO, "Unable to resolve peer name: %s", gai_strerror(ret)); + return EINVAL; + } + + /* Create the new object */ + CHECK_MALLOC( tmp = malloc(sizeof (struct rgw_client)) ); + memset(tmp, 0, sizeof(struct rgw_client)); + fd_list_init(&tmp->chain, NULL); + + /* Copy the fqdn */ + CHECK_MALLOC( tmp->fqdn = strdup(buf) ); + /* Find an appropriate realm */ + tmp->realm = strchr(tmp->fqdn, '.'); + if (tmp->realm) + tmp->realm += 1; + if ((!tmp->realm) || (*tmp->realm == '\0')) /* in case the fqdn was "localhost." for example, if it is possible... */ + tmp->realm = fd_g_config->cnf_diamrlm; + + /* move the sa info reference */ + tmp->sa = *ip_port; + *ip_port = NULL; + + /* move the key material */ + tmp->key.data = *key; + tmp->key.len = keylen; + *key = NULL; + + /* Done! */ + *res = tmp; + return 0; +} + + +/* Decrease refcount on a client; the lock must be held when this function is called. */ +static void client_unlink(struct rgw_client * client) +{ + client->refcount -= 1; + + if (client->refcount <= 0) { + int idx; + /* to be sure: the refcount should be 0 only when client_fini is called */ + ASSERT( FD_IS_LIST_EMPTY(&client->chain) ); + + /* Free the data */ + for (idx = 0; idx < client->aliases_nb; idx++) + free(client->aliases[idx]); + free(client->aliases); + free(client->fqdn); + free(client->sa); + free(client->key.data); + free(client); + } +} + + +/* Macro to avoid duplicting the code in the next function */ +#define client_search_family( _family_ ) \ + case AF_INET##_family_: { \ + struct sockaddr_in##_family_ * sin##_family_ = (struct sockaddr_in##_family_ *)ip_port; \ + for (ref = cli_ip##_family_.next; ref != &cli_ip##_family_; ref = ref->next) { \ + cmp = memcmp(&sin##_family_->sin##_family_##_addr, \ + &((struct rgw_client *)ref)->sin##_family_->sin##_family_##_addr, \ + sizeof(struct in##_family_##_addr)); \ + if (cmp > 0) continue; /* search further in the list */ \ + if (cmp < 0) break; /* this IP is not in the list */ \ + /* Now compare the ports as follow: */ \ + /* If the ip_port we are searching does not contain a port, just return the first match result */ \ + if ( (sin##_family_->sin##_family_##_port == 0) \ + /* If the entry in the list does not contain a port, return it as a match */ \ + || (((struct rgw_client *)ref)->sin##_family_->sin##_family_##_port == 0) \ + /* If both ports are equal, it is a match */ \ + || (sin##_family_->sin##_family_##_port == \ + ((struct rgw_client *)ref)->sin##_family_->sin##_family_##_port)) { \ + *res = (struct rgw_client *)ref; \ + return EEXIST; \ + } \ + /* Otherwise, the list is ordered by port value (byte order does not matter */ \ + if (sin##_family_->sin##_family_##_port \ + > ((struct rgw_client *)ref)->sin##_family_->sin##_family_##_port) continue; \ + else break; \ + } \ + *res = (struct rgw_client *)(ref->prev); \ + return ENOENT; \ + } +/* Function to look for an existing rgw_client, or the previous element. + The cli_mtx must be held when calling this function. + Returns ENOENT if the matching client does not exist, and res points to the previous element in the list. + Returns EEXIST if the matching client is found, and res points to this element. + Returns other error code on other error. */ +static int client_search(struct rgw_client ** res, struct sockaddr * ip_port ) +{ + int ret = 0; + int cmp; + struct fd_list *ref = NULL; + + CHECK_PARAMS(res && ip_port); + + switch (ip_port->sa_family) { + client_search_family() + break; + + client_search_family( 6 ) + break; + } + + /* We're never supposed to reach this point */ + ASSERT(0); + return EINVAL; +} + +int rgw_clients_getkey(struct rgw_client * cli, unsigned char **key, size_t *key_len) +{ + CHECK_PARAMS( cli && key && key_len ); + *key = cli->key.data; + *key_len = cli->key.len; + return 0; +} + +int rgw_clients_search(struct sockaddr * ip_port, struct rgw_client ** ref) +{ + int ret = 0; + + TRACE_ENTRY("%p %p", ip_port, ref); + + CHECK_PARAMS(ip_port && ref); + + CHECK_POSIX( pthread_mutex_lock(&cli_mtx) ); + + ret = client_search(ref, ip_port); + if (ret == EEXIST) { + (*ref)->refcount ++; + ret = 0; + } else { + *ref = NULL; + } + + CHECK_POSIX( pthread_mutex_unlock(&cli_mtx) ); + + return ret; +} + +int rgw_clients_check_dup(struct rgw_radius_msg_meta **msg, struct rgw_client *cli) +{ + int idx; + + TRACE_ENTRY("%p %p", msg, cli); + + CHECK_PARAMS( msg && cli ); + + if ((*msg)->serv_type == RGW_PLG_TYPE_AUTH) + idx = 0; + else + idx = 1; + + if ((cli->last[idx].id == (*msg)->radius.hdr->identifier) && (cli->last[idx].port == (*msg)->port)) { + /* Duplicate! */ + TRACE_DEBUG(INFO, "Received duplicated RADIUS message (id: %02hhx, port: %hu).", (*msg)->radius.hdr->identifier, ntohs((*msg)->port)); + if (cli->last[idx].ans) { + /* Resend the answer */ + CHECK_FCT_DO( rgw_servers_send((*msg)->serv_type, cli->last[idx].ans->buf, cli->last[idx].ans->buf_used, cli->sa, (*msg)->port), ); + } + rgw_msg_free(msg); + } else { + /* Update information for new message */ + if (cli->last[idx].ans) { + /* Free it */ + radius_msg_free(cli->last[idx].ans); + free(cli->last[idx].ans); + cli->last[idx].ans = NULL; + } + cli->last[idx].id = (*msg)->radius.hdr->identifier; + cli->last[idx].port = (*msg)->port; + } + + return 0; +} + +/* Check that the NAS-IP-Adress or NAS-Identifier is coherent with the IP the packet was received from */ +/* Also update the client list of aliases if needed */ +/* NOTE: This function will require changes to allow RADIUS Proxy on the path... */ +int rgw_clients_check_origin(struct rgw_radius_msg_meta *msg, struct rgw_client *cli) +{ + int idx; + struct radius_attr_hdr *nas_ip = NULL, *nas_ip6 = NULL, *nas_id = NULL; + + TRACE_ENTRY("%p %p", msg, cli); + CHECK_PARAMS(msg && cli && !msg->valid_nas_info ); + + /* Find the relevant attributes, if any */ + for (idx = 0; idx < msg->radius.attr_used; idx++) { + struct radius_attr_hdr * attr = (struct radius_attr_hdr *)(msg->radius.buf + msg->radius.attr_pos[idx]); + unsigned char * attr_val = (unsigned char *)(attr + 1); + size_t attr_len = attr->length - sizeof(struct radius_attr_hdr); + + if ((attr->type == RADIUS_ATTR_NAS_IP_ADDRESS) && (attr_len = 4)) { + nas_ip = attr; + continue; + } + + if ((attr->type == RADIUS_ATTR_NAS_IDENTIFIER) && (attr_len > 0)) { + nas_id = attr; + continue; + } + + if ((attr->type == RADIUS_ATTR_NAS_IPV6_ADDRESS) && (attr_len = 16)) { + nas_ip6 = attr; + continue; + } + } + + if (!nas_ip && !nas_ip6 && !nas_id) { + TRACE_DEBUG(FULL, "The message does not contain any NAS identification attribute."); + goto end; + } + + /* Check if the message was received from the IP in NAS-IP-Address attribute */ + if (nas_ip && (cli->sa->sa_family == AF_INET) && !memcmp(nas_ip+1, &cli->sin->sin_addr, sizeof(struct in_addr))) { + TRACE_DEBUG(FULL, "NAS-IP-Address contains the same address as the message was received from."); + msg->valid_nas_info |= 1; + } + if (nas_ip6 && (cli->sa->sa_family == AF_INET6) && !memcmp(nas_ip6+1, &cli->sin6->sin6_addr, sizeof(struct in6_addr))) { + TRACE_DEBUG(FULL, "NAS-IPv6-Address contains the same address as the message was received from."); + msg->valid_nas_info |= 1; + } + + /* If these conditions are not met, the message is probably forged (well, this might be false...) */ + if ((! msg->valid_nas_info) && (nas_ip || nas_ip6)) { + /* + In RADIUS it would be possible for a rogue NAS to forge the NAS-IP- + Address attribute value. Diameter/RADIUS translation agents MUST + check a received NAS-IP-Address or NAS-IPv6-Address attribute against + the source address of the RADIUS packet. If they do not match and + the Diameter/RADIUS translation agent does not know whether the + packet was sent by a RADIUS proxy or NAS (e.g., no Proxy-State + attribute), then by default it is assumed that the source address + corresponds to a RADIUS proxy, and that the NAS Address is behind + that proxy, potentially with some additional RADIUS proxies in + between. The Diameter/RADIUS translation agent MUST insert entries + in the Route-Record AVP corresponding to the apparent route. This + implies doing a reverse lookup on the source address and NAS-IP- + Address or NAS-IPv6-Address attributes to determine the corresponding + FQDNs. + + If the source address and the NAS-IP-Address or NAS-IPv6-Address do + not match, and the Diameter/RADIUS translation agent knows that it is + talking directly to the NAS (e.g., there are no RADIUS proxies + between it and the NAS), then the error should be logged, and the + packet MUST be discarded. + + Diameter agents and servers MUST check whether the NAS-IP-Address AVP + corresponds to an entry in the Route-Record AVP. This is done by + doing a reverse lookup (PTR RR) for the NAS-IP-Address to retrieve + the corresponding FQDN, and by checking for a match with the Route- + Record AVP. If no match is found, then an error is logged, but no + other action is taken. + */ + TRACE_DEBUG(INFO, "Message received with a NAS-IP-Address or NAS-IPv6-Address different from the sender's. Discarding..."); + return ENOTSUP; + } + + /* Now check the nas_id */ + if (nas_id) { + /* + In RADIUS it would be possible for a rogue NAS to forge the NAS- + Identifier attribute. Diameter/RADIUS translation agents SHOULD + attempt to check a received NAS-Identifier attribute against the + source address of the RADIUS packet, by doing an A/AAAA RR query. If + the NAS-Identifier attribute contains an FQDN, then such a query + would resolve to an IP address matching the source address. However, + the NAS-Identifier attribute is not required to contain an FQDN, so + such a query could fail. If it fails, an error should be logged, but + no action should be taken, other than a reverse lookup on the source + address and insert the resulting FQDN into the Route-Record AVP. + + Diameter agents and servers SHOULD check whether a NAS-Identifier AVP + corresponds to an entry in the Route-Record AVP. If no match is + found, then an error is logged, but no other action is taken. + */ + + /* copy the alias */ + char * str; + int found, ret; + struct addrinfo hint, *res; + CHECK_MALLOC( str = malloc(nas_id->length - sizeof(struct radius_attr_hdr) + 1) ); + memcpy(str, nas_id + 1, nas_id->length - sizeof(struct radius_attr_hdr)); + str[nas_id->length - sizeof(struct radius_attr_hdr)] = '\0'; + + /* Check if this alias is already in the aliases list */ + if (!strcasecmp(str, cli->fqdn)) { + TRACE_DEBUG(FULL, "NAS-Identifier contains the fqdn of the NAS"); + found = 1; + } else { + for (idx = 0; idx < cli->aliases_nb; idx++) { + if (!strcasecmp(str, cli->aliases[idx])) { + TRACE_DEBUG(FULL, "NAS-Identifier valid value found in the cache"); + found = 1; + break; + } + } + } + + if (found) { + free(str); + msg->valid_nas_info |= 2; + goto end; + } + + /* Now check if this alias is valid for this peer */ + memset(&hint, 0, sizeof(hint)); + hint.ai_family = cli->sa->sa_family; + hint.ai_flags = AI_CANONNAME; + ret = getaddrinfo(str, NULL, &hint, &res); + if (ret) { + TRACE_DEBUG(INFO, "Error while resolving NAS-Identifier value '%s': %s. Discarding message...", str, gai_strerror(ret)); + free(str); + return EINVAL; + } + if (strcasecmp(cli->fqdn, res->ai_canonname)) { + TRACE_DEBUG(INFO, "The NAS-Identifier value is not valid: '%s' resolved to '%s', expected '%s'. Discarding...", str, res->ai_canonname, cli->fqdn); + free(str); + freeaddrinfo(res); + return EINVAL; + } + + /* It is a valid alias, save it */ + freeaddrinfo(res); + CHECK_MALLOC( cli->aliases = realloc(cli->aliases, (cli->aliases_nb + 1) * sizeof(char *)) ); + cli->aliases[cli->aliases_nb + 1] = str; + cli->aliases_nb ++; + TRACE_DEBUG(FULL, "Saved valid alias for client: '%s' -> '%s'", str, cli->fqdn); + msg->valid_nas_info |= 2; + } +end: + return 0; +} + +int rgw_clients_get_origin(struct rgw_client *cli, char **fqdn, char **realm) +{ + TRACE_ENTRY("%p %p %p", cli, fqdn, realm); + CHECK_PARAMS(cli && fqdn); + + *fqdn = cli->fqdn; + if (realm) + *realm= cli->realm; + return 0; +} + + +void rgw_clients_dispose(struct rgw_client ** ref) +{ + TRACE_ENTRY("%p", ref); + CHECK_PARAMS_DO(ref, return); + + CHECK_POSIX_DO( pthread_mutex_lock(&cli_mtx), ); + client_unlink(*ref); + *ref = NULL; + CHECK_POSIX_DO( pthread_mutex_unlock(&cli_mtx), ); +} + +int rgw_clients_add( struct sockaddr * ip_port, unsigned char ** key, size_t keylen ) +{ + struct rgw_client * prev = NULL, *new = NULL; + int ret; + + TRACE_ENTRY("%p %p %lu", ip_port, key, keylen); + + CHECK_PARAMS( ip_port && key && *key && keylen ); + CHECK_PARAMS( (ip_port->sa_family == AF_INET) || (ip_port->sa_family == AF_INET6) ); + + /* Dump the entry in debug mode */ + if (TRACE_BOOL(FULL + 1 )) { + TRACE_DEBUG(FULL, "Adding client:\n"); + TRACE_DEBUG_sSA(FULL, "\tIP : ", ip_port, NI_NUMERICHOST | NI_NUMERICSERV, "\n" ); + TRACE_DEBUG_BUFFER(FULL, "\tKey: [", *key, keylen, "]\n" ); + } + + /* Lock the lists */ + CHECK_POSIX( pthread_mutex_lock(&cli_mtx) ); + + /* Check if the same entry does not already exist */ + ret = client_search(&prev, ip_port ); + if (ret == ENOENT) { + /* No duplicate found, Ok to add */ + CHECK_FCT_DO( ret = client_create( &new, &ip_port, key, keylen ), goto end ); + fd_list_insert_after(&prev->chain, &new->chain); + new->refcount++; + ret = 0; + goto end; + } + + if (ret == EEXIST) { + /* Check if the key is the same, then skip or return an error */ + if ((keylen == prev->key.len ) && ( ! memcmp(*key, prev->key.data, keylen) )) { + TRACE_DEBUG(INFO, "Skipping duplicate client description"); + ret = 0; + goto end; + } + + fd_log_debug("ERROR: Conflicting RADIUS clients descriptions!\n"); + TRACE_DEBUG(NONE, "Previous entry:\n"); + TRACE_DEBUG_sSA(NONE, "\tIP : ", prev->sa, NI_NUMERICHOST | NI_NUMERICSERV, "\n" ); + TRACE_DEBUG_BUFFER(NONE, "\tKey: [", prev->key.data, prev->key.len, "]\n" ); + TRACE_DEBUG(NONE, "Conflicting entry:\n"); + TRACE_DEBUG_sSA(NONE, "\tIP : ", ip_port, NI_NUMERICHOST | NI_NUMERICSERV, "\n" ); + TRACE_DEBUG_BUFFER(NONE, "\tKey: [", *key, keylen, "]\n" ); + } +end: + /* release the lists */ + CHECK_POSIX( pthread_mutex_unlock(&cli_mtx) ); + + return ret; +} + +static void dump_cli_list(struct fd_list *senti) +{ + struct rgw_client * client = NULL; + struct fd_list *ref = NULL; + + for (ref = senti->next; ref != senti; ref = ref->next) { + client = (struct rgw_client *)ref; + TRACE_DEBUG_sSA(NONE, " - ", client->sa, NI_NUMERICHOST | NI_NUMERICSERV, "\n" ); + TRACE_DEBUG_BUFFER(NONE, " [", client->key.data, client->key.len, "]\n" ); + } +} + +void rgw_clients_dump(void) +{ + if ( ! TRACE_BOOL(FULL) ) + return; + + CHECK_POSIX_DO( pthread_mutex_lock(&cli_mtx), /* ignore error */ ); + + if (!FD_IS_LIST_EMPTY(&cli_ip)) + fd_log_debug(" RADIUS IP clients list:\n"); + dump_cli_list(&cli_ip); + + if (!FD_IS_LIST_EMPTY(&cli_ip6)) + fd_log_debug(" RADIUS IPv6 clients list:\n"); + dump_cli_list(&cli_ip6); + + CHECK_POSIX_DO( pthread_mutex_unlock(&cli_mtx), /* ignore error */ ); +} + +void rgw_clients_fini(void) +{ + struct fd_list * client; + + TRACE_ENTRY(); + + CHECK_POSIX_DO( pthread_mutex_lock(&cli_mtx), /* ignore error */ ); + + /* empty the lists */ + while ( ! FD_IS_LIST_EMPTY(&cli_ip) ) { + client = cli_ip.next; + fd_list_unlink(client); + client_unlink((struct rgw_client *)client); + } + while (! FD_IS_LIST_EMPTY(&cli_ip6)) { + client = cli_ip6.next; + fd_list_unlink(client); + client_unlink((struct rgw_client *)client); + } + + CHECK_POSIX_DO( pthread_mutex_unlock(&cli_mtx), /* ignore error */ ); + +} + +int rgw_client_finish_send(struct radius_msg ** msg, struct rgw_radius_msg_meta * req, struct rgw_client * cli) +{ + int idx; + + TRACE_ENTRY("%p %p %p", msg, req, cli); + CHECK_PARAMS( msg && *msg && cli ); + + if (!req) { + /* We don't support this case yet */ + ASSERT(0); + return ENOTSUP; + } + + if (radius_msg_finish_srv(*msg, cli->key.data, cli->key.len, req->radius.hdr->authenticator)) { + TRACE_DEBUG(INFO, "An error occurred while preparing the RADIUS answer"); + radius_msg_free(*msg); + free(*msg); + *msg = NULL; + return EINVAL; + } + + /* Debug */ + TRACE_DEBUG(FULL, "RADIUS message ready for sending:"); + rgw_msg_dump((struct rgw_radius_msg_meta *)*msg); + + /* Send the message */ + CHECK_FCT( rgw_servers_send(req->serv_type, (*msg)->buf, (*msg)->buf_used, cli->sa, req->port) ); + + /* update the duplicate cache in rgw_clients */ + if (req->serv_type == RGW_PLG_TYPE_AUTH) + idx = 0; + else + idx = 1; + if (cli->last[idx].ans) { + /* Free it */ + radius_msg_free(cli->last[idx].ans); + free(cli->last[idx].ans); + } + cli->last[idx].ans = *msg; + *msg = NULL; + + /* Finished */ + return 0; +} + diff -r ad6c0118fb50 -r a857024cb48b extensions/app_radgw/rgw_common.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/app_radgw/rgw_common.h Wed Apr 14 18:30:22 2010 +0900 @@ -0,0 +1,222 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis * +* * +* Copyright (c) 2009, WIDE Project and NICT * +* All rights reserved. * +* * +* Redistribution and use of this software in source and binary forms, with or without modification, are * +* permitted provided that the following conditions are met: * +* * +* * Redistributions of source code must retain the above * +* copyright notice, this list of conditions and the * +* following disclaimer. * +* * +* * Redistributions in binary form must reproduce the above * +* copyright notice, this list of conditions and the * +* following disclaimer in the documentation and/or other * +* materials provided with the distribution. * +* * +* * Neither the name of the WIDE Project or NICT nor the * +* names of its contributors may be used to endorse or * +* promote products derived from this software without * +* specific prior written permission of WIDE Project and * +* NICT. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED * +* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * +* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * +* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * +* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +*********************************************************************************************************/ + +/* This file contains definitions for both app_radgw extension and its plugins. */ + +#ifndef _RGW_COMMON_H +#define _RGW_COMMON_H + +/* Include definitions from the freeDiameter framework */ +#include + +/* Include hostap files for RADIUS processings */ +#include "hostap_compat.h" +#include "md5.h" +#include "radius.h" + + +/**************************************************************/ +/* Interface with gateway's plug-ins */ +/**************************************************************/ +/* This structure is private for each plugin */ +struct rgwp_config; + +/* This structure points to a RADIUS client description, the definition is not known to plugins */ +struct rgw_client; + +/* Each plugin must provide the following structure. */ +extern struct rgw_api { + /* Parse the configuration file. It may be called several times with different configurations. + Returns NULL on errors. + Called even if no configuration file is passed (with NULL parameter then) */ + struct rgwp_config * (*rgwp_conf_parse) ( char * conf_file ); + + /* Cleanup the configuration state when the daemon is exiting. */ + void (*rgwp_conf_free) (struct rgwp_config * conf); + + /* handle an incoming RADIUS message */ + int (*rgwp_rad_req) ( struct rgwp_config * conf, struct session * session, struct radius_msg * rad_req, struct radius_msg ** rad_ans, struct msg ** diam_fw, struct rgw_client * cli ); + /* ret 0: continue; + ret -1: stop processing this message + ret -2: reply the content of rad_ans to the RADIUS client immediatly + ret >0: critical error (errno), log and exit. + */ + + /* handle the corresponding Diameter answer */ + int (*rgwp_diam_ans) ( struct rgwp_config * conf, struct session * session, struct msg ** diam_ans, struct radius_msg ** rad_fw, struct rgw_client * cli ); + /* ret 0: continue; ret >0: error; ret: -1 ... (tbd) */ + +} rgwp_descriptor; + + + +/**************************************************************/ +/* Additional definitions */ +/**************************************************************/ +/* Type of message / server */ +#define RGW_PLG_TYPE_AUTH 1 +#define RGW_PLG_TYPE_ACCT 2 + +/* Attributes missing from radius.h (not used in EAP) */ +enum { RADIUS_ATTR_CHAP_PASSWORD = 3, + RADIUS_ATTR_SERVICE_TYPE = 6, + RADIUS_ATTR_FRAMED_PROTOCOL = 7, + RADIUS_ATTR_FRAMED_IP_ADDRESS = 8, + RADIUS_ATTR_FRAMED_IP_NETMASK = 9, + RADIUS_ATTR_FRAMED_ROUTING = 10, + RADIUS_ATTR_FILTER_ID = 11, + RADIUS_ATTR_FRAMED_COMPRESSION = 13, + RADIUS_ATTR_LOGIN_IP_HOST = 14, + RADIUS_ATTR_LOGIN_SERVICE = 15, + RADIUS_ATTR_LOGIN_TCP_PORT = 16, + RADIUS_ATTR_CALLBACK_NUMBER = 19, + RADIUS_ATTR_CALLBACK_ID = 20, + RADIUS_ATTR_FRAMED_ROUTE = 22, + RADIUS_ATTR_FRAMED_IPX_NETWORK = 23, + RADIUS_ATTR_LOGIN_LAT_SERVICE = 34, + RADIUS_ATTR_LOGIN_LAT_NODE = 35, + RADIUS_ATTR_LOGIN_LAT_GROUP = 36, + RADIUS_ATTR_FRAMED_APPLETALK_LINK = 37, + RADIUS_ATTR_FRAMED_APPLETALK_NETWORK = 38, + RADIUS_ATTR_FRAMED_APPLETALK_ZONE = 39, + RADIUS_ATTR_CHAP_CHALLENGE = 60, + RADIUS_ATTR_PORT_LIMIT = 62, + RADIUS_ATTR_LOGIN_LAT_PORT = 63, + RADIUS_ATTR_TUNNEL_CLIENT_ENDPOINT = 66, + RADIUS_ATTR_TUNNEL_SERVER_ENDPOINT = 67, + RADIUS_ATTR_TUNNEL_PASSWORD = 69, + RADIUS_ATTR_ARAP_PASSWORD = 70, + RADIUS_ATTR_ARAP_FEATURES = 71, + RADIUS_ATTR_ARAP_ZONE_ACCESS = 72, + RADIUS_ATTR_ARAP_SECURITY = 73, + RADIUS_ATTR_ARAP_SECURITY_DATA = 74, + RADIUS_ATTR_PASSWORD_RETRY = 75, + RADIUS_ATTR_PROMPT = 76, + RADIUS_ATTR_CONFIGURATION_TOKEN = 78, + RADIUS_ATTR_TUNNEL_ASSIGNEMENT_ID = 82, + RADIUS_ATTR_TUNNEL_PREFERENCE = 83, + RADIUS_ATTR_ARAP_CHALLENGE_RESPONSE = 84, + RADIUS_ATTR_NAS_PORT_ID = 87, + RADIUS_ATTR_FRAMED_POOL = 88, + RADIUS_ATTR_TUNNEL_CLIENT_AUTH_ID = 90, + RADIUS_ATTR_TUNNEL_SERVER_AUTH_ID = 91, + RADIUS_ATTR_ORIGINATING_LINE_INFO = 94, + RADIUS_ATTR_FRAMED_INTERFACE_ID = 96, + RADIUS_ATTR_FRAMED_IPV6_PREFIX = 97, + RADIUS_ATTR_LOGIN_IPV6_HOST = 98, + RADIUS_ATTR_FRAMED_IPV6_ROUTE = 99, + RADIUS_ATTR_FRAMED_IPV6_POOL = 100, + RADIUS_ATTR_ERROR_CAUSE = 101, + RADIUS_ATTR_EAP_KEY_NAME = 102 +}; + +enum { DIAM_ATTR_USER_NAME = 1, + DIAM_ATTR_USER_PASSWORD = 2, + DIAM_ATTR_SERVICE_TYPE = 6, + DIAM_ATTR_FRAMED_PROTOCOL = 7, + DIAM_ATTR_FRAMED_IP_ADDRESS = 8, + DIAM_ATTR_FRAMED_IP_NETMASK = 9, + DIAM_ATTR_FRAMED_ROUTING = 10, + DIAM_ATTR_FILTER_ID = 11, + DIAM_ATTR_FRAMED_MTU = 12, + DIAM_ATTR_FRAMED_COMPRESSION = 13, + DIAM_ATTR_LOGIN_IP_HOST = 14, + DIAM_ATTR_LOGIN_SERVICE = 15, + DIAM_ATTR_LOGIN_TCP_PORT = 16, + DIAM_ATTR_REPLY_MESSAGE = 18, + DIAM_ATTR_CALLBACK_NUMBER = 19, + DIAM_ATTR_CALLBACK_ID = 20, + DIAM_ATTR_FRAMED_ROUTE = 22, + DIAM_ATTR_FRAMED_IPX_NETWORK = 23, + DIAM_ATTR_STATE = 24, + DIAM_ATTR_CLASS = 25, + DIAM_ATTR_IDLE_TIMEOUT = 28, + DIAM_ATTR_LOGIN_LAT_SERVICE = 34, + DIAM_ATTR_LOGIN_LAT_NODE = 35, + DIAM_ATTR_LOGIN_LAT_GROUP = 36, + DIAM_ATTR_FRAMED_APPLETALK_LINK = 37, + DIAM_ATTR_FRAMED_APPLETALK_NETWORK = 38, + DIAM_ATTR_FRAMED_APPLETALK_ZONE = 39, + DIAM_ATTR_PORT_LIMIT = 62, + DIAM_ATTR_LOGIN_LAT_PORT = 63, + DIAM_ATTR_TUNNEL_TYPE = 64, + DIAM_ATTR_TUNNEL_MEDIUM_TYPE = 65, + DIAM_ATTR_TUNNEL_CLIENT_ENDPOINT = 66, + DIAM_ATTR_TUNNEL_SERVER_ENDPOINT = 67, + DIAM_ATTR_TUNNEL_PASSWORD = 69, + DIAM_ATTR_ARAP_FEATURES = 71, + DIAM_ATTR_ARAP_ZONE_ACCESS = 72, + DIAM_ATTR_ARAP_SECURITY = 73, + DIAM_ATTR_ARAP_SECURITY_DATA = 74, + DIAM_ATTR_PASSWORD_RETRY = 75, + DIAM_ATTR_PROMPT = 76, + DIAM_ATTR_CONFIGURATION_TOKEN = 78, + DIAM_ATTR_TUNNEL_PRIVATE_GROUP_ID = 81, + DIAM_ATTR_TUNNEL_ASSIGNEMENT_ID = 82, + DIAM_ATTR_TUNNEL_PREFERENCE = 83, + DIAM_ATTR_ARAP_CHALLENGE_RESPONSE = 84, + DIAM_ATTR_ACCT_INTERIM_INTERVAL = 85, + DIAM_ATTR_FRAMED_POOL = 88, + DIAM_ATTR_TUNNEL_CLIENT_AUTH_ID = 90, + DIAM_ATTR_TUNNEL_SERVER_AUTH_ID = 91, + DIAM_ATTR_FRAMED_INTERFACE_ID = 96, + DIAM_ATTR_FRAMED_IPV6_PREFIX = 97, + DIAM_ATTR_LOGIN_IPV6_HOST = 98, + DIAM_ATTR_FRAMED_IPV6_ROUTE = 99, + DIAM_ATTR_FRAMED_IPV6_POOL = 100, + DIAM_ATTR_EAP_KEY_NAME = 102, + DIAM_ATTR_AUTH_APPLICATION_ID = 258, + DIAM_ATTR_MULTI_ROUND_TIMEOUT = 272, + DIAM_ATTR_AUTH_REQUEST_TYPE = 274, + DIAM_ATTR_AUTH_GRACE_PERIOD = 276, + DIAM_ATTR_AUTH_SESSION_STATE = 277, + DIAM_ATTR_ORIGIN_STATE_ID = 278, + DIAM_ATTR_FAILED_AVP = 279, + DIAM_ATTR_ERROR_MESSAGE = 281, + DIAM_ATTR_ROUTE_RECORD = 282, + DIAM_ATTR_PROXY_INFO = 284, + DIAM_ATTR_ERROR_REPORTING_HOST = 294, + DIAM_ATTR_NAS_FILTER_RULE = 400, + DIAM_ATTR_TUNNELING = 401, + DIAM_ATTR_QOS_FILTER_RULE = 407, + DIAM_ATTR_ORIGIN_AAA_PROTOCOL = 408, + DIAM_ATTR_EAP_PAYLOAD = 462, + DIAM_ATTR_EAP_REISSUED_PAYLOAD = 463, + DIAM_ATTR_EAP_MASTER_SESSION_KEY = 464, + DIAM_ATTR_ACCOUNTING_EAP_AUTH_METHOD = 465 +}; + +#endif /* _RGW_COMMON_H */ + diff -r ad6c0118fb50 -r a857024cb48b extensions/app_radgw/rgw_conf.l --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/app_radgw/rgw_conf.l Wed Apr 14 18:30:22 2010 +0900 @@ -0,0 +1,276 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis * +* * +* Copyright (c) 2009, WIDE Project and NICT * +* All rights reserved. * +* * +* Redistribution and use of this software in source and binary forms, with or without modification, are * +* permitted provided that the following conditions are met: * +* * +* * Redistributions of source code must retain the above * +* copyright notice, this list of conditions and the * +* following disclaimer. * +* * +* * Redistributions in binary form must reproduce the above * +* copyright notice, this list of conditions and the * +* following disclaimer in the documentation and/or other * +* materials provided with the distribution. * +* * +* * Neither the name of the WIDE Project or NICT nor the * +* names of its contributors may be used to endorse or * +* promote products derived from this software without * +* specific prior written permission of WIDE Project and * +* NICT. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED * +* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * +* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * +* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * +* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +*********************************************************************************************************/ +/* Lex configuration parser for radius_gw extension. + * + */ + +%{ +#include "rgw.h" +#include "rgw_conf.tab.h" + +/* Update the column information */ +#define YY_USER_ACTION { \ + yylloc->first_column = yylloc->last_column + 1; \ + yylloc->last_column = yylloc->first_column + yyleng - 1; \ +} + +/* %option noinput ? */ +#define YY_NO_INPUT +%} + +%option bison-bridge bison-locations +%option noyywrap +%option nounput + +/* Use the following start condition to parse an URI */ +%x IN_PLG +%x IN_CLI1 +%x IN_CLI2 +%x EXPECT_IP4 +%x EXPECT_IP6 +%x EXPECT_DECINT + +/* Quoted string. Multilines do not match. */ +qstring \"[^\"\n]*\" + +/* Used to match IP, IP6, and port */ +IP4 [0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3} +IP6 [[:xdigit:]]*:[[:xdigit:]]*:[[:xdigit:].:]* +BR_PORT [[][0-9]+[]] + + +%% + + /* All sections */ +<*>\n { + /* Update the line count */ + yylloc->first_line++; + yylloc->last_line++; + yylloc->last_column=0; + } + +<*>([[:space:]]{-}[\n])+ ; /* Eat all spaces, not new lines */ +<*>#.*$ ; /* Eat all comments */ + +<*>{qstring} { + /* First copy the string without the quotes for use in the yacc parser */ + yylval->string = strdup(yytext+1); + if (yylval->string == NULL) { + fd_log_debug("Unable to allocate memory: %s\n", strerror(errno)); + return LEX_ERROR; /* trig an error in yacc parser */ + } + + yylval->string[yyleng-2] = '\0'; + + /* the yacc parser will check the string is valid */ + return QSTRING; + } + + + /* Extension section */ +(?i:"RGWX") { BEGIN(IN_PLG); return PLG_PREFIX; } + +(?i:"auth") { return AUTH; } +(?i:"acct") { return ACCT; } + +[[:xdigit:]]+ { + /* Convert this to an integer value */ + int ret = sscanf(yytext, "%x", &yylval->integer); + if (ret != 1) { + /* No matching: an error occurred */ + fd_log_debug("Unable to convert the value '%s' to a valid number: %s\n", yytext, strerror(errno)); + return LEX_ERROR; /* trig an error in yacc parser */ + /* Maybe we could REJECT instead of failing here? */ + } + return INTEGER; + } + +[:] { return yytext[0]; } + + + /* Client section */ +(?i:"cli") { BEGIN(IN_CLI1); return CLI_PREFIX; } + + /* Match an IP (4 or 6) and optional port */ +({IP4}|{IP6}){BR_PORT}? { + char * work; + char * port; + unsigned short p = 0; + + work = strdup(yytext); + if ( work == NULL ) { + fd_log_debug("Unable to allocate memory: %s\n", strerror(errno)); + return LEX_ERROR; /* trig an error in yacc parser */ + } + + if (port = strchr(work, '[')) { + *port = '\0'; + port++; + if (sscanf(port, "%hu]", &p) != 1) { + fd_log_debug("'%s' is not a valid port: %s\n", port, strerror(errno)); + free(work); + return LEX_ERROR; /* trig an error in yacc parser */ + } + } + + /* Do we have an IP or IPv6? Let's check if we have ':' char somewhere in the beginning */ + if (memchr(work, ':', 5) != NULL) { + struct sockaddr_in6 * sin6 = NULL; + + sin6 = malloc(sizeof(struct sockaddr_in6)); + if (sin6 == NULL) { + fd_log_debug("Unable to allocate memory: %s\n", strerror(errno)); + free(work); + return LEX_ERROR; /* trig an error in yacc parser */ + } + + memset(sin6, 0, sizeof(struct sockaddr_in6)); + sin6->sin6_family = AF_INET6; + if (inet_pton(AF_INET6, work, &sin6->sin6_addr) != 1) { + fd_log_debug("'%s' is not a valid IPv6 address: %s\n", work, strerror(errno)); + free(work); + free(sin6); + return LEX_ERROR; /* trig an error in yacc parser */ + } + sin6->sin6_port = htons(p); + yylval->ss = (struct sockaddr *)sin6; + } else { + struct sockaddr_in * sin = NULL; + + sin = malloc(sizeof(struct sockaddr_in)); + if (sin == NULL) { + fd_log_debug("Unable to allocate memory: %s\n", strerror(errno)); + free(work); + return LEX_ERROR; /* trig an error in yacc parser */ + } + + memset(sin, 0, sizeof(struct sockaddr_in)); + sin->sin_family = AF_INET; + if (inet_pton(AF_INET, work, &sin->sin_addr) != 1) { + fd_log_debug("'%s' is not a valid IP address: %s\n", work, strerror(errno)); + free(work); + free(sin); + return LEX_ERROR; /* trig an error in yacc parser */ + } + + sin->sin_port = htons(p); + yylval->ss = (struct sockaddr *)sin; + } + free(work); + return IP; + } + + +"/" { BEGIN(IN_CLI2); return '/'; } + + + /* Servers section */ +(?i:"auth_server_enable") { return AUTH_ENABLE; } +(?i:"auth_server_port") { BEGIN(EXPECT_DECINT); return AUTH_PORT; } +(?i:"auth_server_ip4") { BEGIN(EXPECT_IP4); return AUTH_IP4; } +(?i:"auth_server_ip6") { BEGIN(EXPECT_IP6); return AUTH_IP6; } +(?i:"acct_server_enable") { return ACCT_ENABLE; } +(?i:"acct_server_port") { BEGIN(EXPECT_DECINT); return ACCT_PORT; } +(?i:"acct_server_ip4") { BEGIN(EXPECT_IP4); return ACCT_IP4; } +(?i:"acct_server_ip6") { BEGIN(EXPECT_IP6); return ACCT_IP6; } + +[[:digit:]]+ { + /* Match an integer (not hexa) */ + int ret = sscanf(yytext, "%d", &yylval->integer); + if (ret != 1) { + /* No matching: an error occurred */ + fd_log_debug("Unable to convert the value '%s' to a valid number: %s\n", yytext, strerror(errno)); + return LEX_ERROR; /* trig an error in yacc parser */ + /* Maybe we could REJECT instead of failing here? */ + } + return INTEGER; + } + +(?i:"disable") { return DISABLED; } + +{IP4} { + struct sockaddr_in * sin = NULL; + + sin = malloc(sizeof(struct sockaddr_in)); + if (sin == NULL) { + fd_log_debug("Unable to allocate memory: %s\n", strerror(errno)); + return LEX_ERROR; /* trig an error in yacc parser */ + } + + memset(sin, 0, sizeof(struct sockaddr_in)); + sin->sin_family = AF_INET; + if (inet_pton(AF_INET, yytext, &sin->sin_addr) != 1) { + fd_log_debug("'%s' is not a valid IP address: %s\n", yytext, strerror(errno)); + free(sin); + return LEX_ERROR; /* trig an error in yacc parser */ + } + yylval->ss = (struct sockaddr *)sin; + return IP; + } + +{IP6} { + struct sockaddr_in6 * sin6 = NULL; + + sin6 = malloc(sizeof(struct sockaddr_in6)); + if (sin6 == NULL) { + fd_log_debug("Unable to allocate memory: %s\n", strerror(errno)); + return LEX_ERROR; /* trig an error in yacc parser */ + } + + memset(sin6, 0, sizeof(struct sockaddr_in6)); + sin6->sin6_family = AF_INET6; + if (inet_pton(AF_INET6, yytext, &sin6->sin6_addr) != 1) { + fd_log_debug("'%s' is not a valid IPv6 address: %s\n", yytext, strerror(errno)); + free(sin6); + return LEX_ERROR; /* trig an error in yacc parser */ + } + yylval->ss = (struct sockaddr *)sin6; + return IP; + } + + + /* Valid single characters for yyparse in all contexts */ +<*>[=] { return yytext[0]; } +<*>[;] { BEGIN(INITIAL); return yytext[0]; } + + /* Unrecognized token */ +<*>[[:alnum:]]+ | /* This rule is only useful to print a complete token in error messages */ + /* Unrecognized character */ +<*>. { + fd_log_debug("Unrecognized text on line %d col %d: '%s'.\n", yylloc->first_line, yylloc->first_column, yytext); + return LEX_ERROR; + } + +%% diff -r ad6c0118fb50 -r a857024cb48b extensions/app_radgw/rgw_conf.y --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/app_radgw/rgw_conf.y Wed Apr 14 18:30:22 2010 +0900 @@ -0,0 +1,404 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis * +* * +* Copyright (c) 2009, WIDE Project and NICT * +* All rights reserved. * +* * +* Redistribution and use of this software in source and binary forms, with or without modification, are * +* permitted provided that the following conditions are met: * +* * +* * Redistributions of source code must retain the above * +* copyright notice, this list of conditions and the * +* following disclaimer. * +* * +* * Redistributions in binary form must reproduce the above * +* copyright notice, this list of conditions and the * +* following disclaimer in the documentation and/or other * +* materials provided with the distribution. * +* * +* * Neither the name of the WIDE Project or NICT nor the * +* names of its contributors may be used to endorse or * +* promote products derived from this software without * +* specific prior written permission of WIDE Project and * +* NICT. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED * +* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * +* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * +* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * +* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +*********************************************************************************************************/ + +/* Yacc extension's configuration parser. + * See doc/app_radgw.conf.sample for configuration file format + */ + +/* For development only : */ +%debug +%error-verbose + +/* The parser receives the configuration file filename as parameter */ +%parse-param {char * conffile} + +/* Keep track of location */ +%locations +%pure-parser + +%{ +#include "rgw.h" +#include "rgw_conf.tab.h" /* bison is not smart enough to define the YYLTYPE before including this code, so... */ + +#include +#include +#include +#include + +/* Forward declaration */ +int yyparse(char * conffile); + +/* Parse the configuration file */ +int rgw_conf_handle(char * conffile) +{ + extern FILE * rgw_confin; + int ret; + + rgw_confin = fopen(conffile, "r"); + if (rgw_confin == NULL) { + ret = errno; + fd_log_debug("Unable to open extension configuration file %s for reading: %s\n", conffile, strerror(ret)); + return ret; + } + + ret = rgw_confparse(conffile); + + fclose(rgw_confin); + + if (ret != 0) { + return EINVAL; + } + + return 0; +} + +/* The Lex parser prototype */ +int rgw_conflex(YYSTYPE *lvalp, YYLTYPE *llocp); + +/* Function to report the errors */ +void yyerror (YYLTYPE *ploc, char * conffile, char const *s) +{ + if (ploc->first_line != ploc->last_line) + fd_log_debug("%s:%d.%d-%d.%d : %s\n", conffile, ploc->first_line, ploc->first_column, ploc->last_line, ploc->last_column, s); + else if (ploc->first_column != ploc->last_column) + fd_log_debug("%s:%d.%d-%d : %s\n", conffile, ploc->first_line, ploc->first_column, ploc->last_column, s); + else + fd_log_debug("%s:%d.%d : %s\n", conffile, ploc->first_line, ploc->first_column, s); +} + +/* This function checks a string value is a valid filename */ +static int is_valid_file( char * candidate ) +{ + int ret; + struct stat buffer; + + ret = stat(candidate, &buffer); + if (ret != 0) { + fd_log_debug("Error on file '%s': %s.\n", candidate, strerror(errno)); + return 0; + } + + /* Ok this candidate is valid */ + return 1; +} + +/* Very simple byte stack management */ +static unsigned char * buf = NULL; +static size_t buf_sz = 0; +static size_t buf_rsz = 0; +static inline int buf_add(unsigned char val) /* add a value in the array */ +{ + buf_sz++; + + if (buf_sz > buf_rsz) { + void * rez=NULL; + buf_rsz += 256; + rez = realloc(buf, buf_rsz); + if (rez == NULL) { + fd_log_debug("Error on memory allocation: %s\n", strerror(errno)); + return 0; + } + buf = (unsigned char *)rez; + } + buf[buf_sz - 1] = val; + return 1; +} +static inline void buf_reinit(void) +{ + free(buf); + buf = NULL; + buf_sz = 0; + buf_rsz = 0; +} + +static int port = 0; +static char * plgconffile = NULL; + +%} + +/* Values returned by lex for token */ +%union { + char *string; /* The string is allocated by strdup in lex.*/ + int integer; /* Store integer values */ + struct sockaddr *ss; /* sockaddr to free after use (alloc in lex) */ +} + +/* typed data */ +%token QSTRING +%token INTEGER +%token IP + +%type FILENAME + +/* simple tokens */ +%token DISABLED +%token AUTH +%token ACCT + +%token PLG_PREFIX +%token CLI_PREFIX + +%token AUTH_ENABLE +%token AUTH_PORT +%token AUTH_IP4 +%token AUTH_IP6 +%token ACCT_ENABLE +%token ACCT_PORT +%token ACCT_IP4 +%token ACCT_IP6 + +/* In case of error in the lexical analysis */ +%token LEX_ERROR + + +/* -------------------------------------- */ +%% + + /* The grammar definition */ +conffile: /* empty grammar is OK */ + | conffile plugin + | conffile clientdef + | conffile authserv + | conffile acctserv + ; + + +/* -------------------------------------- */ +FILENAME: QSTRING + { + /* Verify this is a valid file */ + if (!is_valid_file($1)) { + yyerror (&yylloc, conffile, "Error on file name, aborting..."); + YYERROR; + } + $$ = $1; + } + ; +/* -------------------------------------- */ +plugin: { + /* Reset the parameters */ + buf_reinit(); + port = RGW_PLG_TYPE_AUTH | RGW_PLG_TYPE_ACCT ; + free(plgconffile); plgconffile = NULL; + } + PLG_PREFIX '=' FILENAME plg_attributes ';' + { + /* Add this extension in the list */ + if ( rgw_plg_add( $4, plgconffile, port, &buf, buf_sz ) ) { + yyerror (&yylloc, conffile, "Error parsing / adding extension !"); + YYERROR; + } + + /* Free the array */ + buf_reinit(); + + /* stop conffile from being freed here */ + plgconffile = NULL; + } + ; + +plg_attributes: /* empty */ + | plg_attributes ':' QSTRING + { + plgconffile = $3; + } + | plg_attributes ':' AUTH + { + port = RGW_PLG_TYPE_AUTH; + } + | plg_attributes ':' ACCT + { + port = RGW_PLG_TYPE_ACCT; + } + | plg_attributes ':' extcodes_list + ; + +extcodes_list: /* empty */ + | extcodes_list INTEGER + { + if ($2 < 0 || $2 > 255) { + yyerror (&yylloc, conffile, "Invalid command code value!"); + YYERROR; + } + if ( ! buf_add((unsigned char)$2) ) { + yyerror (&yylloc, conffile, "Error allocating memory!"); + YYERROR; + } + } + ; + +/* -------------------------------------- */ + +clientdef: { + buf_reinit(); + } + CLI_PREFIX '=' IP '/' clisecret_key ';' + { + /* Add this client */ + if ( rgw_clients_add( $4, &buf, buf_sz ) ) { + yyerror (&yylloc, conffile, "Error parsing / adding client !"); + YYERROR; + } + + /* reinit the buffer */ + buf_reinit(); + } + ; + +clisecret_key: /* empty */ + | clisecret_key QSTRING + { + int i; + size_t len = strlen($2); + for (i = 0; i < len; i++) { + if ( ! buf_add( $2 [i] ) ) { + yyerror (&yylloc, conffile, "Memory allocation error."); + YYERROR; + } + } + + free($2); + } + | clisecret_key INTEGER + { + if ( $2 < 0 || $2 > 255 ) { + yyerror (&yylloc, conffile, "Invalid value in key."); + YYERROR; + } + + if ( ! buf_add( $2 ) ) { + yyerror (&yylloc, conffile, "Memory allocation error."); + YYERROR; + } + } + ; + +/* -------------------------------------- */ + +authserv: AUTH_ENABLE '=' INTEGER ';' + { + if ($3 == 0) { + rgw_servers.auth_serv.disabled = 1; + } else { + rgw_servers.auth_serv.disabled = 0; + } + } + | AUTH_PORT '=' INTEGER ';' + { + if ($3 <= 0 || $3 > 65535) { + yyerror (&yylloc, conffile, "Invalid port number !"); + YYERROR; + } + + rgw_servers.auth_serv.port = htons($3); + } + | AUTH_IP4 '=' DISABLED ';' + { + rgw_servers.auth_serv.ip_disabled = 1; + } + | AUTH_IP4 '=' IP ';' + { + if (((struct sockaddr *)($3))->sa_family != AF_INET) { + yyerror (&yylloc, conffile, "Invalid address specification !"); + YYERROR; + } + memcpy( & rgw_servers.auth_serv.ip_endpoint, &((struct sockaddr_in *)($3))->sin_addr, sizeof(struct in_addr) ); + free($3); + rgw_servers.auth_serv.ip_disabled = 0; + } + | AUTH_IP6 '=' DISABLED ';' + { + rgw_servers.auth_serv.ip6_disabled = 1; + } + | AUTH_IP6 '=' IP ';' + { + if (((struct sockaddr *)($3)) -> sa_family != AF_INET6) { + yyerror (&yylloc, conffile, "Invalid address specification !"); + YYERROR; + } + memcpy( & rgw_servers.auth_serv.ip6_endpoint, &((struct sockaddr_in6 *)($3))->sin6_addr, sizeof(struct in6_addr) ); + free($3); + rgw_servers.auth_serv.ip6_disabled = 0; + } + ; + +/* -------------------------------------- */ + +acctserv: ACCT_ENABLE '=' INTEGER ';' + { + if ($3 == 0) { + rgw_servers.acct_serv.disabled = 1; + } else { + rgw_servers.acct_serv.disabled = 0; + } + } + | ACCT_PORT '=' INTEGER ';' + { + if ($3 <= 0 || $3 > 65535) { + yyerror (&yylloc, conffile, "Invalid port number !"); + YYERROR; + } + + rgw_servers.acct_serv.port = htons($3); + } + | ACCT_IP4 '=' DISABLED ';' + { + rgw_servers.acct_serv.ip_disabled = 1; + } + | ACCT_IP4 '=' IP ';' + { + if (((struct sockaddr *)($3)) -> sa_family != AF_INET) { + yyerror (&yylloc, conffile, "Invalid address specification !"); + YYERROR; + } + memcpy( & rgw_servers.auth_serv.ip_endpoint, &((struct sockaddr_in *)($3))->sin_addr, sizeof(struct in_addr) ); + free($3); + rgw_servers.acct_serv.ip_disabled = 0; + } + | ACCT_IP6 '=' DISABLED ';' + { + rgw_servers.acct_serv.ip6_disabled = 1; + } + | ACCT_IP6 '=' IP ';' + { + if (((struct sockaddr *)($3)) -> sa_family != AF_INET6) { + yyerror (&yylloc, conffile, "Invalid address specification !"); + YYERROR; + } + memcpy( & rgw_servers.auth_serv.ip6_endpoint, &((struct sockaddr_in6 *)($3))->sin6_addr, sizeof(struct in6_addr) ); + free($3); + rgw_servers.acct_serv.ip6_disabled = 0; + } + ; diff -r ad6c0118fb50 -r a857024cb48b extensions/app_radgw/rgw_main.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/app_radgw/rgw_main.c Wed Apr 14 18:30:22 2010 +0900 @@ -0,0 +1,76 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis * +* * +* Copyright (c) 2009, WIDE Project and NICT * +* All rights reserved. * +* * +* Redistribution and use of this software in source and binary forms, with or without modification, are * +* permitted provided that the following conditions are met: * +* * +* * Redistributions of source code must retain the above * +* copyright notice, this list of conditions and the * +* following disclaimer. * +* * +* * Redistributions in binary form must reproduce the above * +* copyright notice, this list of conditions and the * +* following disclaimer in the documentation and/or other * +* materials provided with the distribution. * +* * +* * Neither the name of the WIDE Project or NICT nor the * +* names of its contributors may be used to endorse or * +* promote products derived from this software without * +* specific prior written permission of WIDE Project and * +* NICT. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED * +* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * +* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * +* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * +* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +*********************************************************************************************************/ +/* + * Main file of the app_radgw extension. + */ + +#include "rgw.h" + +/* Extension entry point called by freeDiameter */ +static int rgw_main(char * conffile) +{ + CHECK_FCT( rgw_msg_init() ); + + CHECK_FCT( rgw_servers_init() ); + + CHECK_FCT( rgw_conf_handle(conffile) ); + + TRACE_DEBUG(INFO, "Extension RADIUS Gateway initialized with configuration: '%s'", conffile); + rgw_servers_dump(); + rgw_clients_dump(); + rgw_plg_dump(); + + /* Start making extension list accelerators */ + rgw_plg_start_cache(); + + /* Start the worker threads */ + CHECK_FCT( rgw_work_start() ); + + /* Start the servers */ + CHECK_FCT( rgw_servers_start() ); + + return 0; +} + +/* Unload */ +void fd_ext_fini(void) +{ + rgw_servers_fini(); + rgw_work_fini(); + rgw_plg_fini(); + rgw_clients_fini(); +} + +EXTENSION_ENTRY("app_radgw", rgw_main); diff -r ad6c0118fb50 -r a857024cb48b extensions/app_radgw/rgw_msg.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/app_radgw/rgw_msg.c Wed Apr 14 18:30:22 2010 +0900 @@ -0,0 +1,345 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis * +* * +* Copyright (c) 2009, WIDE Project and NICT * +* All rights reserved. * +* * +* Redistribution and use of this software in source and binary forms, with or without modification, are * +* permitted provided that the following conditions are met: * +* * +* * Redistributions of source code must retain the above * +* copyright notice, this list of conditions and the * +* following disclaimer. * +* * +* * Redistributions in binary form must reproduce the above * +* copyright notice, this list of conditions and the * +* following disclaimer in the documentation and/or other * +* materials provided with the distribution. * +* * +* * Neither the name of the WIDE Project or NICT nor the * +* names of its contributors may be used to endorse or * +* promote products derived from this software without * +* specific prior written permission of WIDE Project and * +* NICT. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED * +* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * +* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * +* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * +* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +*********************************************************************************************************/ + +/* This file contains all support functions to parse, create, and manipulate RADIUS messages. Other +modules do not need to "know" the actual representation of RADIUS messages on the network. They only +receive the logical view as exposed in the rgw.h file. This file extends the content of the radius.c +file functions (from hostap project).*/ + +#include "rgw.h" + +/* Directly include the code of the functions for dumping (generated from the IANA registries) */ +#include "rgw_msg_codes.c" +#include "rgw_msg_attrtype.c" + +/* Destroy a message */ +void rgw_msg_free(struct rgw_radius_msg_meta ** msg) +{ + if (!msg || !*msg) + return; + + radius_msg_free(&(*msg)->radius); + free(*msg); + *msg = NULL; +} + +/* This function creates a rgw_radius_msg_meta structure after parsing a RADIUS buffer */ +int rgw_msg_parse(unsigned char * buf, size_t len, struct rgw_radius_msg_meta ** msg) +{ + struct radius_msg * temp_msg = NULL; + + TRACE_ENTRY("%p %g %p", buf, len, msg); + + CHECK_PARAMS( buf && len && msg ); + + *msg = NULL; + + /* Parse the RADIUS message */ + temp_msg = radius_msg_parse(buf, len); + if (temp_msg == NULL) { + TRACE_DEBUG(INFO, "Error parsing the RADIUS message, discarding"); + return EINVAL; + } + + /* Now alloc space for the meta-data */ + CHECK_MALLOC( *msg = realloc(temp_msg, sizeof(struct rgw_radius_msg_meta)) ); + + /* Clear memory after the parsed data */ + memset( &(*msg)->radius + 1, 0, sizeof(struct rgw_radius_msg_meta) - sizeof(struct radius_msg) ); + + return 0; +} + +/* Check if the message has a valid authenticator, and update the meta-data accordingly */ +int rgw_msg_auth_check(struct rgw_radius_msg_meta * msg, struct rgw_client * cli, uint8_t * req_auth) +{ + unsigned char * key; + size_t keylen; + int count; + + TRACE_ENTRY("%p %p %p", msg, cli, req_auth); + + CHECK_PARAMS(msg && cli); + + CHECK_FCT(rgw_clients_getkey(cli, &key, &keylen)); + + count = radius_msg_count_attr(&msg->radius, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, 0); + if (count > 1) { + TRACE_DEBUG(INFO, "Too many Message-Authenticator attributes (%d), discarding message.", count); + return EINVAL; + } + if (count == 0) { + TRACE_DEBUG(FULL, "Message does not contain a Message-Authenticator attributes."); + msg->valid_mac = 0; + } else { + if (radius_msg_verify_msg_auth( &msg->radius, key, keylen, req_auth )) { + TRACE_DEBUG(INFO, "Invalid Message-Authenticator received, discarding message."); + return EINVAL; + } + msg->valid_mac = 1; + } + + return 0; +} + +/* Dump a message (inspired from radius_msg_dump) -- can be used safely with a struct radius_msg as parameter (we don't dump the metadata) */ +void rgw_msg_dump(struct rgw_radius_msg_meta * msg) +{ + unsigned char *auth; + size_t i; + if (! TRACE_BOOL(FULL) ) + return; + + auth = &(msg->radius.hdr->authenticator[0]); + + fd_log_debug("------ RADIUS msg dump -------\n"); + fd_log_debug(" id : 0x%02hhx, code : %hhd (%s)\n", msg->radius.hdr->identifier, msg->radius.hdr->code, rgw_msg_code_str(msg->radius.hdr->code)); + fd_log_debug(" auth: %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx\n", + auth[0], auth[1], auth[2], auth[3], + auth[4], auth[5], auth[6], auth[7]); + fd_log_debug(" %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx\n", + auth[8], auth[9], auth[10], auth[11], + auth[12], auth[13], auth[14], auth[15]); + for (i = 0; i < msg->radius.attr_used; i++) { + struct radius_attr_hdr *attr = (struct radius_attr_hdr *)(msg->radius.buf + msg->radius.attr_pos[i]); + fd_log_debug(" - len:%3hhu, type:0x%02hhx (%s)\n", attr->length, attr->type, rgw_msg_attrtype_str(attr->type)); + /* If we need to dump the value, it's better to call directly radius_msg_dump instead... */ + } + fd_log_debug("-----------------------------\n"); +} + +static struct dict_object * cache_sess_id = NULL; +static struct dict_object * cache_dest_host = NULL; +static struct dict_object * cache_dest_realm = NULL; +static struct dict_object * cache_orig_host = NULL; +static struct dict_object * cache_orig_realm = NULL; + +int rgw_msg_init(void) +{ + TRACE_ENTRY(); + CHECK_FCT( fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Session-Id", &cache_sess_id, ENOENT) ); + CHECK_FCT( fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Destination-Host", &cache_dest_host, ENOENT) ); + CHECK_FCT( fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Destination-Realm", &cache_dest_realm, ENOENT) ); + CHECK_FCT( fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Host", &cache_orig_host, ENOENT) ); + CHECK_FCT( fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Realm", &cache_orig_realm, ENOENT) ); + return 0; +} + +/* Create a msg with origin-host & realm, and session-id, and a session object from a RADIUS request message */ +int rgw_msg_create_base(struct rgw_radius_msg_meta * msg, struct rgw_client * cli, struct session ** session, struct msg ** diam) +{ + int idx, i; + const char * prefix = "Diameter/"; + size_t pref_len; + char * dh = NULL; + size_t dh_len = 0; + char * dr = NULL; + size_t dr_len = 0; + char * si = NULL; + size_t si_len = 0; + char * un = NULL; + size_t un_len = 0; + + char * fqdn; + char * realm; + char * sess_str = NULL; + + struct avp *avp = NULL; + union avp_value avp_val; + + TRACE_ENTRY("%p %p %p %p", msg, cli, session, diam); + CHECK_PARAMS( msg && cli && session && (*session == NULL) && diam && (*diam == NULL) ); + + pref_len = strlen(prefix); + + /* Is there a State attribute with prefix "Diameter/" in the message? (in that case: Diameter/Destination-Host/Destination-Realm/Session-Id) */ + /* NOTE: RFC4005 says "Origin-Host" here, but it's not coherent with the rules for answers. Destination-Host makes more sense */ + /* Is there a Class attribute with prefix "Diameter/" in the message? (in that case: Diameter/Session-Id) */ + for (idx = 0; idx < msg->radius.attr_used; idx++) { + struct radius_attr_hdr * attr = (struct radius_attr_hdr *)(msg->radius.buf + msg->radius.attr_pos[idx]); + char * attr_val = (char *)(attr + 1); + size_t attr_len = attr->length - sizeof(struct radius_attr_hdr); + + if ((attr->type == RADIUS_ATTR_USER_NAME) + && attr_len) { + TRACE_DEBUG(ANNOYING, "Found a User-Name attribute: '%.*s'", attr_len, attr_val); + un = attr_val; + un_len = attr_len; + continue; + } + + if ((attr->type == RADIUS_ATTR_STATE) + && (attr_len > pref_len + 5 /* for the '/'s and non empty strings */ ) + && ! strncmp(attr_val, prefix, pref_len)) { /* should we make it strncasecmp? */ + int i, start; + + TRACE_DEBUG(ANNOYING, "Found a State attribute with '%s' prefix (attr #%d).", prefix, idx); + + /* Now parse the value and check its content is valid. Unfortunately we cannot use strchr here since strings are not \0-terminated */ + + i = start = pref_len; + dh = attr_val + i; + for (; (i < attr_len - 2) && (attr_val[i] != '/'); i++) /* loop */; + if ( i >= attr_len - 2 ) continue; /* the attribute format is not good */ + dh_len = i - start; + + start = ++i; + dr = attr_val + i; + for (; (i < attr_len - 1) && (attr_val[i] != '/'); i++) /* loop */; + if ( i >= attr_len - 1 ) continue; /* the attribute format is not good */ + dr_len = i - start; + + i++; + si = attr_val + i; + si_len = attr_len - i; + + TRACE_DEBUG(ANNOYING, "Attribute parsed successfully: DH:'%.*s' DR:'%.*s' SI:'%.*s'.", dh_len, dh, dr_len, dr, si_len, si); + /* Remove from the message */ + for (i = idx + 1; i < msg->radius.attr_used; i++) + msg->radius.attr_pos[i - 1] = msg->radius.attr_pos[i]; + msg->radius.attr_used -= 1; + break; + } + + if ((attr->type == RADIUS_ATTR_CLASS) + && (attr_len > pref_len ) + && ! strncmp(attr_val, prefix, pref_len)) { + si = attr_val + pref_len; + si_len = attr_len - pref_len; + TRACE_DEBUG(ANNOYING, "Found Class attribute with '%s' prefix (attr #%d), SI:'%.*s'.", prefix, idx, si_len, si); + /* Remove from the message */ + for (i = idx + 1; i < msg->radius.attr_used; i++) + msg->radius.attr_pos[i - 1] = msg->radius.attr_pos[i]; + msg->radius.attr_used -= 1; + break; + } + + } + + /* Get information on this peer */ + CHECK_FCT( rgw_clients_get_origin(cli, &fqdn, &realm) ); + + /* Create the session object */ + if (si_len) { + CHECK_FCT( fd_sess_fromsid ( si, si_len, session, &idx) ); + } else { + if (un) { + int len; + /* If not found, create a new Session-Id. The format is: {fqdn;hi32;lo32;username;diamid} */ + CHECK_MALLOC( sess_str = malloc(un_len + 1 /* ';' */ + fd_g_config->cnf_diamid_len + 1 /* '\0' */) ); + len = sprintf(sess_str, "%.*s;%s", un_len, un, fd_g_config->cnf_diamid); + CHECK_FCT( fd_sess_new(session, fqdn, sess_str, len) ); + free(sess_str); + idx = 1; + } + } + + /* Create an empty Diameter message so that extensions can store their AVPs */ + CHECK_FCT( fd_msg_new ( NULL, MSGFL_ALLOC_ETEID, diam ) ); + + if (*session) { + CHECK_FCT( fd_sess_getsid(*session, &sess_str) ); + TRACE_DEBUG(FULL, "Session '%s' has been successfully %s.", sess_str, idx ? "created" : "retrieved"); + + /* Add the Session-Id AVP as first AVP */ + CHECK_FCT( fd_msg_avp_new ( cache_sess_id, 0, &avp ) ); + memset(&avp_val, 0, sizeof(avp_val)); + avp_val.os.data = (unsigned char *)sess_str; + avp_val.os.len = strlen(sess_str); + CHECK_FCT( fd_msg_avp_setvalue ( avp, &avp_val ) ); + CHECK_FCT( fd_msg_avp_add ( *diam, MSG_BRW_FIRST_CHILD, avp) ); + + } else { + TRACE_DEBUG(FULL, "No session has been created for this message"); + } + + /* Add the Destination-Realm as next AVP */ + CHECK_FCT( fd_msg_avp_new ( cache_dest_realm, 0, &avp ) ); + memset(&avp_val, 0, sizeof(avp_val)); + if (dr) { + avp_val.os.data = (unsigned char *)dr; + avp_val.os.len = dr_len; + } else { + int i = 0; + if (un) { + /* Is there an '@' in the user name? We don't care for decorated NAI here */ + for (i = un_len - 2; i > 0; i--) { + if (un[i] == '@') { + i++; + break; + } + } + } + if (i == 0) { + /* Not found in the User-Name => we use the local domain of this gateway */ + avp_val.os.data = fd_g_config->cnf_diamrlm; + avp_val.os.len = fd_g_config->cnf_diamrlm_len; + } else { + avp_val.os.data = un + i; + avp_val.os.len = un_len - i; + } + } + CHECK_FCT( fd_msg_avp_setvalue ( avp, &avp_val ) ); + CHECK_FCT( fd_msg_avp_add ( *diam, MSG_BRW_LAST_CHILD, avp) ); + + /* Add the Destination-Host as next AVP */ + if (dh) { + CHECK_FCT( fd_msg_avp_new ( cache_dest_host, 0, &avp ) ); + memset(&avp_val, 0, sizeof(avp_val)); + avp_val.os.data = (unsigned char *)dh; + avp_val.os.len = dh_len; + CHECK_FCT( fd_msg_avp_setvalue ( avp, &avp_val ) ); + CHECK_FCT( fd_msg_avp_add ( *diam, MSG_BRW_LAST_CHILD, avp) ); + } + + /* Add the Origin-Host as next AVP */ + CHECK_FCT( fd_msg_avp_new ( cache_orig_host, 0, &avp ) ); + memset(&avp_val, 0, sizeof(avp_val)); + avp_val.os.data = (unsigned char *)fqdn; + avp_val.os.len = strlen(fqdn); + CHECK_FCT( fd_msg_avp_setvalue ( avp, &avp_val ) ); + CHECK_FCT( fd_msg_avp_add ( *diam, MSG_BRW_LAST_CHILD, avp) ); + + /* Add the Origin-Realm as next AVP */ + CHECK_FCT( fd_msg_avp_new ( cache_orig_realm, 0, &avp ) ); + memset(&avp_val, 0, sizeof(avp_val)); + avp_val.os.data = (unsigned char *)realm; + avp_val.os.len = strlen(realm); + CHECK_FCT( fd_msg_avp_setvalue ( avp, &avp_val ) ); + CHECK_FCT( fd_msg_avp_add ( *diam, MSG_BRW_LAST_CHILD, avp) ); + + /* Done! */ + return 0; +} diff -r ad6c0118fb50 -r a857024cb48b extensions/app_radgw/rgw_msg_attrtype.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/app_radgw/rgw_msg_attrtype.c Wed Apr 14 18:30:22 2010 +0900 @@ -0,0 +1,262 @@ +/* Name of RADIUS attribute from its code */ +const char * rgw_msg_attrtype_str(unsigned char c) { + /* 1 User-Name */ + if ( c == 1) return "User-Name "; + /* 2 User-Password */ + if ( c == 2) return "User-Password "; + /* 3 CHAP-Password */ + if ( c == 3) return "CHAP-Password "; + /* 4 NAS-IP-Address */ + if ( c == 4) return "NAS-IP-Address "; + /* 5 NAS-Port */ + if ( c == 5) return "NAS-Port "; + /* 6 Service-Type */ + if ( c == 6) return "Service-Type "; + /* 7 Framed-Protocol */ + if ( c == 7) return "Framed-Protocol "; + /* 8 Framed-IP-Address */ + if ( c == 8) return "Framed-IP-Address "; + /* 9 Framed-IP-Netmask */ + if ( c == 9) return "Framed-IP-Netmask "; + /* 10 Framed-Routing */ + if ( c == 10) return "Framed-Routing "; + /* 11 Filter-Id */ + if ( c == 11) return "Filter-Id "; + /* 12 Framed-MTU */ + if ( c == 12) return "Framed-MTU "; + /* 13 Framed-Compression */ + if ( c == 13) return "Framed-Compression "; + /* 14 Login-IP-Host */ + if ( c == 14) return "Login-IP-Host "; + /* 15 Login-Service */ + if ( c == 15) return "Login-Service "; + /* 16 Login-TCP-Port */ + if ( c == 16) return "Login-TCP-Port "; + /* 17 Unassigned */ + if ( c == 17) return "Unassigned "; + /* 18 Reply-Message */ + if ( c == 18) return "Reply-Message "; + /* 19 Callback-Number */ + if ( c == 19) return "Callback-Number "; + /* 20 Callback-Id */ + if ( c == 20) return "Callback-Id "; + /* 21 Unassigned */ + if ( c == 21) return "Unassigned "; + /* 22 Framed-Route */ + if ( c == 22) return "Framed-Route "; + /* 23 Framed-IPX-Network */ + if ( c == 23) return "Framed-IPX-Network "; + /* 24 State */ + if ( c == 24) return "State "; + /* 25 Class */ + if ( c == 25) return "Class "; + /* 26 Vendor-Specific */ + if ( c == 26) return "Vendor-Specific "; + /* 27 Session-Timeout */ + if ( c == 27) return "Session-Timeout "; + /* 28 Idle-Timeout */ + if ( c == 28) return "Idle-Timeout "; + /* 29 Termination-Action */ + if ( c == 29) return "Termination-Action "; + /* 30 Called-Station-Id */ + if ( c == 30) return "Called-Station-Id "; + /* 31 Calling-Station-Id */ + if ( c == 31) return "Calling-Station-Id "; + /* 32 NAS-Identifier */ + if ( c == 32) return "NAS-Identifier "; + /* 33 Proxy-State */ + if ( c == 33) return "Proxy-State "; + /* 34 Login-LAT-Service */ + if ( c == 34) return "Login-LAT-Service "; + /* 35 Login-LAT-Node */ + if ( c == 35) return "Login-LAT-Node "; + /* 36 Login-LAT-Group */ + if ( c == 36) return "Login-LAT-Group "; + /* 37 Framed-AppleTalk-Link */ + if ( c == 37) return "Framed-AppleTalk-Link "; + /* 38 Framed-AppleTalk-Network */ + if ( c == 38) return "Framed-AppleTalk-Network "; + /* 39 Framed-AppleTalk-Zone */ + if ( c == 39) return "Framed-AppleTalk-Zone "; + /* 40 Acct-Status-Type [RFC2866] */ + if ( c == 40) return "Acct-Status-Type [RFC2866]"; + /* 41 Acct-Delay-Time [RFC2866] */ + if ( c == 41) return "Acct-Delay-Time [RFC2866]"; + /* 42 Acct-Input-Octets [RFC2866] */ + if ( c == 42) return "Acct-Input-Octets [RFC2866]"; + /* 43 Acct-Output-Octets [RFC2866] */ + if ( c == 43) return "Acct-Output-Octets [RFC2866]"; + /* 44 Acct-Session-Id [RFC2866] */ + if ( c == 44) return "Acct-Session-Id [RFC2866]"; + /* 45 Acct-Authentic [RFC2866] */ + if ( c == 45) return "Acct-Authentic [RFC2866]"; + /* 46 Acct-Session-Time [RFC2866] */ + if ( c == 46) return "Acct-Session-Time [RFC2866]"; + /* 47 Acct-Input-Packets [RFC2866] */ + if ( c == 47) return "Acct-Input-Packets [RFC2866]"; + /* 48 Acct-Output-Packets [RFC2866] */ + if ( c == 48) return "Acct-Output-Packets [RFC2866]"; + /* 49 Acct-Terminate-Cause [RFC2866] */ + if ( c == 49) return "Acct-Terminate-Cause [RFC2866]"; + /* 50 Acct-Multi-Session-Id [RFC2866] */ + if ( c == 50) return "Acct-Multi-Session-Id [RFC2866]"; + /* 51 Acct-Link-Count [RFC2866] */ + if ( c == 51) return "Acct-Link-Count [RFC2866]"; + /* 52 Acct-Input-Gigawords [RFC2869] */ + if ( c == 52) return "Acct-Input-Gigawords [RFC2869]"; + /* 53 Acct-Output-Gigawords [RFC2869] */ + if ( c == 53) return "Acct-Output-Gigawords [RFC2869]"; + /* 54 Unassigned */ + if ( c == 54) return "Unassigned "; + /* 55 Event-Timestamp [RFC2869] */ + if ( c == 55) return "Event-Timestamp [RFC2869]"; + /* 56 Egress-VLANID [RFC4675] */ + if ( c == 56) return "Egress-VLANID [RFC4675]"; + /* 57 Ingress-Filters [RFC4675] */ + if ( c == 57) return "Ingress-Filters [RFC4675]"; + /* 58 Egress-VLAN-Name [RFC4675] */ + if ( c == 58) return "Egress-VLAN-Name [RFC4675]"; + /* 59 User-Priority-Table [RFC4675] */ + if ( c == 59) return "User-Priority-Table [RFC4675]"; + /* 60 CHAP-Challenge */ + if ( c == 60) return "CHAP-Challenge "; + /* 61 NAS-Port-Type */ + if ( c == 61) return "NAS-Port-Type "; + /* 62 Port-Limit */ + if ( c == 62) return "Port-Limit "; + /* 63 Login-LAT-Port */ + if ( c == 63) return "Login-LAT-Port "; + /* 64 Tunnel-Type [RFC2868] */ + if ( c == 64) return "Tunnel-Type [RFC2868]"; + /* 65 Tunnel-Medium-Type [RFC2868] */ + if ( c == 65) return "Tunnel-Medium-Type [RFC2868]"; + /* 66 Tunnel-Client-Endpoint [RFC2868] */ + if ( c == 66) return "Tunnel-Client-Endpoint [RFC2868]"; + /* 67 Tunnel-Server-Endpoint [RFC2868] */ + if ( c == 67) return "Tunnel-Server-Endpoint [RFC2868]"; + /* 68 Acct-Tunnel-Connection [RFC2867] */ + if ( c == 68) return "Acct-Tunnel-Connection [RFC2867]"; + /* 69 Tunnel-Password [RFC2868] */ + if ( c == 69) return "Tunnel-Password [RFC2868]"; + /* 70 ARAP-Password [RFC2869] */ + if ( c == 70) return "ARAP-Password [RFC2869]"; + /* 71 ARAP-Features [RFC2869] */ + if ( c == 71) return "ARAP-Features [RFC2869]"; + /* 72 ARAP-Zone-Access [RFC2869] */ + if ( c == 72) return "ARAP-Zone-Access [RFC2869]"; + /* 73 ARAP-Security [RFC2869] */ + if ( c == 73) return "ARAP-Security [RFC2869]"; + /* 74 ARAP-Security-Data [RFC2869] */ + if ( c == 74) return "ARAP-Security-Data [RFC2869]"; + /* 75 Password-Retry [RFC2869] */ + if ( c == 75) return "Password-Retry [RFC2869]"; + /* 76 Prompt [RFC2869] */ + if ( c == 76) return "Prompt [RFC2869]"; + /* 77 Connect-Info [RFC2869] */ + if ( c == 77) return "Connect-Info [RFC2869]"; + /* 78 Configuration-Token [RFC2869] */ + if ( c == 78) return "Configuration-Token [RFC2869]"; + /* 79 EAP-Message [RFC2869] */ + if ( c == 79) return "EAP-Message [RFC2869]"; + /* 80 Message-Authenticator [RFC2869] */ + if ( c == 80) return "Message-Authenticator [RFC2869]"; + /* 81 Tunnel-Private-Group-ID [RFC2868] */ + if ( c == 81) return "Tunnel-Private-Group-ID [RFC2868]"; + /* 82 Tunnel-Assignment-ID [RFC2868] */ + if ( c == 82) return "Tunnel-Assignment-ID [RFC2868]"; + /* 83 Tunnel-Preference [RFC2868] */ + if ( c == 83) return "Tunnel-Preference [RFC2868]"; + /* 84 ARAP-Challenge-Response [RFC2869] */ + if ( c == 84) return "ARAP-Challenge-Response [RFC2869]"; + /* 85 Acct-Interim-Interval [RFC2869] */ + if ( c == 85) return "Acct-Interim-Interval [RFC2869]"; + /* 86 Acct-Tunnel-Packets-Lost [RFC2867] */ + if ( c == 86) return "Acct-Tunnel-Packets-Lost [RFC2867]"; + /* 87 NAS-Port-Id [RFC2869] */ + if ( c == 87) return "NAS-Port-Id [RFC2869]"; + /* 88 Framed-Pool [RFC2869] */ + if ( c == 88) return "Framed-Pool [RFC2869]"; + /* 89 CUI [RFC4372] */ + if ( c == 89) return "CUI [RFC4372]"; + /* 90 Tunnel-Client-Auth-ID [RFC2868] */ + if ( c == 90) return "Tunnel-Client-Auth-ID [RFC2868]"; + /* 91 Tunnel-Server-Auth-ID [RFC2868] */ + if ( c == 91) return "Tunnel-Server-Auth-ID [RFC2868]"; + /* 92 NAS-Filter-Rule [RFC4849] */ + if ( c == 92) return "NAS-Filter-Rule [RFC4849]"; + /* 93 Unassigned */ + if ( c == 93) return "Unassigned "; + /* 94 Originating-Line-Info [RFC4005] */ + if ( c == 94) return "Originating-Line-Info [RFC4005]"; + /* 95 NAS-IPv6-Address [RFC3162] */ + if ( c == 95) return "NAS-IPv6-Address [RFC3162]"; + /* 96 Framed-Interface-Id [RFC3162] */ + if ( c == 96) return "Framed-Interface-Id [RFC3162]"; + /* 97 Framed-IPv6-Prefix [RFC3162] */ + if ( c == 97) return "Framed-IPv6-Prefix [RFC3162]"; + /* 98 Login-IPv6-Host [RFC3162] */ + if ( c == 98) return "Login-IPv6-Host [RFC3162]"; + /* 99 Framed-IPv6-Route [RFC3162] */ + if ( c == 99) return "Framed-IPv6-Route [RFC3162]"; + /* 100 Framed-IPv6-Pool [RFC3162] */ + if ( c == 100) return "Framed-IPv6-Pool [RFC3162]"; + /* 101 Error-Cause Attribute [RFC3576] */ + if ( c == 101) return "Error-Cause Attribute[RFC3576]"; + /* 102 EAP-Key-Name [RFC4072] */ + if ( c == 102) return "EAP-Key-Name [RFC4072]"; + /* 103 Digest-Response [RFC5090] */ + if ( c == 103) return "Digest-Response [RFC5090]"; + /* 104 Digest-Realm [RFC5090] */ + if ( c == 104) return "Digest-Realm [RFC5090]"; + /* 105 Digest-Nonce [RFC5090] */ + if ( c == 105) return "Digest-Nonce [RFC5090]"; + /* 106 Digest-Response-Auth [RFC5090] */ + if ( c == 106) return "Digest-Response-Auth [RFC5090]"; + /* 107 Digest-Nextnonce [RFC5090] */ + if ( c == 107) return "Digest-Nextnonce [RFC5090]"; + /* 108 Digest-Method [RFC5090] */ + if ( c == 108) return "Digest-Method [RFC5090]"; + /* 109 Digest-URI [RFC5090] */ + if ( c == 109) return "Digest-URI [RFC5090]"; + /* 110 Digest-Qop [RFC5090] */ + if ( c == 110) return "Digest-Qop [RFC5090]"; + /* 111 Digest-Algorithm [RFC5090] */ + if ( c == 111) return "Digest-Algorithm [RFC5090]"; + /* 112 Digest-Entity-Body-Hash [RFC5090] */ + if ( c == 112) return "Digest-Entity-Body-Hash [RFC5090]"; + /* 113 Digest-CNonce [RFC5090] */ + if ( c == 113) return "Digest-CNonce [RFC5090]"; + /* 114 Digest-Nonce-Count [RFC5090] */ + if ( c == 114) return "Digest-Nonce-Count [RFC5090]"; + /* 115 Digest-Username [RFC5090] */ + if ( c == 115) return "Digest-Username [RFC5090]"; + /* 116 Digest-Opaque [RFC5090] */ + if ( c == 116) return "Digest-Opaque [RFC5090]"; + /* 117 Digest-Auth-Param [RFC5090] */ + if ( c == 117) return "Digest-Auth-Param [RFC5090]"; + /* 118 Digest-AKA-Auts [RFC5090] */ + if ( c == 118) return "Digest-AKA-Auts [RFC5090]"; + /* 119 Digest-Domain [RFC5090] */ + if ( c == 119) return "Digest-Domain [RFC5090]"; + /* 120 Digest-Stale [RFC5090] */ + if ( c == 120) return "Digest-Stale [RFC5090]"; + /* 121 Digest-HA1 [RFC5090] */ + if ( c == 121) return "Digest-HA1 [RFC5090]"; + /* 122 SIP-AOR [RFC5090] */ + if ( c == 122) return "SIP-AOR [RFC5090]"; + /* 123 Delegated-IPv6-Prefix [RFC4818] */ + if ( c == 123) return "Delegated-IPv6-Prefix [RFC4818]"; + /* 124 MIP6-Feature-Vector [RFC5447] */ + if ( c == 124) return "MIP6-Feature-Vector [RFC5447]"; + /* 125 MIP6-Home-Link-Prefix [RFC5447] */ + if ( c == 125) return "MIP6-Home-Link-Prefix [RFC5447]"; + /* 126-191 Unassigned */ + if ((c >= 126) && (c <= 191)) return "Unassigned "; + /* 192-223 Experimental Use [RFC3575] */ + if ((c >= 192) && (c <= 223)) return "Experimental Use[RFC3575]"; + /* 224-240 Implementation Specific [RFC3575] */ + if ((c >= 224) && (c <= 240)) return "Implementation Specific[RFC3575]"; + /* 241-255 Reserved [RFC3575] */ + if ((c >= 241) && (c <= 255)) return "Reserved [RFC3575]"; + /* fallback */ return "[Unknown]"; +} diff -r ad6c0118fb50 -r a857024cb48b extensions/app_radgw/rgw_msg_codes.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/app_radgw/rgw_msg_codes.c Wed Apr 14 18:30:22 2010 +0900 @@ -0,0 +1,82 @@ +/* Name of RADIUS command from its command code */ +const char * rgw_msg_code_str(unsigned char c) { + /* 1 Access-Request [RFC2865] */ + if ( c == 1) return "Access-Request [RFC2865]"; + /* 2 Access-Accept [RFC2865] */ + if ( c == 2) return "Access-Accept [RFC2865]"; + /* 3 Access-Reject [RFC2865] */ + if ( c == 3) return "Access-Reject [RFC2865]"; + /* 4 Accounting-Request [RFC2865] */ + if ( c == 4) return "Accounting-Request [RFC2865]"; + /* 5 Accounting-Response [RFC2865] */ + if ( c == 5) return "Accounting-Response [RFC2865]"; + /* 6 Accounting-Status [RFC3575] */ + if ( c == 6) return "Accounting-Status [RFC3575]"; + /* 7 Password-Request [RFC3575] */ + if ( c == 7) return "Password-Request [RFC3575]"; + /* 8 Password-Ack [RFC3575] */ + if ( c == 8) return "Password-Ack [RFC3575]"; + /* 9 Password-Reject [RFC3575] */ + if ( c == 9) return "Password-Reject [RFC3575]"; + /* 10 Accounting-Message [RFC3575] */ + if ( c == 10) return "Accounting-Message [RFC3575]"; + /* 11 Access-Challenge [RFC2865] */ + if ( c == 11) return "Access-Challenge [RFC2865]"; + /* 12 Status-Server (experimental) [RFC2865] */ + if ( c == 12) return "Status-Server (experimental)[RFC2865]"; + /* 13 Status-Client (experimental) [RFC2865] */ + if ( c == 13) return "Status-Client (experimental)[RFC2865]"; + /* 21 Resource-Free-Request [RFC3575] */ + if ( c == 21) return "Resource-Free-Request [RFC3575]"; + /* 22 Resource-Free-Response [RFC3575] */ + if ( c == 22) return "Resource-Free-Response [RFC3575]"; + /* 23 Resource-Query-Request [RFC3575] */ + if ( c == 23) return "Resource-Query-Request [RFC3575]"; + /* 24 Resource-Query-Response [RFC3575] */ + if ( c == 24) return "Resource-Query-Response [RFC3575]"; + /* 25 Alternate-Resource-Reclaim-Request [RFC3575] */ + if ( c == 25) return "Alternate-Resource-Reclaim-Request [RFC3575]"; + /* 26 NAS-Reboot-Request [RFC3575] */ + if ( c == 26) return "NAS-Reboot-Request [RFC3575]"; + /* 27 NAS-Reboot-Response [RFC3575] */ + if ( c == 27) return "NAS-Reboot-Response [RFC3575]"; + /* 28 Reserved */ + if ( c == 28) return "Reserved "; + /* 29 Next-Passcode [RFC3575] */ + if ( c == 29) return "Next-Passcode [RFC3575]"; + /* 30 New-Pin [RFC3575] */ + if ( c == 30) return "New-Pin [RFC3575]"; + /* 31 Terminate-Session [RFC3575] */ + if ( c == 31) return "Terminate-Session [RFC3575]"; + /* 32 Password-Expired [RFC3575] */ + if ( c == 32) return "Password-Expired [RFC3575]"; + /* 33 Event-Request [RFC3575] */ + if ( c == 33) return "Event-Request [RFC3575]"; + /* 34 Event-Response [RFC3575] */ + if ( c == 34) return "Event-Response [RFC3575]"; + /* 40 Disconnect-Request [RFC3575][RFC5176] */ + if ( c == 40) return "Disconnect-Request [RFC3575][RFC5176]"; + /* 41 Disconnect-ACK [RFC3575][RFC5176] */ + if ( c == 41) return "Disconnect-ACK [RFC3575][RFC5176]"; + /* 42 Disconnect-NAK [RFC3575][RFC5176] */ + if ( c == 42) return "Disconnect-NAK [RFC3575][RFC5176]"; + /* 43 CoA-Request [RFC3575][RFC5176] */ + if ( c == 43) return "CoA-Request [RFC3575][RFC5176]"; + /* 44 CoA-ACK [RFC3575][RFC5176] */ + if ( c == 44) return "CoA-ACK [RFC3575][RFC5176]"; + /* 45 CoA-NAK [RFC3575][RFC5176] */ + if ( c == 45) return "CoA-NAK [RFC3575][RFC5176]"; + /* 50 IP-Address-Allocate [RFC3575] */ + if ( c == 50) return "IP-Address-Allocate [RFC3575]"; + /* 51 IP-Address-Release [RFC3575] */ + if ( c == 51) return "IP-Address-Release [RFC3575]"; + /* 52-249 Unassigned */ + if ((c >= 52) && (c <= 249)) return "Unassigned "; + /* 250-253 Experimental Use [RFC3575] */ + if ((c >= 250) && (c <= 253)) return "Experimental Use[RFC3575]"; + /* 254 Reserved [RFC3575] */ + if ( c == 254) return "Reserved [RFC3575]"; + /* 255 Reserved [RFC3575] */ + if ( c == 255) return "Reserved [RFC3575]"; + /* fallback */ return "[Unknown]"; +} diff -r ad6c0118fb50 -r a857024cb48b extensions/app_radgw/rgw_plugins.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/app_radgw/rgw_plugins.c Wed Apr 14 18:30:22 2010 +0900 @@ -0,0 +1,504 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis * +* * +* Copyright (c) 2009, WIDE Project and NICT * +* All rights reserved. * +* * +* Redistribution and use of this software in source and binary forms, with or without modification, are * +* permitted provided that the following conditions are met: * +* * +* * Redistributions of source code must retain the above * +* copyright notice, this list of conditions and the * +* following disclaimer. * +* * +* * Redistributions in binary form must reproduce the above * +* copyright notice, this list of conditions and the * +* following disclaimer in the documentation and/or other * +* materials provided with the distribution. * +* * +* * Neither the name of the WIDE Project or NICT nor the * +* names of its contributors may be used to endorse or * +* promote products derived from this software without * +* specific prior written permission of WIDE Project and * +* NICT. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED * +* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * +* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * +* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * +* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +*********************************************************************************************************/ + +/* Manage the list of plugins that provide handlers for RADIUS messages and attributes */ + +#include "rgw.h" +#include +#include + +/* List of plugins, in the order they appear in the configuration file. */ +static struct fd_list plg_list = FD_LIST_INITIALIZER(plg_list); + +/* A plugin entry */ +struct plg_descr { + struct fd_list chain; /* chaining in plg_list */ + + void *dlo; /* pointer returned by dlopen for the extension, to use with dlclose later */ + struct rgw_api *descriptor; /* Points to the resolved plugin's rgwp_descriptor */ + struct rgwp_config *cs; /* the (private) state returned by rgwp_conf_parse */ + + int type; /* this extension is called for messages received on this(these) server port(s) only */ + unsigned char *cc; /* array of command codes, or NULL for all cc */ + size_t cc_len; /* size of the previous array */ + + char *plgname; /* basename of the plugin, for debug messages. To be freed when object is detroyed */ + char *conffile; /* configuration file passed to the extension, or "(null)". To be freed when object is destroyed */ +}; + +/* Accelerators for each command code (one for each port). These accelerators are built on-demand, as a cache, after start_cache function has been called. */ +static struct fd_list plg_accel_auth = FD_LIST_INITIALIZER(plg_accel_auth); +static struct fd_list plg_accel_acct = FD_LIST_INITIALIZER(plg_accel_acct); + +/* Accelerator list item, one per command code value (only the ones actually used) */ +struct plg_accel { + struct fd_list chain; /* link in the plg_accel_* list. List ordered by ccode. */ + unsigned char ccode; /* the command code for this accelerator. We don't handle extended CC yet */ + struct fd_list plugins;/* head for the list of plg_accel_item, corresponding to the extensions to be called for this command code. */ +}; + +/* Accelerator item */ +struct plg_accel_item { + struct fd_list chain; /* link in the plg_accel "plugins" list */ + struct plg_descr * plg; /* pointer to the plugin */ + /* Note: we can further optimize by caching the location of plg->descriptor->rgwp_rad_req etc... at this level. */ +}; + +/* RWlock to protect all the previous lists */ +static pthread_rwlock_t plg_lock = PTHREAD_RWLOCK_INITIALIZER; + +/* Has start_cache been called? */ +static int cache_started = 0; + + +/* The read lock must be held before calling this function */ +static int get_accelerator(struct fd_list ** list, unsigned char ccode, int type) +{ + struct fd_list *refer, *search; + struct plg_accel * accel = NULL; + struct plg_accel_item * item = NULL; + int upgraded = 0; + + TRACE_ENTRY("%p %hhu %i", list, ccode, type); + + CHECK_PARAMS( cache_started && list && ((type == RGW_PLG_TYPE_AUTH) || (type == RGW_PLG_TYPE_ACCT)) ); + + if (type == RGW_PLG_TYPE_AUTH) + refer = &plg_accel_auth; + else + refer = &plg_accel_acct; +restart: + /* Check if we have already an accelerator for this ccode */ + for (search = refer->next; search != refer; search = search->next) { + struct plg_accel * loc = (struct plg_accel *)search; + + if (loc->ccode < ccode) + continue; + + if (loc->ccode > ccode) + break; /* we don't have an accelerator for this value yet */ + + /* We found the matching accelerator, just return this list */ + *list = &loc->plugins; + return 0; + } + + /* We must create the accelerator list, then save it just before "search" */ + + /* First , upgrade the lock to write lock, and restart the search. This is the only robust solution to avoid deadlocks */ + if (! upgraded) { + CHECK_POSIX( pthread_rwlock_unlock(&plg_lock) ); + CHECK_POSIX( pthread_rwlock_wrlock(&plg_lock) ); + upgraded = 1; + goto restart; + } + + /* Now create the new element */ + CHECK_MALLOC( accel = malloc(sizeof(struct plg_accel)) ); + memset(accel, 0, sizeof(struct plg_accel) ); + fd_list_init(&accel->chain, NULL); + fd_list_init(&accel->plugins, accel); + accel->ccode = ccode; + + /* Check each extension from the global list for this port and ccode */ + for (refer = plg_list.next; refer != &plg_list; refer = refer->next) { + struct plg_descr * loc = (struct plg_descr *)refer; + + /* Skip if this extension is not registered for this port */ + if (! (loc->type & type) ) + continue; + + /* Check if the ccode is there */ + if (loc->cc) { + int i; + int match = 0; + for (i=0; i< loc->cc_len; i++) { + if (loc->cc[i] < ccode) + continue; + if (loc->cc[i] == ccode) + match = 1; + break; + } + if (!match) + continue; + } + + /* Ok, this extension must be called for this port / ccode, add to the accelerator */ + CHECK_MALLOC( item = malloc(sizeof(struct plg_accel_item)) ); + memset(item, 0, sizeof(struct plg_accel_item)); + fd_list_init(&item->chain, NULL); + item->plg = loc; + /* Add as last element of the accelerator */ + fd_list_insert_before(&accel->plugins, &item->chain); + } + + /* Now, save this accelerator entry in the global list */ + fd_list_insert_before(search, &accel->chain); + *list = &accel->plugins; + + return 0; +} + + +int rgw_plg_add( char * plgfile, char * conffile, int type, unsigned char ** codes_array, size_t codes_sz ) +{ + struct plg_descr * new; + int ret = 0; + char * tmp; + + TRACE_ENTRY("%p %p %d %p %zi", plgfile, conffile, type, codes_array, codes_sz); + + CHECK_PARAMS( plgfile && type && codes_array && (cache_started == 0) ); + + CHECK_MALLOC( tmp = strdup(plgfile) ); + + CHECK_MALLOC( new = malloc(sizeof(struct plg_descr)) ); + memset(new, 0, sizeof(struct plg_descr)); + + fd_list_init(&new->chain, new); + + /* Copy names, for debug */ + CHECK_MALLOC( new->plgname = strdup(basename(tmp)) ); /* basename is a stupid function :( */ + free(tmp); + CHECK_MALLOC( new->conffile = conffile ?: strdup("(null)") ); + + /* Try and load the plugin */ + TRACE_DEBUG(FULL, "Loading plugin: %s", plgfile); + new->dlo = dlopen(plgfile, RTLD_NOW | RTLD_GLOBAL); + if (new->dlo == NULL) { + /* An error occured */ + fd_log_debug("Loading of plugin '%s' failed:\n %s\n", plgfile, dlerror()); + goto error; + } + + /* Resolve the descriptor */ + new->descriptor = dlsym( new->dlo, "rgwp_descriptor" ); + if (new->descriptor == NULL) { + /* An error occured */ + fd_log_debug("Unable to resolve 'rgwp_descriptor' in plugin '%s':\n %s\n", plgfile, dlerror()); + goto error; + } + + /* Now parse the configuration file, this will initialize all extension states and store it in the returned pointer (the subextensions must be re-entrant) */ + if (new->descriptor->rgwp_conf_parse) { + TRACE_DEBUG(FULL, "Parsing plugin conf file: %s", new->conffile ); + new->cs = (*(new->descriptor->rgwp_conf_parse))(conffile); + if (new->cs == NULL) { + fd_log_debug("An error occurred while parsing configuration file '%s' in plugin '%s', aborting...\n", new->conffile, new->plgname); + goto error; + } + } + + /* Now sort the array (very simple algorithm, but this list is usually small) of command codes and save */ + if (*codes_array && codes_sz) { + int i; + + new->cc = *codes_array; + *codes_array = NULL; + + for (i = 0; i < codes_sz - 1; i++) { + int j, idx = i, min = new->cc[i]; + + /* find the smallest remaining element */ + for (j = i + 1; j < codes_sz; j++) { + if (min > new->cc[j]) { + min = new->cc[j]; + idx = j; + } + } + + /* swap if needed */ + if (idx != i) { + int tmp = new->cc[i]; + new->cc[i] = new->cc[idx]; + new->cc[idx] = tmp; + } + } + new->cc_len = codes_sz; + } + + new->type = type; + + /* And save this new extension in the list. We don't need to lock at this point because we are single threaded. */ + fd_list_insert_before(&plg_list, &new->chain); + + return 0; + + +error: + if (new && new->dlo) + dlclose(new->dlo); + if (new) + free(new); + return EINVAL; +} + +void rgw_plg_dump(void) +{ + struct plg_descr * plg; + struct fd_list * ptr, *ptraccel; + + if ( ! TRACE_BOOL(FULL) ) + return; + + CHECK_POSIX_DO( pthread_rwlock_rdlock(&plg_lock), ); + + if ( ! FD_IS_LIST_EMPTY( &plg_list ) ) + fd_log_debug("[app_radgw] --- List of registered plugins:\n"); + for (ptr = plg_list.next; ptr != &plg_list; ptr = ptr->next) { + + plg = (struct plg_descr *)ptr; + + fd_log_debug(" %-25s ( %-25s ) - types: %s%s, codes: ", + plg->plgname, + basename(plg->conffile), + plg->type & RGW_PLG_TYPE_AUTH ? "Au" : " ", + plg->type & RGW_PLG_TYPE_ACCT ? "Ac" : " "); + + if (plg->cc) { + int i; + + for (i = 0; i < plg->cc_len; i++) { + fd_log_debug("%02hhx ", plg->cc[i]); + } + fd_log_debug("\n"); + } else { + fd_log_debug("*\n"); + } + } + + CHECK_POSIX_DO( pthread_rwlock_unlock(&plg_lock), ); + + /* Dump the list of accelerators */ + if ( ! TRACE_BOOL(FULL + 1) ) + return; + + CHECK_POSIX_DO( pthread_rwlock_rdlock(&plg_lock), ); + if ( !FD_IS_LIST_EMPTY( &plg_accel_auth ) || !FD_IS_LIST_EMPTY( &plg_accel_acct )) + fd_log_debug(" --- Accelerators:\n"); + + for (ptraccel = plg_accel_auth.next; ptraccel != &plg_accel_auth; ptraccel = ptraccel->next) { + struct plg_accel * accel = (struct plg_accel *)ptraccel; + fd_log_debug(" auth, code %02hhu:\n", accel->ccode); + + for (ptr = accel->plugins.next; ptr != &accel->plugins; ptr = ptr->next) { + struct plg_accel_item * item = (struct plg_accel_item *)ptr; + fd_log_debug(" %-15s (%s)\n", item->plg->plgname, basename(item->plg->conffile)); + } + } + for (ptraccel = plg_accel_acct.next; ptraccel != &plg_accel_acct; ptraccel = ptraccel->next) { + struct plg_accel * accel = (struct plg_accel *)ptraccel; + fd_log_debug(" acct, code %02hhu:\n", accel->ccode); + + for (ptr = accel->plugins.next; ptr != &accel->plugins; ptr = ptr->next) { + struct plg_accel_item * item = (struct plg_accel_item *)ptr; + fd_log_debug(" %-15s (%s)\n", item->plg->plgname, basename(item->plg->conffile)); + } + } + + + CHECK_POSIX_DO( pthread_rwlock_unlock(&plg_lock), ); + +} + +void rgw_plg_start_cache(void) +{ + cache_started++; +} + +int rgw_plg_loop_req(struct rgw_radius_msg_meta **rad, struct session **session, struct msg **diam_msg, struct rgw_client * cli) +{ + int ret = 0; + struct fd_list * head = NULL, *li; + struct radius_msg * rad_ans = NULL; + + TRACE_ENTRY("%p %p %p %p", rad, session, diam_msg, cli); + CHECK_PARAMS( rad && *rad && session && diam_msg && *diam_msg && cli); + + /* First, get the list of extensions for this message */ + CHECK_POSIX( pthread_rwlock_rdlock( &plg_lock) ); + CHECK_FCT_DO( ret = get_accelerator(&head, (*rad)->radius.hdr->code, (*rad)->serv_type), + { CHECK_POSIX( pthread_rwlock_unlock( &plg_lock) ); return ret; } ); + + /* Loop in the list of extensions */ + for (li = head->next; li != head; li = li->next) { + struct plg_descr * plg = ((struct plg_accel_item *) li)->plg; + + if (plg->descriptor->rgwp_rad_req) { + TRACE_DEBUG(ANNOYING, "Calling next plugin: %s", plg->plgname); + ret = (*plg->descriptor->rgwp_rad_req)(plg->cs, *session, &(*rad)->radius, &rad_ans, diam_msg, cli); + if (ret) + break; + } else { + TRACE_DEBUG(ANNOYING, "Skipping extension '%s' (NULL callback)", plg->plgname); + } + } + + CHECK_POSIX( pthread_rwlock_unlock( &plg_lock) ); + + /* If no error encountered, we're done here */ + if (ret == 0) + return 0; + + /* Destroy the Diameter temp message, if any */ + if (*diam_msg) { + CHECK_FCT_DO( fd_msg_free(*diam_msg), ); + *diam_msg = NULL; + } + + /* Destroy the session, there won't be a reply message to retrieve the data */ + if (*session) { + CHECK_FCT_DO( fd_sess_destroy(session), ); + } + + /* Send the radius message back if required */ + if ((ret == -2) && rad_ans && rad) { + CHECK_FCT_DO( rgw_client_finish_send(&rad_ans, *rad, cli), /* It failed, it can't be helped... */); + } + + if (ret > 0) { + /* Critical error, log and exit */ + fd_log_debug("An error occurred while handling a RADIUS message, turn on DEBUG for details: %s\n", strerror(ret)); + return ret; + } + + /* Now, discard the message and return */ + rgw_msg_free(rad); + return 0; +} + +/* Loop in the extension list (same as req) to convert data from diam_ans to rad_ans */ +int rgw_plg_loop_ans(struct rgw_radius_msg_meta *req, struct session *session, struct msg **diam_ans, struct radius_msg ** rad_ans, struct rgw_client * cli) +{ + int ret = 0; + struct fd_list * head = NULL, *li; + + TRACE_ENTRY("%p %p %p %p %p", req, session, diam_ans, rad_ans, cli); + CHECK_PARAMS( req && session && diam_ans && *diam_ans && rad_ans && *rad_ans && cli); + + /* Get the list of extensions of the RADIUS request */ + CHECK_POSIX( pthread_rwlock_rdlock( &plg_lock) ); + CHECK_FCT_DO( ret = get_accelerator(&head, req->radius.hdr->code, req->serv_type), + { CHECK_POSIX( pthread_rwlock_unlock( &plg_lock) ); return ret; } ); + + /* Loop in the list of extensions */ + for (li = head->next; li != head; li = li->next) { + struct plg_descr * plg = ((struct plg_accel_item *) li)->plg; + + if (plg->descriptor->rgwp_diam_ans) { + TRACE_DEBUG(ANNOYING, "Calling next plugin: %s", plg->plgname); + ret = (*plg->descriptor->rgwp_diam_ans)(plg->cs, session, diam_ans, rad_ans, (void *)cli); + if (ret) + break; + } else { + TRACE_DEBUG(ANNOYING, "Skipping extension '%s' (NULL callback)", plg->plgname); + } + } + + CHECK_POSIX( pthread_rwlock_unlock( &plg_lock) ); + + /* If no error encountered, we're done here */ + if (ret == 0) + return 0; + + /* Destroy the temporary RADIUS answer */ + if (*rad_ans) { + radius_msg_free(*rad_ans); + free(*rad_ans); + *rad_ans = NULL; + } + + if (ret > 0) { + /* Critical error, log and exit */ + fd_log_debug("[app_radgw] An error occurred while handling a DIAMETER answer to a converted RADIUS request, turn on DEBUG for details: %s\n", strerror(ret)); + return ret; + } + + /* We might define other return values with special meaning here (ret == -1, ...) for example create a new Diameter request */ + + return 0; +} + +void rgw_plg_fini(void) +{ + struct fd_list * item, *subitem; + + TRACE_ENTRY(); + + CHECK_POSIX_DO( pthread_rwlock_rdlock( &plg_lock), /* continue anyway */ ); + + /* Remove all elements from all accelerators */ + while ( ! FD_IS_LIST_EMPTY(&plg_accel_auth) ) { + item = plg_accel_auth.next; + fd_list_unlink(item); + { + struct plg_accel * accel = (struct plg_accel *)item; + while ( ! FD_IS_LIST_EMPTY(&accel->plugins) ) { + subitem = accel->plugins.next; + fd_list_unlink(subitem); + free(subitem); + } + } + free(item); + } + while ( ! FD_IS_LIST_EMPTY(&plg_accel_acct) ) { + item = plg_accel_acct.next; + fd_list_unlink(item); + { + struct plg_accel * accel = (struct plg_accel *)item; + while ( ! FD_IS_LIST_EMPTY(&accel->plugins) ) { + subitem = accel->plugins.next; + fd_list_unlink(subitem); + free(subitem); + } + } + free(item); + } + + /* Now destroy all plugins information */ + while ( ! FD_IS_LIST_EMPTY(&plg_list) ) { + struct plg_descr * plg = (struct plg_descr *) plg_list.next; + fd_list_unlink(&plg->chain); + free(plg->conffile); + free(plg->plgname); + free(plg->cc); + if (plg->cs && plg->descriptor && plg->descriptor->rgwp_conf_free ) + (*plg->descriptor->rgwp_conf_free)(plg->cs); + dlclose(plg->dlo); + free(plg); + } + + CHECK_POSIX_DO( pthread_rwlock_unlock( &plg_lock), ); +} diff -r ad6c0118fb50 -r a857024cb48b extensions/app_radgw/rgw_servers.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/app_radgw/rgw_servers.c Wed Apr 14 18:30:22 2010 +0900 @@ -0,0 +1,308 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis * +* * +* Copyright (c) 2009, WIDE Project and NICT * +* All rights reserved. * +* * +* Redistribution and use of this software in source and binary forms, with or without modification, are * +* permitted provided that the following conditions are met: * +* * +* * Redistributions of source code must retain the above * +* copyright notice, this list of conditions and the * +* following disclaimer. * +* * +* * Redistributions in binary form must reproduce the above * +* copyright notice, this list of conditions and the * +* following disclaimer in the documentation and/or other * +* materials provided with the distribution. * +* * +* * Neither the name of the WIDE Project or NICT nor the * +* names of its contributors may be used to endorse or * +* promote products derived from this software without * +* specific prior written permission of WIDE Project and * +* NICT. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED * +* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * +* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * +* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * +* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +*********************************************************************************************************/ + +/* Manage the RADIUS server(s): opening sockets, receiving messages, ... */ + +#include "rgw.h" + +#define RADIUS_MAX_MSG_LEN 3000 +#define RADIUS_AUTH_PORT 1812 +#define RADIUS_ACCT_PORT 1813 + +/* Declare the rgw_servers */ +struct rgw_servs rgw_servers; + +void rgw_servers_dump(void) +{ + char ipstr[INET6_ADDRSTRLEN]; + + if ( ! TRACE_BOOL(FULL) ) + return; + + fd_log_debug(" auth server:\n"); + fd_log_debug(" disabled..... : %s\n", rgw_servers.auth_serv.disabled ? "TRUE":"false"); + fd_log_debug(" IP disabled.. : %s\n", rgw_servers.auth_serv.ip_disabled ? "TRUE":"false"); + fd_log_debug(" IPv6 disabled : %s\n", rgw_servers.auth_serv.ip6_disabled ? "TRUE":"false"); + fd_log_debug(" port......... : %hu\n", ntohs(rgw_servers.auth_serv.port)); + inet_ntop(AF_INET, &rgw_servers.auth_serv.ip_endpoint,ipstr,sizeof(ipstr)); + fd_log_debug(" IP bind...... : %s\n", ipstr); + inet_ntop(AF_INET6, &rgw_servers.auth_serv.ip6_endpoint,ipstr,sizeof(ipstr)); + fd_log_debug(" IPv6 bind.... : %s\n", ipstr); + + fd_log_debug(" acct server:\n"); + fd_log_debug(" disabled..... : %s\n", rgw_servers.acct_serv.disabled ? "TRUE":"false"); + fd_log_debug(" IP disabled.. : %s\n", rgw_servers.acct_serv.ip_disabled ? "TRUE":"false"); + fd_log_debug(" IPv6 disabled : %s\n", rgw_servers.acct_serv.ip6_disabled ? "TRUE":"false"); + fd_log_debug(" port......... : %hu\n", ntohs(rgw_servers.acct_serv.port)); + inet_ntop(AF_INET, &rgw_servers.acct_serv.ip_endpoint,ipstr,sizeof(ipstr)); + fd_log_debug(" IP bind...... : %s\n", ipstr); + inet_ntop(AF_INET6, &rgw_servers.acct_serv.ip6_endpoint,ipstr,sizeof(ipstr)); + fd_log_debug(" IPv6 bind.... : %s\n", ipstr); + +} + +static struct servers_data { + int type; /* auth or acct */ + int family; /* AF_INET or AF_INET6 */ + int sock; /* the socket number */ + pthread_t th; /* the running server thread, or NULL */ + char name[10]; +} SERVERS[4]; + +int rgw_servers_init(void) +{ + memset(&rgw_servers, 0, sizeof(rgw_servers)); + memset(&SERVERS[0], 0, sizeof(SERVERS)); + + rgw_servers.auth_serv.port = htons(RADIUS_AUTH_PORT); + rgw_servers.acct_serv.port = htons(RADIUS_ACCT_PORT); + + return 0; +} + +static void * server_thread(void * param) +{ + struct servers_data * me = (struct servers_data *)param; + + TRACE_ENTRY("%p", param); + + CHECK_PARAMS_DO(param, return NULL); + + /* Set the thread name */ + { + char buf[48]; + snprintf(buf, sizeof(buf), "radgw/%s serv", me->name); + fd_log_threadname ( buf ); + } + + /* Now loop on this socket, parse and queue each message received, until thread is cancelled. */ + while (1) { + struct sockaddr_storage from; + socklen_t fromlen = sizeof(from); + int len; + struct rgw_client * nas_info = NULL; + uint16_t port = 0; + unsigned char buf[RADIUS_MAX_MSG_LEN]; + struct rgw_radius_msg_meta *msg = NULL; + + pthread_testcancel(); + + /* receive the next message */ + CHECK_SYS_DO( len = recvfrom( me->sock, &buf[0], sizeof(buf), 0, (struct sockaddr *) &from, &fromlen), break ); + + /* Get the port */ + if (from.ss_family == AF_INET) + port = ((struct sockaddr_in *)&from)->sin_port; + if (from.ss_family == AF_INET6) + port = ((struct sockaddr_in6 *)&from)->sin6_port; + if (!port) { + TRACE_DEBUG(INFO, "Invalid port (family: %d), discarding received %d bytes...", from.ss_family, len); + continue; + } + + TRACE_DEBUG(FULL, "Received %d bytes", len); + TRACE_DEBUG_sSA(FULL, " from ", &from, NI_NUMERICHOST | NI_NUMERICSERV, "\n" ); + + /* Search the associated client definition, if any */ + CHECK_FCT_DO( rgw_clients_search((struct sockaddr *) &from, &nas_info), + { + TRACE_DEBUG(INFO, "Discarding %d bytes received from unknown IP:", len); + TRACE_DEBUG_sSA(INFO, " ", &from, NI_NUMERICHOST | NI_NUMERICSERV, "\n" ); + continue; + } ); + + + /* parse the message, loop if message is invalid */ + CHECK_FCT_DO( rgw_msg_parse(&buf[0], len, &msg), + { + char * cliname = NULL; + CHECK_FCT_DO( rgw_clients_get_origin(nas_info, &cliname, NULL), ); + TRACE_DEBUG(INFO, "Discarding invalid RADIUS message from '%s'", cliname); + rgw_clients_dispose(&nas_info); + continue; + } ); + + msg->serv_type = me->type; + msg->port = port; + + rgw_msg_dump(msg); + + /* queue the message for a worker thread */ + CHECK_FCT_DO( rgw_work_add(msg, nas_info), break ); + + /* Then wait for next incoming message */ + } + + TRACE_DEBUG(INFO, "Server thread terminated."); + return NULL; +} + +/* Set the socket options for UDP sockets, before bind is called */ +static int _udp_setsockopt(int family, int sk) +{ + int ret = 0; + int opt; + + /* In case of v6 address, force the v6only option, we use a different socket for v4 */ + #ifdef IPV6_V6ONLY + if (family == AF_INET6) { + opt = 1; + ret = setsockopt(sk, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)); + if (ret != 0) { + ret = errno; + TRACE_DEBUG(INFO, "Unable to set the socket IPV6_V6ONLY option: %s", strerror(ret)); + return ret; + } + } + #endif /* IPV6_V6ONLY */ + + return 0; +} + +/* We reuse the same logic for all 4 possible servers (IP / IPv6, Auth / Acct ports) */ +#define UDPSERV( _type_, _portval_, _family_ ) { \ + /* Check that this type / family are not disabled by configuration */ \ + if ( (! rgw_servers. _type_ ## _serv.disabled) \ + && ( ! rgw_servers. _type_ ## _serv.ip ## _family_ ## _disabled ) ) { \ + struct sockaddr_in ## _family_ sin ## _family_ ; \ + /* Create the socket */ \ + CHECK_SYS( SERVERS[idx].sock = socket(AF_INET ## _family_, SOCK_DGRAM, 0) ); \ + /* Set the parameters for bind into "sin" or "sin6" */ \ + memset(& sin ## _family_, 0, sizeof(struct sockaddr_in ## _family_)); \ + sin ## _family_ . sin ## _family_ ## _family = AF_INET ## _family_; \ + sin ## _family_ . sin ## _family_ ## _port = rgw_servers. _type_ ## _serv . port; \ + memcpy( &sin ## _family_ .sin ## _family_ ## _addr, \ + &rgw_servers. _type_ ## _serv . ip ## _family_ ## _endpoint, \ + sizeof(struct in ## _family_ ## _addr) ); \ + /* This sockopt must be set before binding */ \ + TRACE_DEBUG(ANNOYING, "Setting socket options..."); \ + CHECK_FCT( _udp_setsockopt(AF_INET ## _family_, SERVERS[idx].sock) ); \ + /* OK, now, bind */ \ + TRACE_DEBUG(ANNOYING, "Binding " #_type_ " ip" #_family_ " server..."); \ + CHECK_SYS( bind( SERVERS[idx].sock, \ + (struct sockaddr *)&sin ## _family_, \ + sizeof(struct sockaddr_in ## _family_) ) ); \ + /* Save the server information in SERVERS structure */ \ + SERVERS[idx].type = _portval_; \ + SERVERS[idx].family = AF_INET ## _family_; \ + snprintf(&SERVERS[idx].name[0], sizeof(SERVERS[idx].name), # _type_ "/ip" #_family_); \ + /* Create the server thread */ \ + CHECK_POSIX( pthread_create(&SERVERS[idx].th, NULL, server_thread, &SERVERS[idx]) ); \ + idx++; \ + } \ +} + +int rgw_servers_start(void) +{ + int idx = 0; + + TRACE_ENTRY(); + + UDPSERV( auth, RGW_PLG_TYPE_AUTH, ); + UDPSERV( auth, RGW_PLG_TYPE_AUTH, 6 ); + UDPSERV( acct, RGW_PLG_TYPE_ACCT, ); + UDPSERV( acct, RGW_PLG_TYPE_ACCT, 6 ); + + TRACE_DEBUG(FULL, "%d UDP servers started succesfully.", idx); + return 0; +} + +/* Send a RADIUS message */ +int rgw_servers_send(int type, unsigned char *buf, size_t buflen, struct sockaddr *to, uint16_t to_port) +{ + int idx = 0; + int ret = 0; + struct sockaddr_storage sto; + + /* Find the appropriate socket to use (not sure if it is important) */ + for (idx = 0; idx < sizeof(SERVERS) / sizeof(SERVERS[0]); idx++) { + if ( SERVERS[idx].sock && (type == SERVERS[idx].type) && (to->sa_family == SERVERS[idx].family) ) { + ret = 1; + break; + } + } + + if (!ret) { + TRACE_DEBUG(INFO, "Trying to send a message from a disabled server: %s / %s", + (type == RGW_PLG_TYPE_AUTH) ? "Auth" : "Acct", + (to->sa_family == AF_INET) ? "IP (v4)" : "IPv6"); + return EINVAL; + } + + /* Prepare the destination info */ + memset(&sto, 0, sizeof(sto)); + if (to->sa_family == AF_INET) { + memcpy(&sto, to, sizeof(struct sockaddr_in)); + ((struct sockaddr_in *)&sto)->sin_port = to_port; + } else { + memcpy(&sto, to, sizeof(struct sockaddr_in6)); + ((struct sockaddr_in6 *)&sto)->sin6_port = to_port; + } + + TRACE_DEBUG(FULL, "Sending %d bytes", buflen); + TRACE_DEBUG_sSA(FULL, " to ", &sto, NI_NUMERICHOST | NI_NUMERICSERV, "\n" ); + + /* Send */ + ret = sendto(SERVERS[idx].sock, buf, buflen, 0, (struct sockaddr *)&sto, sSAlen(&sto)); + if (ret < 0) { + ret = errno; + TRACE_DEBUG(INFO, "An error prevented sending of a RADIUS message: %s", strerror(ret)); + return ret; + } + if (ret != buflen) { + TRACE_DEBUG(INFO, "Incomplete send: %d bytes / %zd", ret, buflen); + return EAGAIN; + } + + /* Done :) */ + return 0; +} + +void rgw_servers_fini(void) +{ + int idx = 0; + + for (idx = 0; idx < sizeof(SERVERS) / sizeof(SERVERS[0]); idx++) { + if (SERVERS[idx].sock == 0) + break; + + CHECK_FCT_DO( fd_thr_term(&SERVERS[idx].th), /* continue */ ); + close(SERVERS[idx].sock); + SERVERS[idx].sock = 0; + } + +} + + diff -r ad6c0118fb50 -r a857024cb48b extensions/app_radgw/rgw_worker.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/app_radgw/rgw_worker.c Wed Apr 14 18:30:22 2010 +0900 @@ -0,0 +1,351 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis * +* * +* Copyright (c) 2009, WIDE Project and NICT * +* All rights reserved. * +* * +* Redistribution and use of this software in source and binary forms, with or without modification, are * +* permitted provided that the following conditions are met: * +* * +* * Redistributions of source code must retain the above * +* copyright notice, this list of conditions and the * +* following disclaimer. * +* * +* * Redistributions in binary form must reproduce the above * +* copyright notice, this list of conditions and the * +* following disclaimer in the documentation and/or other * +* materials provided with the distribution. * +* * +* * Neither the name of the WIDE Project or NICT nor the * +* names of its contributors may be used to endorse or * +* promote products derived from this software without * +* specific prior written permission of WIDE Project and * +* NICT. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED * +* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * +* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * +* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * +* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +*********************************************************************************************************/ + +/* Manage incoming RADIUS messages. */ + +#include "rgw.h" + +/* How many threads to handle messages in parallel ? */ +#define NB_WORKERS 2 + +static pthread_t workers[NB_WORKERS]; +static struct fifo * work_stack = NULL; + +/* Data that is stacked */ +struct work_item { + struct rgw_radius_msg_meta * msg; + struct rgw_client * cli; +}; + +/* Data stored in freeDiameter while pending Diameter answer */ +struct pending_answer { + struct rgw_radius_msg_meta * rad; /* the RADIUS message that was received and translated */ + struct rgw_client * cli; /* the client it was received from */ + struct session * sess; /* the Diameter session created for this message (useful?) */ +}; + +/* Callback when a Diameter answer is received */ +static void receive_diam_answer(void * paback, struct msg **ans); + +/* Worker thread, processing incoming RADIUS messages (after parsing) */ +static void * work_th(void * arg) +{ + char thname[10]; + + TRACE_ENTRY("%p", arg); + + /* Set the thread name */ + { + char buf[48]; + snprintf(buf, sizeof(buf), "radgw/worker #%d", (int)arg); + fd_log_threadname ( buf ); + } + + while (1) { /* The thread will be cancelled */ + + struct rgw_radius_msg_meta * msg; + struct rgw_client * cli; + struct session * session; + struct msg * diam_msg; + int pb, a; + struct pending_answer * pa; + + /* Get the next incoming RADIUS message */ + { + struct work_item * wi = NULL; + + CHECK_FCT_DO( fd_fifo_get(work_stack, &wi), break ); + + msg = wi->msg; + cli = wi->cli; + free(wi); + } + + TRACE_DEBUG(ANNOYING, "Processing next RADIUS message: %p received on client: %p", msg, cli); + + /* process the data */ + + /* Check authenticator, if any */ + CHECK_FCT_DO( rgw_msg_auth_check(msg, cli, NULL), + { + /* An error occurred, discard message */ + rgw_msg_free(&msg); + rgw_clients_dispose(&cli); + continue; + } ); + + /* Check duplicate */ + CHECK_FCT_DO( rgw_clients_check_dup(&msg, cli), + { + /* An error occurred, discard message */ + rgw_msg_free(&msg); + rgw_clients_dispose(&cli); + continue; + } ); + if (msg == NULL) { + rgw_clients_dispose(&cli); + continue; /* the message was a duplicate */ + } + + /* Check that IP is coherent with the identity in the message */ + CHECK_FCT_DO( rgw_clients_check_origin(msg, cli), + { + /* An error occurred, discard message */ + rgw_msg_free(&msg); + rgw_clients_dispose(&cli); + continue; + } ); + + /* Note: after this point, the radius message buffer may not be consistent with the array of attributes anymore. */ + + session = NULL; + diam_msg = NULL; + + /* Create the session and an empty message with default common AVPs */ + CHECK_FCT_DO( rgw_msg_create_base(msg, cli, &session, &diam_msg), + { + /* An error occurred, discard message */ + rgw_msg_free(&msg); + rgw_clients_dispose(&cli); + continue; + } ); + + /* Pass the message to the list of registered plugins */ + CHECK_FCT_DO( rgw_plg_loop_req(&msg, &session, &diam_msg, cli), + { + /* An error occurred, discard message */ + rgw_msg_free(&msg); + rgw_clients_dispose(&cli); + continue; + } ); + if (msg == NULL) { + rgw_clients_dispose(&cli); + continue; /* the message was handled already */ + } + + pb = 0; + + /* Check the created Diameter message */ + if ((diam_msg == NULL) || ( fd_msg_parse_rules(diam_msg, fd_g_config->cnf_dict, NULL) ) ) { + fd_log_debug("[radgw] No or invalid Diameter message was generated after processing the RADIUS command %hhd (%s).\n" + " This is likely an implementation problem, please report.\n", + msg->radius.hdr->code, rgw_msg_code_str(msg->radius.hdr->code)); + /* We might also dump the conflicting rule here if useful */ + pb++; + } + + /* Check if the full content of the RADIUS message was handled */ + for (a = 0; a < msg->radius.attr_used; a++) { + struct radius_attr_hdr *attr = (struct radius_attr_hdr *)(msg->radius.buf + msg->radius.attr_pos[a]); + pb++; + fd_log_debug("[radgw] No plugin available to handle attribute %hhd (%s) in command %hhd (%s)! Translation aborted.\n", + attr->type, rgw_msg_attrtype_str(attr->type), + msg->radius.hdr->code, rgw_msg_code_str(msg->radius.hdr->code)); + } + + /* Check the session is correct */ + ASSERT(session != NULL); + + if (pb) { + /* Something went wrong during the conversion */ + if (session) { + CHECK_FCT_DO( fd_sess_destroy(&session), ); + } + + if (diam_msg) { + CHECK_FCT_DO( fd_msg_free(diam_msg), ); + diam_msg = NULL; + } + + rgw_msg_free(&msg); + rgw_clients_dispose(&cli); + + TRACE_DEBUG(INFO, "%d problem(s) occurred while translating a RADIUS message, data discarded.\n", pb); + continue; + } + + /* Send the Diameter message and register for receiving the answer */ + CHECK_MALLOC_DO( pa = malloc(sizeof(struct pending_answer)), break ); + memset(pa, 0, sizeof(*pa)); + pa->rad = msg; + pa->cli = cli; + pa->sess= session; + + CHECK_FCT_DO( fd_msg_send( &diam_msg, receive_diam_answer, pa), + { + /* If an error occurs, log and destroy the data */ + fd_log_debug("An error occurred while sending Diameter message, please turn Debug on for detail.\n"); + if (session) { + CHECK_FCT_DO( fd_sess_destroy(&session), ); + } + + if (diam_msg) { + CHECK_FCT_DO( fd_msg_free(diam_msg), ); + diam_msg = NULL; + } + + rgw_msg_free(&msg); + rgw_clients_dispose(&cli); + + free(pa); + + continue; + } ); + + /* Done! */ + } + + TRACE_DEBUG(INFO, "Thread terminated!"); + return NULL; +} + +static void receive_diam_answer(void * paback, struct msg **ans) +{ + struct pending_answer * pa = (struct pending_answer *)paback; + struct radius_msg * rad_ans; + struct avp *avp; + struct avp_hdr *ahdr; + int pb = 0; + + TRACE_ENTRY("%p %p", pa, ans); + CHECK_PARAMS_DO( pa && ans, return ); + + /* Create an empty RADIUS answer message */ + CHECK_MALLOC_DO( rad_ans = radius_msg_new(0, pa->rad->radius.hdr->identifier), goto out ); + + /* Pass the Diameter answer to the same extensions as the request */ + CHECK_FCT_DO( rgw_plg_loop_ans(pa->rad, pa->sess, ans, &rad_ans, pa->cli), goto out ); + + /* Now check what AVPs remain in the diameter answer. If AVPs with the 'M' flag are here, we have a problem... */ + CHECK_FCT_DO( fd_msg_browse(*ans, MSG_BRW_FIRST_CHILD, &avp, NULL), { avp = NULL; pb++; } ); + while (avp) { + CHECK_FCT_DO( fd_msg_avp_hdr ( avp, &ahdr ), { pb++; continue; } ); + if (ahdr->avp_flags & AVP_FLAG_MANDATORY) { + if (ahdr->avp_flags & AVP_FLAG_VENDOR) { + TRACE_DEBUG(FULL, "Remaining Mandatory Vendor AVP, code %d", ahdr->avp_code); + pb++; + } else { + switch (ahdr->avp_code) { + /* A few AVPs can be safely ignored here: */ + case DIAM_ATTR_ROUTE_RECORD: + case DIAM_ATTR_PROXY_INFO: + + + /* just ignore */ + break; + + default: + TRACE_DEBUG(FULL, "Remaining Mandatory AVP, code %d", ahdr->avp_code); + pb++; + } + } + } + CHECK_FCT_DO( fd_msg_browse(avp, MSG_BRW_NEXT, &avp, NULL), { pb++; break; } ); + } + + if (pb) { + TRACE_DEBUG(INFO, "[radgw] WARNING: %d mandatory AVP in the Diameter answer have not been translated to RADIUS!\n Please use plg_debug.rgwx for more information.", pb); + } + + /* Now try and send the RADIUS answer */ + if (rad_ans) { + CHECK_FCT_DO( rgw_client_finish_send(&rad_ans, pa->rad, pa->cli), goto out); + } + +out: + /* Destroy remaining session data (stateless gateway) */ + CHECK_FCT_DO( fd_sess_destroy(&pa->sess), ); + + /* Clear the Diameter message */ + if (*ans) { + CHECK_FCT_DO( fd_msg_free(*ans), ); + *ans = NULL; + } + + /* Release reference on the client */ + rgw_clients_dispose(&pa->cli); + + /* Clear the answer data */ + free(pa); + + /* Finished */ + return; +} + +int rgw_work_start(void) +{ + int i; + TRACE_ENTRY(); + + memset(workers, 0, sizeof(workers)); + + CHECK_FCT( fd_fifo_new ( &work_stack ) ); + + /* Create the worker thread(s) */ + for (i = 0; i < NB_WORKERS; i++) { + CHECK_POSIX( pthread_create(&workers[i], NULL, work_th, (void *)i) ); + } + + return 0; +} + +int rgw_work_add(struct rgw_radius_msg_meta * msg, struct rgw_client * client) +{ + struct work_item * new; + + CHECK_MALLOC( new = malloc(sizeof(struct work_item)) ); + memset(new, 0, sizeof(struct work_item)); + + new->msg = msg; + new->cli = client; + + CHECK_FCT( fd_fifo_post(work_stack, &new) ); + + return 0; +} + +void rgw_work_fini(void) +{ + int i; + TRACE_ENTRY(); + + for (i = 0; i < NB_WORKERS; i++) { + fd_thr_term(&workers[i]); + } + + TODO("Empty the stack, what to do about the RADIUS messages?"); + + return; +}