1961 lines
57 KiB
C
1961 lines
57 KiB
C
/*
|
|
******************************************************************************
|
|
* SOURCE FILE
|
|
*
|
|
* Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipike_radius.c,v $ $Revision: 1.37.10.4 $
|
|
* $Source: /home/interpeak/CVSRoot/ipike/src/ipike_radius.c,v $
|
|
* $Author: zan $ $Date: 2013-08-07 12:43:42 $
|
|
* $State: Exp $ $Locker: $
|
|
*
|
|
* INTERPEAK_COPYRIGHT_STRING
|
|
* Design and implementation by Kenneth Jonsson <kenneth@interpeak.se>
|
|
******************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 1 DESCRIPTION
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 2 CONFIGURATION
|
|
****************************************************************************
|
|
*/
|
|
#include "ipike_config.h"
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 3 INCLUDE FILES
|
|
****************************************************************************
|
|
*/
|
|
|
|
#ifdef IPIKE_USE_RADIUS
|
|
|
|
#include "ipike_h.h"
|
|
#include "ipike_radius.h"
|
|
#include "ipike_sadb.h"
|
|
#include <ipcom_clib.h>
|
|
#include <ipcom_syslog.h>
|
|
#include <ipcom_syslog.h>
|
|
#include <openssl/md5.h>
|
|
/*
|
|
****************************************************************************
|
|
* 4 EXTERN PROTOTYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 5 DEFINES
|
|
****************************************************************************
|
|
*/
|
|
|
|
#define IPIKE_EAP_MAX_BUF 1400u
|
|
#define IPIKE_EAP_HEADER_LEN 5u
|
|
|
|
#define IPIKE_RADIUS_VENDOR_ID_MICROSOFT 311
|
|
#define IPIKE_RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY 16
|
|
#define IPIKE_RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY 17
|
|
|
|
/* Cisco LEAP */
|
|
#define IPIKE_RADIUS_VENDOR_ID_CISCO 9
|
|
#define IPIKE_RADIUS_VENDOR_ATTR_CISCO_SESSION_KEY 1
|
|
#define IPIKE_RADIUS_CISCO_SESSION_KEY_LEN 34
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 6 TYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
typedef struct Ipike_radius_list_elem_st
|
|
{
|
|
Ipike_radius_server* v;
|
|
char* ike_id;
|
|
} Ipike_radius_list_elem;
|
|
|
|
typedef struct Ipike_radius_list_key_st
|
|
{
|
|
Ip_fd fd;
|
|
const char* ike_id;
|
|
} Ipike_radius_list_key;
|
|
|
|
typedef struct Ipike_radius_unique_id_struct
|
|
{
|
|
Ip_u8 radius_id;
|
|
Ip_u8* unique_id;
|
|
Ip_u32 unique_id_len;
|
|
} Ipike_radius_unique_id;
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 7 LOCAL PROTOTYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_radius_create_server
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*/
|
|
IP_STATIC Ipike_radius_server*
|
|
ipike_radius_create_server(
|
|
union Ip_sockaddr_union* server_addr,
|
|
union Ip_sockaddr_union* local_addr,
|
|
const char* shared_secret);
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_radius_free_server
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*/
|
|
IP_STATIC void
|
|
ipike_radius_free_server(Ipike_radius_server* ctx);
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_radius_hash_key
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*/
|
|
IP_STATIC unsigned
|
|
ipike_radius_hash_key(Ipike_radius_list_key* key);
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_radius_hash_obj
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*/
|
|
IP_STATIC unsigned
|
|
ipike_radius_hash_obj(Ipike_radius_list_elem* elem);
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_radius_hash_cmp
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*/
|
|
IP_STATIC Ip_bool
|
|
ipike_radius_hash_cmp(Ipike_radius_list_elem* elem, Ipike_radius_list_key* key);
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_radius_hash_add
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*/
|
|
IP_STATIC Ipike_error
|
|
ipike_radius_hash_add(Ipike_radius_server* server, const char* ike_id);
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_sadb_hash_free
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*/
|
|
IP_STATIC void
|
|
ipike_radius_hash_free(Ipike_radius_list_elem* elem, void* unused);
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_radius_hash_get
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*/
|
|
IP_STATIC Ipike_radius_server*
|
|
ipike_radius_hash_get(Ip_fd fd, const char* ike_id);
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_radius_get_server
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*/
|
|
IP_STATIC Ipike_radius_server*
|
|
ipike_radius_get_server(
|
|
const char* ike_id,
|
|
union Ip_sockaddr_union* server_addr,
|
|
union Ip_sockaddr_union* local_addr,
|
|
const char* shared_secret);
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_radius_receive_buffer_size
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*/
|
|
IP_STATIC Ip_u32
|
|
ipike_radius_receive_buffer_size();
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_radius_alloc_receive_buffer
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*/
|
|
IP_STATIC Ipradius_header*
|
|
ipike_radius_alloc_receive_buffer();
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_radius_free_receive_buffer
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*/
|
|
IP_STATIC void
|
|
ipike_radius_free_receive_buffer(Ipradius_header** msg);
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipeap_sim_radius_get_vendor_attr
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*/
|
|
IP_STATIC Ip_u8*
|
|
ipike_radius_get_vendor_attr(
|
|
Ipradius_header* msg,
|
|
Ip_u32 vendor,
|
|
Ip_u8 subtype,
|
|
Ip_u32* len);
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_radius_decrypt_microsoft_key
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*/
|
|
IP_STATIC Ip_u8*
|
|
ipike_radius_decrypt_microsoft_key(
|
|
const Ip_u8* enc,
|
|
Ip_u32 enc_len,
|
|
const Ip_u8* req_authenticator,
|
|
const Ip_u8* secret,
|
|
Ip_u32 secret_len,
|
|
Ip_u32* reslen);
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_radius_get_cisco_session_key
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*/
|
|
IP_STATIC Ipike_error
|
|
ipike_radius_get_cisco_session_key(
|
|
Ipike_radius_ctx* ctx,
|
|
Ip_u8** key,
|
|
Ip_u32* key_len);
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_radius_get_microsoft_key
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*/
|
|
IP_STATIC Ipike_error
|
|
ipike_radius_get_microsoft_keys(
|
|
Ipike_radius_ctx* ctx,
|
|
Ip_u8** akey_recv,
|
|
Ip_u8** akey_send,
|
|
Ip_u32* key_len);
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_radius_event_callback
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*/
|
|
IP_STATIC Ip_err
|
|
ipike_radius_event_callback(
|
|
int event,
|
|
Ipradius_request* request,
|
|
Ipradius_header* request_packet,
|
|
Ipradius_header* response_packet,
|
|
Ipradius_server* server);
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_radius_msg_add_eap
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*/
|
|
IP_STATIC Ipike_error
|
|
ipike_radius_msg_add_eap(Ipeap_pkt_hdr* hdr, Ipradius_header* msg);
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_radius_msg_extract_eap
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*/
|
|
IP_STATIC Ipike_error
|
|
ipike_radius_msg_extract_eap(
|
|
Ipradius_header* msg,
|
|
Ipike_value* eap);
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_radius_msg_add_cookies
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*/
|
|
IP_STATIC Ipike_error
|
|
ipike_radius_msg_add_unique_id(
|
|
Ip_u8* unique_id,
|
|
Ip_u32 unique_id_len,
|
|
Ipradius_header* msg);
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_radius_msg_extract_unique_id
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*/
|
|
IP_STATIC Ipike_error
|
|
ipike_radius_msg_extract_unique_id(
|
|
Ipradius_header* msg,
|
|
Ip_u8* unique_id,
|
|
Ip_u32* unique_id_len);
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_radius_send_message
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*/
|
|
IP_STATIC Ip_err
|
|
ipike_radius_send_message(Ipike_radius_ctx* ctx, Ipradius_header* msg);
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_radius_receive_message
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*/
|
|
IP_STATIC Ip_err
|
|
ipike_radius_receive_message(Ip_fd fd, Ipradius_header* msg);
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 8 DATA
|
|
****************************************************************************
|
|
*/
|
|
|
|
const Ip_u8 EAP_START[] =
|
|
{
|
|
0x01,0x00,0x00,0x05,0x01
|
|
};
|
|
const Ip_u32 EAP_START_LEN = 5;
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 9 LOCAL FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_radius_create_server
|
|
*===========================================================================
|
|
*/
|
|
IP_STATIC Ipike_radius_server*
|
|
ipike_radius_create_server(
|
|
union Ip_sockaddr_union* server_addr,
|
|
union Ip_sockaddr_union* local_addr,
|
|
const char* shared_secret)
|
|
{
|
|
Ipike_radius_server* ctx = IP_NULL;
|
|
|
|
if ((server_addr == IP_NULL) ||
|
|
(local_addr == IP_NULL) ||
|
|
(shared_secret == IP_NULL))
|
|
{
|
|
IPCOM_LOG0(DEBUG2, "ipike_radius_get_server: Bad radius configuration");
|
|
return IP_NULL;
|
|
}
|
|
|
|
|
|
|
|
if (!ipike.radius.initialized)
|
|
(void)ipike_radius_startup();
|
|
|
|
ctx = (Ipike_radius_server*)ipcom_calloc(1, sizeof(*ctx));
|
|
if (ctx != IP_NULL)
|
|
{
|
|
IPCOM_LOG1(DEBUG2, "ipike_radius_get_server: Radius server: '%s'", ipike_addr_to_str(server_addr));
|
|
IPCOM_LOG1(DEBUG2, "ipike_radius_get_server: Local address: '%s'", ipike_addr_to_str(local_addr));
|
|
|
|
(void)ipike_addr_copy(&ctx->server.server_addr, server_addr);
|
|
(void)ipike_addr_copy(&ctx->local_addr, local_addr);
|
|
|
|
if (ipike_addr_get_port(&ctx->server.server_addr) == 0)
|
|
(void)ipike_addr_set_port(&ctx->server.server_addr, 1812);
|
|
|
|
ctx->fd = IP_INVALID_SOCKET;
|
|
ctx->server.index = 0;
|
|
ctx->server.pollnum = 0;
|
|
ctx->server.pollsec = 3;
|
|
if (ipcom_strlen(shared_secret) < sizeof(ctx->server.shared_secret))
|
|
{
|
|
ipcom_strcpy((char*)ctx->server.shared_secret, shared_secret);
|
|
ctx->server.shared_secret_len = (Ip_u32) ipcom_strlen((char*)ctx->server.shared_secret);
|
|
ctx->server.conf[0] = (unsigned long)-1;
|
|
(void)ipradius_server_add(&ctx->server);
|
|
#ifdef IPCOM_USE_INET
|
|
if (ctx->local_addr.sa.sa_family == IP_AF_INET)
|
|
{
|
|
ctx->fd = ipcom_socket(IP_AF_INET, IP_SOCK_DGRAM, 0);
|
|
if (ctx->fd != IP_INVALID_SOCKET)
|
|
{
|
|
if (ipcom_bind(ctx->fd,
|
|
(struct Ip_sockaddr *)&ctx->local_addr,
|
|
sizeof(ctx->local_addr)) < 0)
|
|
{
|
|
ipcom_socketclose(ctx->fd);
|
|
ctx->fd = IP_INVALID_SOCKET;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
#ifdef IPCOM_USE_INET6
|
|
if (ctx->local_addr.sa.sa_family == IP_AF_INET6)
|
|
{
|
|
ctx->fd = ipcom_socket(IP_AF_INET6, IP_SOCK_DGRAM, 0);
|
|
if (ctx->fd != IP_INVALID_SOCKET)
|
|
{
|
|
if (ipcom_bind(ctx->fd,
|
|
(struct Ip_sockaddr *)&ctx->local_addr,
|
|
sizeof(ctx->local_addr)) < 0)
|
|
{
|
|
ipcom_socketclose(ctx->fd);
|
|
ctx->fd = IP_INVALID_SOCKET;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
if ((ctx->fd != IP_INVALID_SOCKET) &&
|
|
(ipike_dispatch_add_radius_socket(ctx->fd) != IPIKE_SUCCESS))
|
|
{
|
|
ipcom_socketclose(ctx->fd);
|
|
ctx->fd = IP_INVALID_SOCKET;
|
|
}
|
|
if (ctx->fd == IP_INVALID_SOCKET)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipike_radius_create: failed to connect radius server");
|
|
ipcom_free(ctx);
|
|
ctx = IP_NULL;
|
|
}
|
|
}
|
|
|
|
return ctx;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_radius_free_server
|
|
*===========================================================================
|
|
*/
|
|
IP_STATIC void
|
|
ipike_radius_free_server(Ipike_radius_server* ctx)
|
|
{
|
|
if (ctx != IP_NULL)
|
|
{
|
|
(void)ipradius_server_remove(&ctx->server);
|
|
if (ctx->fd != IP_INVALID_SOCKET)
|
|
{
|
|
ipike_dispatch_remove_radius_socket(ctx->fd);
|
|
ipcom_socketclose(ctx->fd);
|
|
}
|
|
ipcom_free(ctx);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_radius_hash_key
|
|
*===========================================================================
|
|
*/
|
|
IP_STATIC unsigned
|
|
ipike_radius_hash_key(Ipike_radius_list_key* key)
|
|
{
|
|
unsigned val = 0;
|
|
|
|
if (key->ike_id != IP_NULL)
|
|
val = ipcom_hash_update(key->ike_id, (int)ipcom_strlen(key->ike_id), 0);
|
|
else
|
|
val = ipcom_hash_update(&key->fd, sizeof(key->fd), val);
|
|
|
|
return val;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_radius_hash_obj
|
|
*===========================================================================
|
|
*/
|
|
IP_STATIC unsigned
|
|
ipike_radius_hash_obj(Ipike_radius_list_elem* elem)
|
|
{
|
|
Ipike_radius_list_key key;
|
|
|
|
key.fd = elem->v->fd;
|
|
key.ike_id = elem->ike_id;
|
|
|
|
return ipike_radius_hash_key(&key);
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_radius_hash_cmp
|
|
*===========================================================================
|
|
*/
|
|
IP_STATIC Ip_bool
|
|
ipike_radius_hash_cmp(
|
|
Ipike_radius_list_elem* elem,
|
|
Ipike_radius_list_key* key)
|
|
{
|
|
if (key->ike_id != IP_NULL)
|
|
{
|
|
if ((elem->ike_id != IP_NULL) &&
|
|
(!ipcom_strcmp(elem->ike_id, key->ike_id)))
|
|
return IP_TRUE;
|
|
}
|
|
else
|
|
{
|
|
if (key->fd == elem->v->fd)
|
|
return IP_TRUE;
|
|
}
|
|
|
|
return IP_FALSE;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_radius_hash_add
|
|
*===========================================================================
|
|
*/
|
|
IP_STATIC Ipike_error
|
|
ipike_radius_hash_add(Ipike_radius_server* server, const char* ike_id)
|
|
{
|
|
Ipike_radius_list_elem* elem;
|
|
|
|
/* Create for serach by both ike_id and fd */
|
|
elem = ipcom_malloc(sizeof(*elem));
|
|
if (elem == IP_NULL)
|
|
{
|
|
IP_PANIC();
|
|
return IPIKE_OUT_OF_MEMORY;
|
|
}
|
|
elem->v = server;
|
|
elem->ike_id = ipcom_strdup(ike_id);
|
|
if (ipcom_hash_add(ipike.radius.hash_table, elem) != IPCOM_SUCCESS)
|
|
{
|
|
ipcom_free(elem);
|
|
return IPIKE_FAILED;
|
|
}
|
|
elem = ipcom_malloc(sizeof(*elem));
|
|
if (elem == IP_NULL)
|
|
{
|
|
IP_PANIC();
|
|
return IPIKE_OUT_OF_MEMORY;
|
|
}
|
|
elem->v = server;
|
|
elem->ike_id = IP_NULL;
|
|
if (ipcom_hash_add(ipike.radius.hash_table, elem) != IPCOM_SUCCESS)
|
|
{
|
|
ipcom_free(elem);
|
|
return IPIKE_FAILED;
|
|
}
|
|
|
|
return IPIKE_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_sadb_hash_free
|
|
*===========================================================================
|
|
*/
|
|
IP_STATIC void
|
|
ipike_radius_hash_free(Ipike_radius_list_elem* elem, void* unused)
|
|
{
|
|
(void) unused;
|
|
|
|
/* Always added twice, so only delete server when ike_id present */
|
|
if (elem->ike_id != IP_NULL)
|
|
{
|
|
ipike_radius_free_server(elem->v);
|
|
ipcom_free(elem->ike_id);
|
|
}
|
|
ipcom_free(elem);
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_radius_hash_get
|
|
*===========================================================================
|
|
*/
|
|
IP_STATIC Ipike_radius_server*
|
|
ipike_radius_hash_get(Ip_fd fd, const char* ike_id)
|
|
{
|
|
Ipike_radius_list_key key;
|
|
Ipike_radius_list_elem* elem;
|
|
|
|
if (ipike.radius.hash_table != IP_NULL)
|
|
{
|
|
key.fd = fd;
|
|
key.ike_id = ike_id;
|
|
elem = (Ipike_radius_list_elem*)ipcom_hash_get(ipike.radius.hash_table, &key);
|
|
if (elem != IP_NULL)
|
|
return elem->v;
|
|
}
|
|
|
|
return IP_NULL;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_radius_hash_unique_id_key
|
|
*===========================================================================
|
|
*/
|
|
IP_STATIC unsigned
|
|
ipike_radius_hash_unique_id_key(Ip_u8* key)
|
|
{
|
|
return ipcom_hash_update((void*)key, 1, 0);
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_radius_hash_unique_id_obj
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*/
|
|
IP_STATIC unsigned
|
|
ipike_radius_hash_unique_id_obj(Ipike_radius_unique_id* elem)
|
|
{
|
|
return ipike_radius_hash_unique_id_key(&elem->radius_id);
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_radius_hash_unique_id_cmp
|
|
*===========================================================================
|
|
*/
|
|
IP_STATIC Ip_bool
|
|
ipike_radius_hash_unique_id_cmp(Ip_u8* key, Ipike_radius_unique_id* elem)
|
|
{
|
|
if (*key != elem->radius_id)
|
|
return IP_FALSE;
|
|
return IP_TRUE;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_radius_hash_unique_id_get
|
|
*===========================================================================
|
|
*/
|
|
IP_STATIC Ip_bool
|
|
ipike_radius_hash_unique_id_get(Ip_u8 rad_id, Ip_u8* id, Ip_u32* id_len)
|
|
{
|
|
Ipike_radius_unique_id* elem;
|
|
|
|
if (ipike.radius.unique_id_table != IP_NULL)
|
|
{
|
|
elem = (Ipike_radius_unique_id*)ipcom_hash_get(ipike.radius.unique_id_table, &rad_id);
|
|
if (elem == IP_NULL)
|
|
{
|
|
return IP_FALSE;
|
|
}
|
|
else
|
|
{
|
|
if (IP_NULL != id)
|
|
{
|
|
if (*id_len < elem->unique_id_len)
|
|
return IP_FALSE;
|
|
*id_len = elem->unique_id_len;
|
|
ipcom_memcpy(id, elem->unique_id, *id_len);
|
|
}
|
|
return IP_TRUE;
|
|
}
|
|
}
|
|
|
|
return IP_FALSE;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_radius_hash_unique_id_free
|
|
*===========================================================================
|
|
*/
|
|
IP_STATIC void
|
|
ipike_radius_hash_unique_id_free(Ipike_radius_unique_id* elem, void* unused)
|
|
{
|
|
(void) unused;
|
|
|
|
if (IP_NULL != elem)
|
|
{
|
|
if (IP_NULL != elem->unique_id)
|
|
ipcom_free(elem->unique_id);
|
|
ipcom_free(elem);
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_radius_hash_unique_id_remove
|
|
*===========================================================================
|
|
*/
|
|
IP_STATIC void
|
|
ipike_radius_hash_unique_id_remove(Ip_u8 rad_id)
|
|
{
|
|
Ipike_radius_unique_id* elem = IP_NULL;
|
|
|
|
elem = (Ipike_radius_unique_id*)ipcom_hash_get(ipike.radius.unique_id_table, &rad_id);
|
|
if (elem != IP_NULL)
|
|
{
|
|
(void)ipcom_hash_remove(ipike.radius.unique_id_table, (void*)elem);
|
|
ipike_radius_hash_unique_id_free(elem, IP_NULL);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_radius_hash_unique_id_add
|
|
*===========================================================================
|
|
*/
|
|
IP_STATIC Ip_bool
|
|
ipike_radius_hash_unique_id_add(Ip_u8 rad_id, Ip_u8* id, Ip_u32 id_len)
|
|
{
|
|
Ip_bool success = IP_FALSE;
|
|
Ipike_radius_unique_id* elem = IP_NULL;
|
|
|
|
if (ipike_radius_hash_unique_id_get(rad_id, IP_NULL, IP_NULL))
|
|
{
|
|
IPCOM_LOG1(DEBUG, "ipike_radius_hash_unique_id_add: id %u already used, will be removed", rad_id);
|
|
ipike_radius_hash_unique_id_remove(rad_id);
|
|
}
|
|
elem = ipcom_malloc(sizeof(*elem));
|
|
if (IP_NULL != elem)
|
|
{
|
|
elem->unique_id = ipcom_malloc(id_len);
|
|
if (IP_NULL != elem->unique_id)
|
|
{
|
|
elem->radius_id = rad_id;
|
|
ipcom_memcpy(elem->unique_id, id, id_len);
|
|
elem->unique_id_len = id_len;
|
|
if (ipcom_hash_add(ipike.radius.unique_id_table, elem) == IPCOM_SUCCESS)
|
|
success = IP_TRUE;
|
|
}
|
|
if (!success)
|
|
ipike_radius_hash_unique_id_free(elem, IP_NULL);
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_radius_get_server
|
|
*===========================================================================
|
|
*/
|
|
IP_STATIC Ipike_radius_server*
|
|
ipike_radius_get_server(
|
|
const char* ike_id,
|
|
union Ip_sockaddr_union* server_addr,
|
|
union Ip_sockaddr_union* local_addr,
|
|
const char* shared_secret)
|
|
{
|
|
Ipike_radius_server* server;
|
|
|
|
/* Try to get radius server by ike_id and create at failure */
|
|
server = ipike_radius_hash_get(0, ike_id);
|
|
if (server == IP_NULL)
|
|
{
|
|
server = ipike_radius_create_server(server_addr, local_addr, shared_secret);
|
|
if (server != IP_NULL)
|
|
{
|
|
if (ipike_radius_hash_add(server, ike_id) != IPIKE_SUCCESS)
|
|
{
|
|
ipike_radius_free_server(server);
|
|
server = IP_NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
return server;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_radius_receive_buffer_size
|
|
*===========================================================================
|
|
*/
|
|
IP_STATIC Ip_u32
|
|
ipike_radius_receive_buffer_size()
|
|
{
|
|
return (Ip_u32)(IPIKE_EAP_MAX_BUF);
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_radius_alloc_receive_buffer
|
|
*===========================================================================
|
|
*/
|
|
IP_STATIC Ipradius_header*
|
|
ipike_radius_alloc_receive_buffer()
|
|
{
|
|
return (Ipradius_header*)ipcom_calloc(1, ipike_radius_receive_buffer_size());
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_radius_free_receive_buffer
|
|
*===========================================================================
|
|
*/
|
|
IP_STATIC void
|
|
ipike_radius_free_receive_buffer(Ipradius_header** msg)
|
|
{
|
|
if ((msg != IP_NULL) && (*msg != IP_NULL))
|
|
{
|
|
ipcom_free(*msg);
|
|
*msg = IP_NULL;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipeap_sim_radius_get_vendor_attr
|
|
*===========================================================================
|
|
*/
|
|
IP_STATIC Ip_u8*
|
|
ipike_radius_get_vendor_attr(
|
|
Ipradius_header* msg,
|
|
Ip_u32 vendor,
|
|
Ip_u8 subtype,
|
|
Ip_u32* len)
|
|
{
|
|
Ip_u8* attr = IP_NULL;
|
|
Ip_u8* data = IP_NULL;
|
|
|
|
while ((attr = ipradius_attr_get_next(msg, attr)) != IP_NULL)
|
|
{
|
|
if ((attr[0] == IPRADIUS_ATTR_Vendor_Specific) &&
|
|
(attr[1] > 7) &&
|
|
(attr[2] == ((vendor >> 24) & 0xff)) &&
|
|
(attr[3] == ((vendor >> 16) & 0xff)) &&
|
|
(attr[4] == ((vendor >> 8) & 0xff)) &&
|
|
(attr[5] == ((vendor) & 0xff)) &&
|
|
(attr[6] == subtype))
|
|
{
|
|
*len = (Ip_u32) (attr[7] - 2);
|
|
data = &attr[8];
|
|
break;
|
|
}
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_radius_decrypt_microsoft_key
|
|
*===========================================================================
|
|
*/
|
|
IP_STATIC Ip_u8*
|
|
ipike_radius_decrypt_microsoft_key(
|
|
const Ip_u8* enc,
|
|
Ip_u32 enc_len,
|
|
const Ip_u8* req_authenticator,
|
|
const Ip_u8* secret,
|
|
Ip_u32 secret_len,
|
|
Ip_u32* reslen)
|
|
{
|
|
const Ip_u8* salt = IP_NULL;
|
|
Ip_u8* plain = IP_NULL;
|
|
Ip_bool first = IP_TRUE;
|
|
Ip_size_t left = 0;
|
|
const Ip_u8* pos = IP_NULL;
|
|
Ip_u8* p = IP_NULL;
|
|
MD5_CTX ctx = {0};
|
|
Ip_u8 digest[16] = {0};
|
|
Ip_u32 i = 0;
|
|
Ip_u8* res = IP_NULL;
|
|
|
|
/* For detail information read RFC 2548 */
|
|
|
|
/* First two bytes is salt value */
|
|
salt = enc;
|
|
pos = enc + 2;
|
|
left = enc_len - 2;
|
|
/* Should be multiple of 16 bytes */
|
|
if ((left % 16) != 0)
|
|
return IP_NULL;
|
|
|
|
plain = ipcom_malloc(left);
|
|
if (plain == IP_NULL)
|
|
return IP_NULL;
|
|
p = plain;
|
|
while (left > 0)
|
|
{
|
|
MD5_Init(&ctx);
|
|
MD5_Update(&ctx, secret, secret_len);
|
|
/* round 0: MD5(Secret | Authenticator | Salt)
|
|
* round >0: MD5(Secret | <encrypted data>(round - 1)) */
|
|
if (first)
|
|
{
|
|
MD5_Update(&ctx, req_authenticator, 16);
|
|
MD5_Update(&ctx, salt, 2);
|
|
first = IP_FALSE;
|
|
}
|
|
else
|
|
{
|
|
MD5_Update(&ctx, pos - sizeof(digest), sizeof(digest));
|
|
}
|
|
MD5_Final(digest, &ctx);
|
|
|
|
/* xor with digest to decrypt */
|
|
for (i = 0; i < sizeof(digest); i++)
|
|
p[i] = (Ip_u8)(pos[i] ^ digest[i]);
|
|
|
|
p += sizeof(digest);
|
|
pos += sizeof(digest);
|
|
left -= sizeof(digest);
|
|
}
|
|
if (plain[0] >= (enc_len - 2))
|
|
{
|
|
ipcom_free(plain);
|
|
return IP_NULL;
|
|
}
|
|
res = ipcom_malloc(plain[0]);
|
|
if (res == IP_NULL)
|
|
{
|
|
ipcom_free(plain);
|
|
return IP_NULL;
|
|
}
|
|
ipcom_memcpy(res, plain + 1, plain[0]);
|
|
if (reslen != IP_NULL)
|
|
*reslen = plain[0];
|
|
ipcom_free(plain);
|
|
|
|
return res;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_radius_get_microsoft_key
|
|
*===========================================================================
|
|
*/
|
|
IP_STATIC Ipike_error
|
|
ipike_radius_get_cisco_session_key(
|
|
Ipike_radius_ctx* ctx,
|
|
Ip_u8** key,
|
|
Ip_u32* key_len)
|
|
{
|
|
Ip_u8 *enc_key = IP_NULL;
|
|
Ip_u32 enc_key_len = 0;
|
|
Ipike_error err = IPCOM_ERR_FAILED;
|
|
|
|
ip_assert(key);
|
|
|
|
/* Try to get cisco session key */
|
|
enc_key = ipike_radius_get_vendor_attr( ctx->last_msg,
|
|
IPIKE_RADIUS_VENDOR_ID_CISCO,
|
|
IPIKE_RADIUS_VENDOR_ATTR_CISCO_SESSION_KEY,
|
|
&enc_key_len);
|
|
|
|
/* Got session key? */
|
|
if ( enc_key )
|
|
{
|
|
const char *enc_key_prefix = "leap:session-key=";
|
|
Ip_u32 enc_key_prefix_len = (Ip_u32) strlen(enc_key_prefix);
|
|
|
|
/* Make sure we have correct prefix */
|
|
if ( enc_key_prefix_len > enc_key_len ||
|
|
ipcom_strncmp(enc_key_prefix, (const char *)enc_key, enc_key_prefix_len) != 0 )
|
|
{
|
|
IPCOM_LOG1(ERR,
|
|
"ipike_radius_get_cisco_session_key() :: expected '%s' as key prefix",
|
|
enc_key_prefix);
|
|
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
/* Validate session key length */
|
|
if ( enc_key_len - enc_key_prefix_len != IPIKE_RADIUS_CISCO_SESSION_KEY_LEN )
|
|
{
|
|
IPCOM_LOG1(ERR,
|
|
"ipike_radius_get_cisco_session_key() :: bad key length '%u'",
|
|
enc_key_len - enc_key_prefix_len);
|
|
}
|
|
|
|
/* Using the same algorithm as mschap */
|
|
*key = ipike_radius_decrypt_microsoft_key(enc_key + enc_key_prefix_len,
|
|
enc_key_len - enc_key_prefix_len,
|
|
ctx->last_msg->auth,
|
|
ctx->server->server.shared_secret,
|
|
ctx->server->server.shared_secret_len,
|
|
key_len);
|
|
|
|
/* Decrypt Successful?*/
|
|
if ( *key )
|
|
{
|
|
(void)ipike_print_memory(IPCOM_LOG_DEBUG2,
|
|
"ipike_radius_get_cisco_session_key() ::"\
|
|
" Cisco EAP session key:\n",
|
|
*key, *key_len);
|
|
err = IPIKE_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
IPCOM_LOG0(ERR,
|
|
"ipike_radius_get_cisco_session_key() ::"\
|
|
" failed to decrypt cisco session key");
|
|
}
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_radius_get_microsoft_key
|
|
*===========================================================================
|
|
*/
|
|
IP_STATIC Ipike_error
|
|
ipike_radius_get_microsoft_keys(
|
|
Ipike_radius_ctx* ctx,
|
|
Ip_u8** akey_recv,
|
|
Ip_u8** akey_send,
|
|
Ip_u32* key_len)
|
|
{
|
|
Ip_err err = IPCOM_ERR_FAILED;
|
|
Ip_u8* key_send = IP_NULL;
|
|
Ip_u32 key_send_len = 0;
|
|
Ip_u8* key_recv = IP_NULL;
|
|
Ip_u32 key_recv_len = 0;
|
|
|
|
*akey_recv = IP_NULL;
|
|
*akey_send = IP_NULL;
|
|
*key_len = 0;
|
|
/* key_recv and key_send will be pointers to existing buffers */
|
|
key_recv = ipike_radius_get_vendor_attr(
|
|
ctx->last_msg,
|
|
IPIKE_RADIUS_VENDOR_ID_MICROSOFT,
|
|
IPIKE_RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY,
|
|
&key_recv_len);
|
|
key_send = ipike_radius_get_vendor_attr(
|
|
ctx->last_msg,
|
|
IPIKE_RADIUS_VENDOR_ID_MICROSOFT,
|
|
IPIKE_RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY,
|
|
&key_send_len);
|
|
|
|
if ((key_recv != IP_NULL) && (key_send != IP_NULL))
|
|
{
|
|
/* key_recv and key_send will be allocated */
|
|
key_recv = ipike_radius_decrypt_microsoft_key(
|
|
key_recv,
|
|
key_recv_len,
|
|
ctx->last_msg->auth,
|
|
ctx->server->server.shared_secret,
|
|
ctx->server->server.shared_secret_len,
|
|
&key_recv_len);
|
|
key_send = ipike_radius_decrypt_microsoft_key(
|
|
key_send,
|
|
key_send_len,
|
|
ctx->last_msg->auth,
|
|
ctx->server->server.shared_secret,
|
|
ctx->server->server.shared_secret_len,
|
|
&key_send_len);
|
|
|
|
if ((key_send == IP_NULL) ||
|
|
(key_recv == IP_NULL) ||
|
|
(key_recv_len != key_send_len))
|
|
{
|
|
ipcom_free(key_recv);
|
|
ipcom_free(key_send);
|
|
}
|
|
else
|
|
{
|
|
*akey_recv = key_recv;
|
|
*akey_send = key_send;
|
|
*key_len = key_recv_len;
|
|
err = IPIKE_SUCCESS;
|
|
}
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_radius_event_callback
|
|
*===========================================================================
|
|
*/
|
|
IP_STATIC Ip_err
|
|
ipike_radius_event_callback(
|
|
int event,
|
|
Ipradius_request* request,
|
|
Ipradius_header* request_packet,
|
|
Ipradius_header* response_packet,
|
|
Ipradius_server* server)
|
|
{
|
|
(void)request;
|
|
(void)request_packet;
|
|
(void)response_packet;
|
|
(void)server;
|
|
|
|
switch(event)
|
|
{
|
|
case IPRADIUS_EVENT_ACCESS_ACCEPT:
|
|
break;
|
|
case IPRADIUS_EVENT_ACCESS_REJECT:
|
|
break;
|
|
case IPRADIUS_EVENT_ACCESS_CHALLENGE:
|
|
break;
|
|
case IPRADIUS_EVENT_ACCESS_TIMEOUT:
|
|
break;
|
|
case IPRADIUS_EVENT_ACCESS_CANCELLED:
|
|
break;
|
|
case IPRADIUS_EVENT_ACCOUNTING_RESPONSE:
|
|
break;
|
|
case IPRADIUS_EVENT_ACCOUNTING_REQUEST:
|
|
break;
|
|
case IPRADIUS_EVENT_ACCOUNTING_TIMEOUT:
|
|
break;
|
|
case IPRADIUS_EVENT_ACCOUNTING_CANCELLED:
|
|
break;
|
|
|
|
default:
|
|
IP_PANIC();
|
|
break;
|
|
}
|
|
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_radius_msg_add_eap
|
|
*===========================================================================
|
|
*/
|
|
IP_STATIC Ipike_error
|
|
ipike_radius_msg_add_eap(Ipeap_pkt_hdr* hdr, Ipradius_header* msg)
|
|
{
|
|
Ip_u8* data = IP_NULL;
|
|
Ip_size_t len = 0;
|
|
Ip_size_t n = 0;
|
|
|
|
data = (Ip_u8*)hdr;
|
|
len = ip_htons(hdr->len);
|
|
while (len > 0)
|
|
{
|
|
/* Maximum 255 bytes for each attribute, so split to multiple */
|
|
if (len > 253)
|
|
n = 253;
|
|
else
|
|
n = len;
|
|
if (ipradius_attr_add_array(msg,
|
|
IPRADIUS_ATTR_EAP_Message,
|
|
data,
|
|
(Ip_u8)n))
|
|
return IPIKE_FAILED;
|
|
data += n;
|
|
len -= n;
|
|
}
|
|
|
|
return IPIKE_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_radius_msg_extract_eap
|
|
*===========================================================================
|
|
*/
|
|
IP_STATIC Ipike_error
|
|
ipike_radius_msg_extract_eap(
|
|
Ipradius_header* msg,
|
|
Ipike_value* eap)
|
|
{
|
|
Ip_u8* attr = IP_NULL;
|
|
|
|
ipcom_free(eap->value);
|
|
eap->value = IP_NULL;
|
|
eap->len = 0;
|
|
|
|
while ((attr = ipradius_attr_get_next(msg, attr)) != IP_NULL)
|
|
{
|
|
if (attr[0] == IPRADIUS_ATTR_EAP_Message)
|
|
{
|
|
eap->value = ipcom_realloc(eap->value, eap->len + attr[1]);
|
|
if (eap->value != IP_NULL)
|
|
{
|
|
ipcom_memcpy(eap->value + eap->len,
|
|
&attr[2],
|
|
(Ip_size_t) (attr[1]-2));
|
|
eap->len += (Ip_size_t) attr[1] - 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
return (eap->value != IP_NULL) ? IPIKE_SUCCESS : IPIKE_FAILED;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_radius_msg_add_cookies
|
|
*===========================================================================
|
|
*/
|
|
IP_STATIC Ipike_error
|
|
ipike_radius_msg_add_unique_id(
|
|
Ip_u8* unique_id,
|
|
Ip_u32 unique_id_len,
|
|
Ipradius_header* msg)
|
|
{
|
|
char* state;
|
|
|
|
state = ipcom_malloc(2*unique_id_len+1);
|
|
|
|
if (state == IP_NULL)
|
|
return IPIKE_FAILED;
|
|
|
|
ipike_bin2hex(unique_id, unique_id_len, state);
|
|
|
|
if (ipradius_attr_add_array(msg,
|
|
IPRADIUS_ATTR_Proxy_State,
|
|
(Ip_u8*)state,
|
|
(Ip_u8)ipcom_strlen(state)) != IPCOM_SUCCESS)
|
|
{
|
|
ipcom_free(state);
|
|
return IPIKE_FAILED;
|
|
}
|
|
ipcom_free(state);
|
|
|
|
return IPIKE_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_radius_msg_extract_unique_id
|
|
*===========================================================================
|
|
*/
|
|
IP_STATIC Ipike_error
|
|
ipike_radius_msg_extract_unique_id(
|
|
Ipradius_header* msg,
|
|
Ip_u8* unique_id,
|
|
Ip_u32* unique_id_len)
|
|
{
|
|
Ip_u8* attr = IP_NULL;
|
|
Ip_u8* p = IP_NULL;
|
|
Ip_size_t n = 0;
|
|
|
|
while ((attr = ipradius_attr_get_next(msg, attr)) != IP_NULL)
|
|
{
|
|
if (attr[0] == IPRADIUS_ATTR_Proxy_State)
|
|
{
|
|
if (attr[1] > ((2 * (*unique_id_len)) + 2))
|
|
return IPIKE_FAILED;
|
|
n = (Ip_size_t) (attr[1] - 2);
|
|
p = ipike_hex2bin((char*)&attr[2], &n);
|
|
if (p != IP_NULL)
|
|
{
|
|
*unique_id_len = (Ip_u32) n;
|
|
ipcom_memcpy(unique_id, p, n);
|
|
ipcom_free(p);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ((p != IP_NULL) ? IPIKE_SUCCESS : IPIKE_FAILED);
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_radius_send_message
|
|
*===========================================================================
|
|
*/
|
|
IP_STATIC Ip_err
|
|
ipike_radius_send_message(Ipike_radius_ctx* ctx, Ipradius_header* msg)
|
|
{
|
|
Ipradius_request request = {0};
|
|
Ip_u8 errattr = 0;
|
|
|
|
request.unique = ctx->server->id;
|
|
request.pwd_type = IPRADIUS_No_Password;
|
|
/* Set RADIUS socket descriptor and event callback. */
|
|
#ifdef IPCOM_USE_INET
|
|
if (ctx->server->local_addr.sa.sa_family == IP_AF_INET)
|
|
request.fd = ctx->server->fd;
|
|
else
|
|
request.fd = IP_INVALID_SOCKET;
|
|
#endif
|
|
#ifdef IPCOM_USE_INET6
|
|
if (ctx->server->local_addr.sa.sa_family == IP_AF_INET6)
|
|
request.fd6 = ctx->server->fd;
|
|
else
|
|
request.fd6 = IP_INVALID_SOCKET;
|
|
#endif
|
|
request.event_callback = ipike_radius_event_callback;
|
|
|
|
request.num_servers = 1;
|
|
ipcom_memcpy(&request.serv_list[0].server_addr,
|
|
&ctx->server->server.server_addr,
|
|
sizeof(union Ip_sockaddr_union));
|
|
request.serv_list[0].server_type = ctx->server->server.server_type;
|
|
request.serv_list[0].pollnum = ctx->server->server.pollnum;
|
|
request.serv_list[0].pollsec = ctx->server->server.pollsec;
|
|
|
|
return ipradius_access_request(msg, &request, &errattr);
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_radius_receive_message
|
|
*===========================================================================
|
|
*/
|
|
IP_STATIC Ip_err
|
|
ipike_radius_receive_message(
|
|
Ip_fd fd,
|
|
Ipradius_header* msg)
|
|
{
|
|
Ip_s32 maxfd;
|
|
Ip_s32 num;
|
|
Ip_fd_set read_set;
|
|
struct Ip_timeval tmo;
|
|
Ip_err retval;
|
|
Ip_ssize_t length;
|
|
Ip_socklen_t addr_len;
|
|
union Ip_sockaddr_union addr;
|
|
Ip_u32 count = 0;
|
|
|
|
again:
|
|
IP_FD_ZERO(&read_set);
|
|
IP_FD_SET(fd, &read_set);
|
|
maxfd = (Ip_s32)fd;
|
|
|
|
tmo.tv_usec = 0;
|
|
tmo.tv_sec = 3;
|
|
num = ipcom_socketselect(maxfd + 1, &read_set, IP_NULL, IP_NULL, &tmo);
|
|
if (num < 0)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipike_radius_receive_message - out of memory");
|
|
return IPIKE_FAILED;
|
|
}
|
|
else if (num == 0)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipike_radius_receive_message - no server reply");
|
|
return IPIKE_FAILED;
|
|
}
|
|
/* Got a packet waiting... */
|
|
else if (num == 1)
|
|
{
|
|
ipcom_memset(&addr, 0, sizeof(addr));
|
|
addr_len = sizeof(addr);
|
|
/* Read the packet. */
|
|
length = ipcom_recvfrom(fd,
|
|
msg,
|
|
ipike_radius_receive_buffer_size(),
|
|
0,
|
|
&addr.sa,
|
|
&addr_len);
|
|
if (length <= 0)
|
|
{
|
|
IPCOM_LOG1(ERR, "ipike_radius_receive_message - recv failed (%d)", ipcom_errno);
|
|
if (count++ < 10)
|
|
goto again;
|
|
return IPIKE_FAILED;
|
|
}
|
|
|
|
if (ip_ntohs(msg->length)>length || ip_ntohs(msg->length)<=0)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipike_radius_receive_message - invalid packet");
|
|
return IPIKE_FAILED;
|
|
}
|
|
/* Pass packet to RADIUS for input parsing. */
|
|
/* Ipradius_header is followed by data payload, accessing data seems over access */
|
|
/* coverity[tainted_data] */
|
|
retval = ipradius_read(msg, (Ip_size_t) length, &addr.sa);
|
|
if (retval != IPCOM_SUCCESS)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipike_radius_receive_message - input parsing failed");
|
|
if (count++ < 10)
|
|
goto again;
|
|
return IPIKE_FAILED;
|
|
}
|
|
|
|
/* Valid packet, return. */
|
|
return 0;
|
|
}
|
|
|
|
IP_PANIC();
|
|
IPCOM_LOG1(ERR, "ipike_radius_receive_message - internal error, num = %d", num);
|
|
return IPIKE_FAILED;
|
|
}
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 10 PUBLIC FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_radius_create
|
|
*===========================================================================
|
|
*/
|
|
IP_GLOBAL Ipike_radius_ctx*
|
|
ipike_radius_create(
|
|
const char* ike_id,
|
|
union Ip_sockaddr_union* server_addr,
|
|
union Ip_sockaddr_union* local_addr,
|
|
const char* shared_secret)
|
|
{
|
|
Ipike_radius_ctx* ctx = IP_NULL;
|
|
|
|
ctx = (Ipike_radius_ctx*)ipcom_calloc(1, sizeof(*ctx));
|
|
if (ctx != IP_NULL)
|
|
{
|
|
ctx->server = ipike_radius_get_server(ike_id,
|
|
server_addr,
|
|
local_addr,
|
|
shared_secret);
|
|
if (ctx->server == IP_NULL)
|
|
{
|
|
ipcom_free(ctx);
|
|
ctx = IP_NULL;
|
|
}
|
|
}
|
|
|
|
return ctx;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_radius_free
|
|
*===========================================================================
|
|
*/
|
|
IP_GLOBAL void
|
|
ipike_radius_free(Ipike_radius_ctx* ctx)
|
|
{
|
|
if (ctx != IP_NULL)
|
|
{
|
|
if (ctx->last_msg != IP_NULL)
|
|
ipike_radius_free_receive_buffer(&ctx->last_msg);
|
|
ipcom_free(ctx);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_radius_send
|
|
*===========================================================================
|
|
*/
|
|
IP_GLOBAL Ipike_error
|
|
ipike_radius_send(
|
|
Ipike_radius_ctx* ctx,
|
|
Ip_u8* unique_id,
|
|
Ip_u32 unique_id_len,
|
|
Ipeap_pkt* pkt)
|
|
{
|
|
Ip_err err = IPIKE_FAILED;
|
|
Ipeap_pkt_hdr* hdr = IP_NULL;
|
|
Ipradius_header* msg = IP_NULL;
|
|
Ip_u8* data = IP_NULL;
|
|
Ip_u8 len = 0;
|
|
|
|
msg = ipradius_access_request_pkt_malloc();
|
|
if (msg == IP_NULL)
|
|
{
|
|
IP_PANIC();
|
|
return IPIKE_OUT_OF_MEMORY;
|
|
}
|
|
|
|
msg->id = ++ctx->server->id;
|
|
msg->code = IPRADIUS_CODE_Access_Request;
|
|
|
|
hdr = (Ipeap_pkt_hdr*)&pkt->data[pkt->start];
|
|
data = (Ip_u8*)hdr + sizeof(*hdr);
|
|
|
|
if (ctx->identity[0] != 0)
|
|
if (ipradius_attr_add_array(msg,
|
|
IPRADIUS_ATTR_User_Name,
|
|
(Ip_u8*)ctx->identity,
|
|
(Ip_u8)ipcom_strlen(ctx->identity)))
|
|
goto cleanup;
|
|
/* Add unique id so we will find the correct exchange at response */
|
|
if (ipike_radius_msg_add_unique_id(unique_id,
|
|
unique_id_len,
|
|
msg) != IPIKE_SUCCESS)
|
|
goto cleanup;
|
|
/* Copy state from last response */
|
|
if (ctx->last_msg != IP_NULL)
|
|
if ((data = ipradius_attr_get_array(ctx->last_msg,
|
|
IPRADIUS_ATTR_State,
|
|
&len)) != IP_NULL)
|
|
if (ipradius_attr_add_array(msg,
|
|
IPRADIUS_ATTR_State,
|
|
data,
|
|
(Ip_u8)(len-2)))
|
|
goto cleanup;
|
|
|
|
if (ipike_radius_msg_add_eap(hdr, msg))
|
|
goto cleanup;
|
|
|
|
if (ipike_radius_send_message(ctx, msg))
|
|
goto cleanup;
|
|
|
|
/* Store unique id, so we may handle if state isn't returned */
|
|
(void)ipike_radius_hash_unique_id_add(msg->id, unique_id, unique_id_len);
|
|
|
|
msg = IP_NULL;
|
|
err = IPCOM_SUCCESS;
|
|
|
|
cleanup:
|
|
|
|
if (msg != IP_NULL)
|
|
(void)ipradius_access_request_pkt_free(msg);
|
|
|
|
return err;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_radius_receive
|
|
*===========================================================================
|
|
*/
|
|
IP_GLOBAL Ipike_error
|
|
ipike_radius_receive(
|
|
Ip_fd fd,
|
|
Ipike_value* eap,
|
|
Ip_u8* unique_id,
|
|
Ip_u32* unique_id_len,
|
|
Ipradius_header** last_msg)
|
|
{
|
|
Ipike_error err = IPIKE_FAILED;
|
|
Ipradius_header* msg = IP_NULL;
|
|
Ipike_radius_server* ctx = IP_NULL;
|
|
|
|
if ((ctx = ipike_radius_hash_get(fd, IP_NULL)) == IP_NULL)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipike_radius_receive - unknown radius server");
|
|
goto cleanup;
|
|
}
|
|
|
|
msg = ipike_radius_alloc_receive_buffer();
|
|
if (msg==IP_NULL)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipike_radius_receive - mem alloc failed");
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Ipradius_header is followed by data payload, accessing data seems over access */
|
|
/* coverity[tainted_data] */
|
|
if (ipike_radius_receive_message(fd, msg))
|
|
{
|
|
IPCOM_LOG0(ERR, "ipike_radius_receive - receive failed");
|
|
goto cleanup;
|
|
}
|
|
if (msg->code != IPRADIUS_CODE_Access_Accept &&
|
|
msg->code != IPRADIUS_CODE_Access_Reject &&
|
|
msg->code != IPRADIUS_CODE_Access_Challenge)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipike_radius_receive - unknown message");
|
|
goto cleanup;
|
|
}
|
|
IPCOM_LOG0(DEBUG2, "ipike_radius_receive - message received");
|
|
/* Ipradius_header is followed by data payload, accessing data seems over access */
|
|
/* coverity[tainted_data] */
|
|
if (ipradius_attr_verify_message_auth(msg,
|
|
ctx->server.shared_secret,
|
|
ctx->server.shared_secret_len))
|
|
{
|
|
IPCOM_LOG0(ERR, "ipike_radius_receive - failed to verify message authenticator");
|
|
goto cleanup;
|
|
}
|
|
/* Ipradius_header is followed by data payload, accessing data seems over access */
|
|
/* coverity[tainted_data] */
|
|
if (ipike_radius_msg_extract_eap(msg, eap) != IPIKE_SUCCESS)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipike_radius_receive - failed to extract eap message");
|
|
goto cleanup;
|
|
}
|
|
if (ipike_radius_msg_extract_unique_id(msg,
|
|
unique_id,
|
|
unique_id_len) != IPIKE_SUCCESS)
|
|
{
|
|
if (!ipike_radius_hash_unique_id_get(msg->id, unique_id, unique_id_len))
|
|
{
|
|
IPCOM_LOG0(ERR, "ipike_radius_receive - failed to extract cookies");
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
err = IPIKE_SUCCESS;
|
|
*last_msg = msg;
|
|
msg = IP_NULL;
|
|
|
|
cleanup:
|
|
|
|
if (msg != IP_NULL)
|
|
ipike_radius_free_receive_buffer(&msg);
|
|
|
|
return err;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_radius_process_pkt
|
|
*===========================================================================
|
|
*/
|
|
IP_GLOBAL Ipike_error
|
|
ipike_radius_process_pkt(
|
|
Ipike_radius_ctx* ctx,
|
|
Ip_u8* unique_id,
|
|
Ip_u32 unique_id_len,
|
|
Ipeap_pkt* pkt_in,
|
|
Ipeap_pkt* pkt_out)
|
|
{
|
|
Ipike_error err = IPEAP_FAILURE;
|
|
Ipeap_pkt_hdr* hdr = IP_NULL;
|
|
Ip_u8* data = IP_NULL;
|
|
Ip_u32 len = 0;
|
|
|
|
if ((pkt_in->end - pkt_in->start) == 0)
|
|
{
|
|
if (pkt_out->max_len >= (int)EAP_START_LEN)
|
|
{
|
|
pkt_out->end = pkt_out->start + (int)EAP_START_LEN;
|
|
ipcom_memcpy(pkt_out->data+ pkt_out->start, EAP_START, EAP_START_LEN);
|
|
err = IPEAP_PENDING_SEND_OUT_PKT;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Get identity from packet */
|
|
hdr = (Ipeap_pkt_hdr*)&pkt_in->data[pkt_in->start];
|
|
data = (Ip_u8*)hdr + IPIKE_EAP_HEADER_LEN;
|
|
if ((hdr->code == IPEAP_PKT_RESPONSE) &&
|
|
(ip_htons(hdr->len) > IPIKE_EAP_HEADER_LEN) &&
|
|
(hdr->type == IPEAP_TYPE_IDENTITY))
|
|
{
|
|
len = ip_htons(hdr->len) - IPIKE_EAP_HEADER_LEN;
|
|
if (len < sizeof(ctx->identity))
|
|
{
|
|
ipcom_memcpy(ctx->identity, data, len);
|
|
ctx->identity[len] = 0;
|
|
IPCOM_LOG1(DEBUG2, "Identity: '%s'", ctx->identity);
|
|
}
|
|
}
|
|
/* Send to radius server */
|
|
if (ipike_radius_send(ctx,
|
|
unique_id,
|
|
unique_id_len,
|
|
pkt_in) != IPCOM_SUCCESS)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipike_radius_process_pkt: Failed to send to radius server");
|
|
}
|
|
else
|
|
{
|
|
err = IPCOM_SUCCESS;
|
|
}
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_radius_process_dispatcher_event
|
|
*===========================================================================
|
|
*/
|
|
IP_GLOBAL void
|
|
ipike_radius_process_dispatcher_event(Ip_fd fd)
|
|
{
|
|
Ip_u8 unique_id[74] = {0};
|
|
Ip_u32 unique_id_len = sizeof(unique_id);
|
|
Ipike_isakmp_exchange* ie = IP_NULL;
|
|
Ipike_value eap = {0};
|
|
Ipradius_header* msg = IP_NULL;
|
|
|
|
if (ipike_radius_receive(
|
|
fd,
|
|
&eap,
|
|
unique_id,
|
|
&unique_id_len,
|
|
&msg) != IPIKE_SUCCESS)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipike_radius_process_dispatcher_event: Failed to get radius message");
|
|
ipike_dispatch_remove_radius_socket(fd);
|
|
}
|
|
else if ((ie = ipike_isakmp_get_exchange_from_unique_id(
|
|
unique_id,
|
|
unique_id_len)) == IP_NULL)
|
|
{
|
|
ipike_radius_free_receive_buffer(&msg);
|
|
IPCOM_LOG0(ERR, "ipike_radius_process_dispatcher_event: Failed to get exchange");
|
|
}
|
|
else
|
|
{
|
|
/* Set last radius buffer, so it may be used later */
|
|
ipike_radius_free_receive_buffer(&ie->ike_sa->v2.radius_ctx->last_msg);
|
|
ie->ike_sa->v2.radius_ctx->last_msg = msg;
|
|
/* Set EAP message */
|
|
ipcom_free(ie->ike_sa->v2.eap.value);
|
|
ie->ike_sa->v2.eap.value = eap.value;
|
|
ie->ike_sa->v2.eap.len = eap.len;
|
|
/* Continue processing of exchange */
|
|
ipike_isakmp_process_message_continue(ie);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_radius_extract_key
|
|
*===========================================================================
|
|
*/
|
|
IP_GLOBAL void
|
|
ipike_radius_extract_key(Ipike_radius_ctx* ctx, Ip_u8* key, Ip_u32* key_size)
|
|
{
|
|
Ip_u8* key_recv = IP_NULL;
|
|
Ip_u8* key_send = IP_NULL;
|
|
Ip_u32 key_len = 0;
|
|
Ip_u32 max_len = 0;
|
|
Ip_u32 len = 0;
|
|
|
|
max_len = *key_size;
|
|
*key_size = 0;
|
|
if (ipike_radius_get_microsoft_keys(ctx,
|
|
&key_recv,
|
|
&key_send,
|
|
&key_len) == IPIKE_SUCCESS)
|
|
{
|
|
/* Append recv key */
|
|
if ((key_len + *key_size) > max_len)
|
|
len = max_len - *key_size;
|
|
else
|
|
len = key_len;
|
|
ipcom_memcpy(key + *key_size, key_recv, len);
|
|
*key_size += len;
|
|
/* Append send key */
|
|
if ((key_len + *key_size) > max_len)
|
|
len = max_len - *key_size;
|
|
else
|
|
len = key_len;
|
|
|
|
ipcom_memcpy(key + *key_size, key_send, len);
|
|
*key_size += len;
|
|
|
|
/* free allocated keys */
|
|
ipcom_free(key_recv);
|
|
ipcom_free(key_send);
|
|
|
|
key_recv = IP_NULL;
|
|
key_send = IP_NULL;
|
|
}
|
|
/* Try to get cisco session key (LEAP) */
|
|
else if ( ipike_radius_get_cisco_session_key(ctx, &key_recv, &key_len) == IPIKE_SUCCESS )
|
|
{
|
|
if ( key_len > max_len )
|
|
key_len = max_len;
|
|
|
|
/* Copy key */
|
|
ipcom_memcpy(key, key_recv, key_len);
|
|
|
|
*key_size = key_len;
|
|
|
|
/* Free allocated bytes */
|
|
ipcom_free(key_recv);
|
|
key_recv = IP_NULL;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_radius_startup
|
|
*===========================================================================
|
|
*/
|
|
IP_GLOBAL Ipike_error
|
|
ipike_radius_startup(void)
|
|
{
|
|
Ipradius_open conf;
|
|
|
|
if (ipike.radius.initialized)
|
|
ipike_radius_shutdown();
|
|
ipike.radius.initialized = IP_TRUE;
|
|
|
|
/* Initialize hash table */
|
|
ipike.radius.hash_table = ipcom_hash_new(
|
|
(Ipcom_hash_obj_func) ipike_radius_hash_obj,
|
|
(Ipcom_hash_key_func) ipike_radius_hash_key,
|
|
(Ipcom_hash_cmp_func) ipike_radius_hash_cmp);
|
|
if (ipike.radius.hash_table == IP_NULL)
|
|
{
|
|
IP_PANIC();
|
|
return IPIKE_OUT_OF_MEMORY;
|
|
}
|
|
|
|
/* Initialize hash table */
|
|
ipike.radius.unique_id_table = ipcom_hash_new(
|
|
(Ipcom_hash_obj_func) ipike_radius_hash_unique_id_obj,
|
|
(Ipcom_hash_key_func) ipike_radius_hash_unique_id_key,
|
|
(Ipcom_hash_cmp_func) ipike_radius_hash_unique_id_cmp);
|
|
if (ipike.radius.unique_id_table == IP_NULL)
|
|
{
|
|
IP_PANIC();
|
|
return IPIKE_OUT_OF_MEMORY;
|
|
}
|
|
|
|
/* Open IPRADIUS */
|
|
ipcom_strcpy(conf.ipradius_NAS_Identifier,"Interpeak IKE");
|
|
conf.ipradius_NAS_Identifier_size = (Ip_u32) ipcom_strlen(conf.ipradius_NAS_Identifier);
|
|
conf.access_request_attribute_size = IPIKE_EAP_MAX_BUF;
|
|
conf.accounting_request_attribute_size = IPRADIUS_accounting_request_attribute_size;
|
|
conf.conf[0] = IP_TAG_END;
|
|
if (ipradius_open(&conf) != IPCOM_SUCCESS)
|
|
{
|
|
/* Ignore failure, since no closing */
|
|
IPCOM_LOG0(DEBUG2, "ipike_radius_startup: Failed to open radius (ignored)");
|
|
}
|
|
|
|
return IPIKE_SUCCESS;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_radius_shutdown
|
|
*===========================================================================
|
|
*/
|
|
IP_GLOBAL void
|
|
ipike_radius_shutdown(void)
|
|
{
|
|
if (!ipike.radius.initialized)
|
|
return;
|
|
|
|
/* Close hash table */
|
|
if (ipike.radius.hash_table != IP_NULL)
|
|
{
|
|
ipcom_hash_for_each(ipike.radius.hash_table,
|
|
(void(*)(void *, void *)) ipike_radius_hash_free, IP_NULL);
|
|
ipcom_hash_delete(ipike.radius.hash_table);
|
|
ipike.radius.hash_table = IP_NULL;
|
|
}
|
|
if (ipike.radius.unique_id_table != IP_NULL)
|
|
{
|
|
ipcom_hash_for_each(ipike.radius.unique_id_table,
|
|
(void(*)(void *, void *)) ipike_radius_hash_unique_id_free, IP_NULL);
|
|
ipcom_hash_delete(ipike.radius.unique_id_table);
|
|
ipike.radius.unique_id_table = IP_NULL;
|
|
}
|
|
|
|
ipike.radius.initialized = IP_FALSE;
|
|
|
|
return;
|
|
}
|
|
|
|
#else
|
|
|
|
int ipike_radius_empty_file = 0;
|
|
|
|
#endif /* IPIKE_USE_RADIUS */
|
|
|
|
/*
|
|
****************************************************************************
|
|
* END OF FILE
|
|
****************************************************************************
|
|
*/
|
|
|