1830 lines
60 KiB
C
1830 lines
60 KiB
C
/* dot1xAuthSvrLib.c - IEEE 802.1X authentication server interface */
|
|
|
|
/* Copyright 2002-2008, 2010,2012,2014,2016 Wind River Systems, Inc. */
|
|
#include "copyright_wrs.h"
|
|
|
|
/*
|
|
modification history
|
|
--------------------
|
|
02s,13jun16,chm Fix for VXW6-85382.
|
|
02r,18sep14,lzq Fix Coverity issue.
|
|
02q,05may12,zan Fix for WIND00340951. Check ip address before remove radius server.
|
|
02p,12jan12,zan Fix for WIND00322248
|
|
02o,11nov10,hfc fix defect WIND00232734
|
|
02n,28may10,hfc EAP-Update ,req:WIND00198105
|
|
02m,04apr10,kxr Coverity changes
|
|
02l,04mar10,kxr Replace CCI calls with openssl
|
|
02k,07oct08,dwb Fix for WIND00136166
|
|
02j,10sep08,dwb Restructuring socket code to adjust for ipcom_socket changes.
|
|
02i,28aug08,dwb Reverting to real ipcom sockets
|
|
02h,19mar08,kjn Remove 2:nd argument from ipcom_pkt_free()
|
|
02g,05feb08,dwb Changes for SMP
|
|
02f,29oct07,dwb Fix for WIND00109453
|
|
02e,23oct07,mry Fixed dual stack support.
|
|
02d,27sep07,mry Add fix for WIND00105532
|
|
02c,02mar07,dwb Added dot1xAuthSvrLibProcessVendorMsg to properly decrypt
|
|
vendor specific keys from RADIUS access accept packet,
|
|
See defect WIND00089466 or Fix WIND00089467
|
|
02b,06feb07,dwb Fix for WIND00085529
|
|
02a,04dec06,dwb Fixed to walk chained eap segments and various cleanup work
|
|
01z,31oct06,dwb Changes for port to ipnet radius client
|
|
01y,06arp06,rb Fix for SPR 119970 - Authenticator data abort
|
|
01x,05mar06,tat removing include of rwos.h
|
|
01x,17mar06,rb Fix for SPR 119191 - Coverity errors
|
|
01w,17jun05,eja Fix Trace Bug B0562
|
|
01v,12may05,alm Updating comments
|
|
01u,10may05,eja Remove dead code
|
|
01t,20apr05,eja Make changes for updated dot1xMemFree macro
|
|
01s,30mar05,eja Fix DIAB warnings and SWAT bugs
|
|
01r,14feb05,eja remove call to clientIpAddrGet(radius_configuration_text) in
|
|
init() method to get around mem part alloc error
|
|
01q,03jan05,eja Fix SWAT warnings
|
|
01p,30jun04,dxb Included string.h to fix compile dependency error on Base
|
|
MSP.
|
|
01o,05dec03,dxb Fix for SPR 92134 - RADIUS Server Failover
|
|
01n,04sep03,ggg Remove warnings for diab MIPS and SH
|
|
01m,05may03,ggg Update documentation
|
|
01l,22apr03,ggg Remove Cisco AVP string check
|
|
01k,21apr03,ggg File name, location change
|
|
01j,14apr03,ggg Fix non-key VSA handling (SPR 87624)
|
|
01i,30jul02,ggg Update DBG_PRINT for Diab and Gnu varargs handling
|
|
01h,08jul02,ggg Undef debug by default
|
|
01g,08jul02,ggg Correct file name in comment
|
|
01f,21jun02,myz removed the code referencing RADIUS private header files.
|
|
01e,10jun02,myz reworked the request error handling
|
|
01d,06jun02,myz removed potential compiler warnings
|
|
01c,31may02,myz modified dot1xRadiusCfg() function
|
|
01b,30may02,myz change dot1xAuthServersRemoved to dot1xAuthServersRemove
|
|
01a,05Feb02,myz written
|
|
*/
|
|
|
|
/*
|
|
DESCRIPTION
|
|
|
|
This library provides the routines for delivering the EAP packet to
|
|
the authentication server and the mechanism for receiving the results
|
|
from it. The application must provide the required information in one of
|
|
the arguments when calling the API to deliver the EAP packet. The results
|
|
from the RADIUS server are sent back through the user-specified callback.
|
|
*/
|
|
|
|
#include "vxWorks.h"
|
|
#include "stdio.h"
|
|
#include "string.h"
|
|
#include "sllLib.h"
|
|
#include "netinet/in.h"
|
|
#include "taskLib.h"
|
|
|
|
#define __EAP__
|
|
|
|
#define bool BOOL
|
|
typedef unsigned char BYTE;
|
|
|
|
#include <ipcom_os.h>
|
|
#include <ipcom_config.h>
|
|
#include <ipradius.h>
|
|
#include <ipcom_clib.h>
|
|
#include <ipcom_syslog.h>
|
|
#include <ipcom_sock.h>
|
|
|
|
#include "dot1xAuthSvrLib.h"
|
|
#include "dot1xKeyLib.h"
|
|
#include <dot1xMemPartLib.h>
|
|
#include <dot1xDebugLib.h>
|
|
#include <vsbConfig.h>
|
|
|
|
/* eliminate compiler warning re: redefine of NULL */
|
|
#undef IPCRYPTO_USE_TYPE_MAPPING
|
|
#include <openssl/md5.h>
|
|
|
|
/* Make sure we have at least one type of network support */
|
|
#if !defined(INET) && !defined(INET6)
|
|
#error "Must define at least IPCOM_USE_INET or IPCOM_USE_INET6"
|
|
#endif
|
|
|
|
/* EAP Message defines */
|
|
#define DOT1X_AUTH_RADIUS_ATTR_HEADER_LEN 2
|
|
#define DOT1X_AUTH_RADIUS_ATTR_TYPE_IDX 0
|
|
#define DOT1X_AUTH_RADIUS_ATTR_LEN_IDX 1
|
|
#define DOT1X_AUTH_RADIUS_ATTR_DATA_IDX 2
|
|
|
|
/* Logging defines */
|
|
#define DOT1X_LOG_FATAL "(f) " __FILE__ " (s) FATAL (r) "
|
|
#define DOT1X_LOG_ERROR "(f) " __FILE__ " (s) ERROR (r) "
|
|
#define DOT1X_LOG_INFO "(f) " __FILE__ " (s) INFO (r) "
|
|
#define DOT1X_LOG_DEBUG "(f) " __FILE__ " (s) DEBUG (r) "
|
|
|
|
#if defined(_WRS_CONFIG_DOT1X_AS_DEBUG) && !defined(DOT1X_AS_DEBUG)
|
|
#define DOT1X_AS_DEBUG
|
|
#endif
|
|
|
|
#ifdef DOT1X_AS_DEBUG
|
|
|
|
static char *svrRsp[] =
|
|
{
|
|
"Unknown response",
|
|
"access accept code",
|
|
"access reject code",
|
|
"access challenge code",
|
|
"accounting response code",
|
|
"access timeout",
|
|
"access cancelled",
|
|
"accounting timeout",
|
|
"accounting cancelled",
|
|
"accounting request"
|
|
};
|
|
|
|
int dot1xAsDebug = 1;
|
|
#define DBG_PRINT if (dot1xAsDebug) printf
|
|
#else
|
|
|
|
#ifdef __DCC__
|
|
#define DBG_PRINT(...)
|
|
#else /* __GNUC__ (and others?) */
|
|
#define DBG_PRINT(args...)
|
|
#endif /* __DCC__*/
|
|
|
|
#endif /* DOT1X_AS_DEBUG */
|
|
|
|
|
|
/*** Local defines ***/
|
|
|
|
/* This library's initialization state */
|
|
|
|
#define DOT1X_AS_FALSE 0 /* not initialized */
|
|
#define DOT1X_AS_TRUE 1 /* initialized */
|
|
#define DOT1X_AS_IN_PROGRESS 2 /* initialization in progress */
|
|
|
|
/* Server set state */
|
|
#define DOT1X_SS_DONE 0 /* Server info configuration is complete */
|
|
#define DOT1X_SS_IN_PROGRESS 1 /* Server info set is in progress */
|
|
|
|
/* requestMsgNodeGet() retriving type */
|
|
|
|
#define GET_TYPE_FIND 1 /* simply find the node */
|
|
#define GET_TYPE_FIND_AND_REMOVE 2 /* find and remove the node from list */
|
|
|
|
/* RCVD_ATTR structure index definition */
|
|
|
|
#define ATTR_EP_INX 0 /* index to store EAP attribute */
|
|
#define ATTR_SK_INX 1 /* index to store send key */
|
|
#define ATTR_RK_INX 2 /* index to store receive key */
|
|
#define ATTR_ST_INX 3 /* index to store STATE attr */
|
|
#define NUM_OF_ATTRS_SEND 4 /* the total number of attribute can be sent to
|
|
user */
|
|
|
|
/* RADIUS Vendor-Specific attribute defines */
|
|
#define VENDOR_ID_CISCO 9
|
|
#define VENDOR_ID_MICROSOFT 311
|
|
#define VENDOR_TYPE_CISCO_AVPAIR 1
|
|
#define VENDOR_TYPE_MS_MPPE_SEND_KEY 16
|
|
#define VENDOR_TYPE_MS_MPPE_RECV_KEY 17
|
|
|
|
/* RADIUS Vendor-Specific processing defines */
|
|
#define DOT1X_AUTH_MD5_RESULT_LENGTH 16
|
|
#define DOT1X_AUTH_RADIUS_PASSWORD_MODULUS 16
|
|
#define DOT1X_AUTH_RADIUS_SIZE_OF_AUTHENTICATOR 16
|
|
#define DOT1X_AUTH_VENDOR_SALT_LENGTH 2
|
|
#define DOT1X_AUTH_VENDOR_SPECIFIC_ID_LENGTH 4
|
|
#define DOT1X_AUTH_VENDOR_TYPE_LENGTH 1
|
|
#define DOT1X_AUTH_VENDOR_LENGTH 1
|
|
#define DOT1X_AUTH_VENDOR_KEY_LENGTH 1
|
|
|
|
/* Misc. defines */
|
|
#define CFG_GEN_STR_MAX_LEN 0x20 /* max generic config string length*/
|
|
#define CFG_SECRET_STR_MAX_LEN 0x50 /* max secret string length */
|
|
|
|
/* ipradius packet size */
|
|
#define IPRADIUS_PKT_SIZE 1472
|
|
|
|
/* For splitting up eap packets into multiple attributes */
|
|
#define DOT1X_AUTH_MAX_EAP_FRAG_SIZE 253 /* = MAX octets allowed in a RADIUS attribute value field */
|
|
|
|
/*** typedefs ***/
|
|
|
|
/* Request message node for holding info before a reply is received */
|
|
|
|
typedef struct {
|
|
SL_NODE node; /* single link list node */
|
|
UINT32 seqNum; /* requestHandle of radius_server_request() */
|
|
FUNCPTR pUserCallback; /* callback for receiving RADIUS server result*/
|
|
UINT32 reqHandle; /* User message handle */
|
|
} AUTH_REQUEST_MSG_NODE;
|
|
|
|
/* The data structure to control interaction with the RADIUS server */
|
|
|
|
typedef struct {
|
|
UINT32 seqNum; /* The last requestHandle */
|
|
UINT32 nasIpAddr; /* Client IP address in network byte order */
|
|
SEM_ID semId; /* semaphore for controlling the shared data */
|
|
BOOL serverError; /* RADIUS server error indicator */
|
|
char *pkt; /* RADIUS packet */
|
|
Ip_fd radius_fd; /* RADIUS IPv4 socket fd */
|
|
/* RADIUS server address */
|
|
/* Server address structure */
|
|
union Ip_sockaddr_union
|
|
radius_server_addr[DOT1X_AUTH_MAX_RADIUS_SERVERS];
|
|
/* Primary/Secondary server information */
|
|
Ip_u32 radius_server_index[DOT1X_AUTH_MAX_RADIUS_SERVERS];
|
|
/* Cache for server the user specified server information */
|
|
DOT1X_AUTH_RADIUS_SERVER_INFO server_info[DOT1X_AUTH_MAX_RADIUS_SERVERS];
|
|
UINT32 num_radius_servers;
|
|
SL_LIST list; /* request message node list */
|
|
} AUTH_SERVER_ACTION_CONTROL;
|
|
|
|
/* RADIUS initialization configuration */
|
|
|
|
typedef struct {
|
|
UINT8 * pClientIpAddr; /* client IP address */
|
|
UINT8 * pClientPort; /* client UDP port */
|
|
UINT8 * pServerIpAddr; /* additional RADIUS server IP address */
|
|
UINT8 * pServerPort; /* additional RADIUS server UDP port */
|
|
UINT8 * pPrintfEnable; /* print message enable or disable */
|
|
UINT8 * pSecret; /* shared secret */
|
|
} DOT1X_AS_INIT_CONFIG_STR;
|
|
|
|
/* ipcom IPC Message structures */
|
|
#define IPCOM_IPC_MSG_AUTH_INPUT 1001 /* type: Ipcom_ipc_msg_auth */
|
|
|
|
typedef struct Ipcom_msg_auth_struct
|
|
{
|
|
int msgtype;
|
|
int len; /* Length of data[] contents. */
|
|
/* Address from which the data came from */
|
|
union Ip_sockaddr_union addr;
|
|
Ip_u8 data[1]; /* Variable length data holder. */
|
|
}
|
|
Ipcom_msg_auth;
|
|
|
|
/*** local data ***/
|
|
|
|
LOCAL AUTH_SERVER_ACTION_CONTROL authServerCtrl;
|
|
LOCAL int authLibInitDone = DOT1X_AS_FALSE;
|
|
LOCAL int authLibServerSetDone = DOT1X_SS_DONE;
|
|
|
|
/*** globals ***/
|
|
|
|
DOT1X_AS_INIT_CONFIG_STR dot1xAsInitConfigStr = {0,0,0,0,0,0};
|
|
|
|
/* forward declarations */
|
|
LOCAL Ip_err ipradiusRequestNormalReply(int, struct Ipradius_request_struct *,
|
|
Ipradius_header *, Ipradius_header *,Ipradius_server *);
|
|
LOCAL void ipradiusRequestErrorReply(UINT32,UINT32);
|
|
LOCAL Ip_u8 * dot1xAuthSvrLibProcessEAPMsg(Ipradius_header *response_packet,
|
|
Ip_u8 *pAttr, int *dataLen,UINT8 *pBuf);
|
|
LOCAL int vendorAttributeKeyExtract (UINT8 **, int *);
|
|
LOCAL AUTH_REQUEST_MSG_NODE * requestMsgNodeGet (SL_LIST *, UINT32,int);
|
|
LOCAL STATUS dot1xAuthSvrLibProcessVendorMsg(UINT8* pVendorMsg,
|
|
UINT32 pVendorMsgLen, UINT8 *requestAuth, char *secret,
|
|
UINT32 secretLen);
|
|
LOCAL BOOL dot1xAuthSvrLibMd5Decryption (unsigned char *cptr_identifier,
|
|
USHORT identifier_length, unsigned char *p_secret, USHORT secret_length,
|
|
unsigned char *p_data, USHORT data_length);
|
|
LOCAL BOOL dot1xAuthSvrLibMd5Digest ( unsigned char *input, ULONG input_length,
|
|
unsigned char *result, ULONG result_length);
|
|
LOCAL IPCOM_PROCESS(tDot1xRecv);
|
|
|
|
/* externals */
|
|
|
|
/* Public methods */
|
|
|
|
/****************************************************************************
|
|
* dot1xAuthServerLibInit - initialize this authentication server library
|
|
*
|
|
* This routine performs initialization of this library. It checks
|
|
* the RADIUS interface configuration, and initializes the control data
|
|
* structure. This routine is automatically called by the send API
|
|
* dot1xAuthServerEapMsgSend() if it is not invoked previously.
|
|
*
|
|
* RETURNS: OK or ERROR if initialization is not done.
|
|
*/
|
|
STATUS dot1xAuthServerLibInit
|
|
(
|
|
DOT1X_AUTH_RADIUS_SERVER_INFO *serverInfo
|
|
)
|
|
{
|
|
/* Atomically check the initialization flag and update it to
|
|
* in progress if it is not already. This test is safe with repect to
|
|
* both interrupts and accesses of other processors. i.e. SMP situations */
|
|
if(vxAtomicCas(&authLibInitDone,DOT1X_AS_FALSE,DOT1X_AS_IN_PROGRESS) == TRUE)
|
|
{
|
|
|
|
/* No server info, so were done */
|
|
if ( serverInfo == NULL )
|
|
{
|
|
authLibInitDone = DOT1X_AS_FALSE;
|
|
return ERROR;
|
|
}
|
|
memset(&authServerCtrl,0,sizeof(AUTH_SERVER_ACTION_CONTROL));
|
|
authServerCtrl.radius_fd = IP_INVALID_SOCKET;
|
|
|
|
if ((authServerCtrl.semId = semBCreate(SEM_Q_PRIORITY, SEM_FULL))
|
|
==NULL)
|
|
{
|
|
DBG_PRINT("RadiusIF: Error: Can't create the"
|
|
" semaphore\n");
|
|
|
|
authLibInitDone = DOT1X_AS_FALSE;
|
|
return ERROR;
|
|
}
|
|
|
|
authServerCtrl.serverError = FALSE;
|
|
|
|
#ifdef DOT1X_AS_DEBUG
|
|
{
|
|
UINT32 ipAddr = ntohl(authServerCtrl.nasIpAddr);
|
|
DBG_PRINT("RadiusIF: NAS IP Address:%d.%d.%d.%d\n", ipAddr >> 24,
|
|
(ipAddr >> 16) & 0xFF, (ipAddr >> 8) & 0xFF, ipAddr & 0xFF);
|
|
}
|
|
#endif
|
|
|
|
authServerCtrl.seqNum = 1;
|
|
sllInit(&authServerCtrl.list);
|
|
|
|
authServerCtrl.pkt = malloc(IPRADIUS_PKT_SIZE);
|
|
if (authServerCtrl.pkt == IP_NULL)
|
|
{
|
|
DBG_PRINT("Failed to create packet data buffer\n");
|
|
authLibInitDone = DOT1X_AS_FALSE;
|
|
return ERROR;
|
|
}
|
|
|
|
authLibInitDone = DOT1X_AS_TRUE;
|
|
|
|
/* Set the server info. Note that this function configures
|
|
* the socket to communitcate with the RADIUS server */
|
|
if( dot1xAuthServerLibRadiusServerSet(serverInfo) == ERROR )
|
|
return (ERROR);
|
|
|
|
return OK;
|
|
}
|
|
|
|
else
|
|
{
|
|
if (authLibInitDone == DOT1X_AS_IN_PROGRESS) {
|
|
return ERROR;
|
|
}
|
|
}
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
/****************************************************************************
|
|
* dot1xAuthServerLibRadiusServerSet - Set the RADIUS server information
|
|
*
|
|
* This routine sets the RADIUS server information and configures the socket
|
|
* to communicate with the RADIUS server
|
|
*
|
|
* RETURNS: OK or ERROR if configuration was not completed.
|
|
*/
|
|
STATUS dot1xAuthServerLibRadiusServerSet
|
|
(
|
|
DOT1X_AUTH_RADIUS_SERVER_INFO *serverInfo
|
|
)
|
|
{
|
|
Ipradius_server server;
|
|
UINT8 svrIdx = 0;
|
|
union Ip_sockaddr_union addr;
|
|
Ip_err retval;
|
|
|
|
DBG_PRINT("authLibServerSetDone: %d\n",authLibServerSetDone);
|
|
|
|
/* Return is no server info was specified */
|
|
if(serverInfo == NULL)
|
|
return (OK);
|
|
|
|
/* The user should not be calling this function unless
|
|
* the server has been initialized */
|
|
if ( authLibInitDone != DOT1X_AS_TRUE )
|
|
return (ERROR);
|
|
|
|
/* Atomically check the configuration status flag and update it to
|
|
* in progress if it is not already. This test is safe with repect to
|
|
* both interrupts and accesses of other processors. i.e. SMP situations */
|
|
if(vxAtomicCas(&authLibServerSetDone,DOT1X_SS_DONE,DOT1X_SS_IN_PROGRESS) == TRUE)
|
|
{
|
|
|
|
authServerCtrl.num_radius_servers = 0;
|
|
|
|
if( authServerCtrl.radius_fd != IP_INVALID_SOCKET)
|
|
ipcom_socketclose(authServerCtrl.radius_fd);
|
|
|
|
/* Set the server address info for the primary and secondary servers,
|
|
* and add the servers to ipradius' server list */
|
|
while ( svrIdx < DOT1X_AUTH_MAX_RADIUS_SERVERS )
|
|
{
|
|
/* Copy the server info so we have it for later */
|
|
authServerCtrl.server_info[svrIdx].timeout =
|
|
serverInfo[svrIdx].timeout;
|
|
authServerCtrl.server_info[svrIdx].retry_count =
|
|
serverInfo[svrIdx].retry_count;
|
|
authServerCtrl.server_info[svrIdx].domain =
|
|
serverInfo[svrIdx].domain;
|
|
authServerCtrl.server_info[svrIdx].port =
|
|
serverInfo[svrIdx].port;
|
|
|
|
|
|
/* Resolve the RADIUS server IPv4/IPv6 address */
|
|
if( (serverInfo[svrIdx].domain == IP_AF_INET) ||
|
|
(serverInfo[svrIdx].domain == IP_AF_INET6) )
|
|
{
|
|
retval = ipcom_getsockaddrbyaddrname(serverInfo[svrIdx].domain,
|
|
IP_FALSE /* use DNS */, serverInfo[svrIdx].server_addr,
|
|
&authServerCtrl.radius_server_addr[svrIdx].sa);
|
|
if (retval)
|
|
{
|
|
|
|
DBG_PRINT("dot1xAuthServerLibInit: invalid"
|
|
"server address=%s' from domain = %d, err = %d\n",
|
|
serverInfo[svrIdx].server_addr,serverInfo[svrIdx].domain,
|
|
retval);
|
|
authLibServerSetDone = DOT1X_SS_DONE;
|
|
return OK; /* Do not halt system, just disable RADIUS login. */
|
|
}
|
|
}
|
|
|
|
else
|
|
{
|
|
DBG_PRINT("dot1xAuthServerLibInit: "
|
|
"invalid domain: %d\n",serverInfo[svrIdx].domain);
|
|
}
|
|
|
|
/* Check if we need to remove the server from the ipradius
|
|
* server list */
|
|
if( authServerCtrl.radius_server_index[svrIdx] != 0 )
|
|
{
|
|
DBG_PRINT("Removing server from index: %d\n",
|
|
authServerCtrl.radius_server_index[svrIdx]);
|
|
server.index = authServerCtrl.radius_server_index[svrIdx];
|
|
if( ipradius_server_remove(&server) != IPCOM_SUCCESS )
|
|
{
|
|
authLibServerSetDone = DOT1X_SS_DONE;
|
|
return (ERROR);
|
|
}
|
|
authServerCtrl.radius_server_index[svrIdx] = 0;
|
|
}
|
|
|
|
DBG_PRINT("dot1xAuthServerLibInit:\n"
|
|
"Configuring server info for server %d:\n"
|
|
"Domain: %d\nAddress: %s\nPort: %d\nRetry Count: %d\n"
|
|
"Timeout: %d\n",svrIdx,serverInfo[svrIdx].domain,
|
|
serverInfo[svrIdx].server_addr,serverInfo[svrIdx].port,
|
|
serverInfo[svrIdx].retry_count,serverInfo[svrIdx].timeout);
|
|
|
|
|
|
|
|
/* Cache radius server port. */
|
|
authServerCtrl.radius_server_addr[svrIdx].sin.sin_port =
|
|
ip_htons(serverInfo[svrIdx].port);
|
|
|
|
authServerCtrl.num_radius_servers++;
|
|
|
|
/* Check to if there are less than the number default servers */
|
|
if( serverInfo[svrIdx].endTag == DOT1X_AUTH_TAG_LAST_SERVER)
|
|
break;
|
|
|
|
svrIdx++;
|
|
|
|
} /* while */
|
|
|
|
#ifdef INET
|
|
/* Get RADIUS client socket if one has not already been created */
|
|
if( serverInfo[0].domain == IP_AF_INET )
|
|
{
|
|
authServerCtrl.radius_fd =
|
|
ipcom_socket(serverInfo[0].domain, IP_SOCK_DGRAM, 0);
|
|
if (authServerCtrl.radius_fd == IP_INVALID_SOCKET)
|
|
{
|
|
DBG_PRINT("dot1xAuthServerLibInit: ipcom_socket() failed,"
|
|
" errno=%d", ipcom_errno);
|
|
goto fail;
|
|
}
|
|
|
|
/* Bind to the socket */
|
|
memset(&addr, 0, sizeof(addr));
|
|
addr.sin.sin_len = authServerCtrl.radius_server_addr[0].sa.sa_len;
|
|
addr.sin.sin_family = (Ip_sa_family_t)serverInfo[0].domain;
|
|
if (ipcom_bind(authServerCtrl.radius_fd, &addr.sa,
|
|
authServerCtrl.radius_server_addr[0].sa.sa_len) == IP_SOCKERR)
|
|
{
|
|
DBG_PRINT("dot1xAuthServerLibInit: ipcom_bind() failed"
|
|
" errno=%d", ipcom_errno);
|
|
goto fail;
|
|
}
|
|
|
|
} else
|
|
#endif
|
|
#ifdef INET6
|
|
if ( serverInfo[0].domain == IP_AF_INET6 )
|
|
{
|
|
authServerCtrl.radius_fd =
|
|
ipcom_socket(serverInfo[0].domain, IP_SOCK_DGRAM, 0);
|
|
if (authServerCtrl.radius_fd == IP_INVALID_SOCKET)
|
|
{
|
|
DBG_PRINT("dot1xAuthServerLibInit: ipcom_socket() failed"
|
|
" errno=%d", ipcom_errno);
|
|
goto fail;
|
|
}
|
|
|
|
/* Bind to the socket */
|
|
memset(&addr, 0, sizeof(addr));
|
|
addr.sin6.sin6_port =authServerCtrl.radius_server_addr[0].sin.sin_port;
|
|
addr.sin6.sin6_len = authServerCtrl.radius_server_addr[0].sa.sa_len;
|
|
addr.sin6.sin6_family = (Ip_sa_family_t)serverInfo[0].domain;
|
|
if (ipcom_bind(authServerCtrl.radius_fd, &addr.sa,
|
|
authServerCtrl.radius_server_addr[0].sa.sa_len) == IP_SOCKERR)
|
|
{
|
|
DBG_PRINT("dot1xAuthServerLibInit: ipcom_bind() failed"
|
|
" errno=%d", ipcom_errno);
|
|
goto fail;
|
|
}
|
|
} else
|
|
#endif
|
|
DBG_PRINT("dot1xAuthServerLibRadiusServerSet: Invalid domain %d\n",
|
|
serverInfo[0].domain);
|
|
|
|
for( svrIdx = 0; svrIdx < authServerCtrl.num_radius_servers; svrIdx++)
|
|
{
|
|
/* Add IPCOM AUTH RADIUS server. */
|
|
DBG_PRINT("dot1xAuthServerLibRadiusServerSet: adding servers to"
|
|
" radius server list\n");
|
|
|
|
server.index = 0; /* let ipradius choose server index. */
|
|
memcpy(&server.server_addr,&authServerCtrl.radius_server_addr[svrIdx],
|
|
sizeof(union Ip_sockaddr_union));
|
|
|
|
server.server_type = IPRADIUS_ACCESS_SERVER;
|
|
server.pollnum = serverInfo[svrIdx].retry_count;
|
|
server.pollsec = serverInfo[svrIdx].timeout;
|
|
|
|
if (strlen(serverInfo[svrIdx].shared_secret) < IPRADIUS_MAX_SECRET_SIZE)
|
|
{
|
|
strncpy((char *)server.shared_secret, serverInfo[svrIdx].shared_secret, IPRADIUS_MAX_SECRET_SIZE);
|
|
server.shared_secret_len = strlen((char *)server.shared_secret);
|
|
}
|
|
else
|
|
{
|
|
DBG_PRINT("dot1xAuthServerLibRadiusServerSet: Shared secret too long, length must be < %d\n",
|
|
IPRADIUS_MAX_SECRET_SIZE);
|
|
goto fail;
|
|
}
|
|
|
|
retval = ipradius_server_add(&server);
|
|
if (retval != IPCOM_SUCCESS)
|
|
{
|
|
DBG_PRINT("dot1xAuthServerLibInit: "
|
|
"ipradius_server_add() failed, err=%ld\n", retval);
|
|
goto fail;
|
|
}
|
|
|
|
/* Save index so we can easily remove it from the
|
|
* ipradius server list later */
|
|
authServerCtrl.radius_server_index[svrIdx] = server.index;
|
|
|
|
DBG_PRINT( "dot1xAuthServerLibRadiusServerSet: "
|
|
" Added RADIUS server at index %d\n",
|
|
authServerCtrl.radius_server_index[svrIdx]);
|
|
}
|
|
|
|
authLibServerSetDone = DOT1X_SS_DONE;
|
|
|
|
/* Create the radius receive task */
|
|
if( ipcom_proc_acreate("tDot1xRecv", tDot1xRecv, IP_NULL, IP_NULL) != IPCOM_SUCCESS )
|
|
goto fail;
|
|
|
|
return (OK);
|
|
}
|
|
else
|
|
{
|
|
return (ERROR);
|
|
}
|
|
|
|
fail:
|
|
if (authServerCtrl.radius_fd != IP_INVALID_SOCKET)
|
|
ipcom_socketclose(authServerCtrl.radius_fd);
|
|
|
|
authServerCtrl.radius_fd = IP_INVALID_SOCKET;
|
|
authLibServerSetDone = DOT1X_SS_DONE;
|
|
|
|
return ERROR;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* dot1xAuthServerLibShutDown - shut down the authentication server library
|
|
*
|
|
* This routine releases the resources used by this library and resets it to
|
|
* the uninitialized state. It provides two types of shutdown: normal or
|
|
* forced. In the normal shutdown, if there are outstanding requests, the
|
|
* shutdown will not be performed. In the latter case, the shutdown is performed
|
|
* regardless of outstanding requests.
|
|
*
|
|
* RETURNS: OK or ERROR if shutdown is not performed.
|
|
*/
|
|
STATUS dot1xAuthServerLibShutDown
|
|
(
|
|
int flag /* 0 controlled shutdown, 1 forced shutdown */
|
|
)
|
|
{
|
|
AUTH_REQUEST_MSG_NODE * pMsgNode;
|
|
Ipradius_server server;
|
|
UINT8 svrIdx;
|
|
|
|
/* Atomically check the initialization flag and update it to
|
|
* in progress if it is not already. This test is safe with repect to
|
|
* both interrupts and accesses of other processors. i.e. SMP situations */
|
|
if(vxAtomicCas(&authLibInitDone,DOT1X_AS_TRUE,DOT1X_AS_IN_PROGRESS) == TRUE)
|
|
{
|
|
if (flag == DOT1X_AS_NORMAL_SHUTDOWN)
|
|
{
|
|
/* if there is outstanding requests, don't do it */
|
|
|
|
if (SLL_FIRST(&authServerCtrl.list))
|
|
{
|
|
authLibInitDone = DOT1X_AS_TRUE;
|
|
return ERROR;
|
|
}
|
|
}
|
|
|
|
/* free any allocated data structures */
|
|
while ( (pMsgNode = (AUTH_REQUEST_MSG_NODE *)
|
|
sllGet(&authServerCtrl.list)) != NULL )
|
|
{
|
|
DOT1X_FREE(pMsgNode);
|
|
}
|
|
|
|
/* Remove the servers from the ipradius server list */
|
|
for( svrIdx = 0; svrIdx < DOT1X_AUTH_MAX_RADIUS_SERVERS; svrIdx++)
|
|
{
|
|
|
|
if(authServerCtrl.radius_server_index[svrIdx] == 0)
|
|
continue;
|
|
|
|
server.index = authServerCtrl.radius_server_index[svrIdx];
|
|
DBG_PRINT("Removing server at index: %d\n",server.index);
|
|
if( ipradius_server_remove(&server)
|
|
!= IPCOM_SUCCESS )
|
|
{
|
|
/* This should never happend */
|
|
DBG_PRINT("dot1xAuthServerLibShutDown: "
|
|
"Failed to remove the server from ipradius");
|
|
return (ERROR);
|
|
}
|
|
}
|
|
|
|
semDelete(authServerCtrl.semId);
|
|
|
|
/* Free the RADIUS packet buffer */
|
|
if (authServerCtrl.pkt != IP_NULL)
|
|
free(authServerCtrl.pkt);
|
|
|
|
/* Close the radius socket */
|
|
if( authServerCtrl.radius_fd != IP_INVALID_SOCKET)
|
|
ipcom_socketclose(authServerCtrl.radius_fd);
|
|
|
|
bzero((char *)&authServerCtrl,sizeof(AUTH_SERVER_ACTION_CONTROL));
|
|
|
|
authLibInitDone = DOT1X_AS_FALSE;
|
|
}
|
|
else
|
|
{
|
|
if (authLibInitDone == DOT1X_AS_IN_PROGRESS)
|
|
return ERROR;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
/* Local methods */
|
|
|
|
/*****************************************************************************
|
|
* dot1xAuthServerEapMsgSend - send the EAP message to the authentication server
|
|
*
|
|
* This routine delivers the EAP packet to the authentication server for the
|
|
* application. Parameter pInfo points to a DOT1X_AS_REQUIRED_INFO data
|
|
* structure which the user must provide. Upon receiving the reply from the
|
|
* RADIUS server, the result is delivered back to the application through the
|
|
* callback specified in the pInfo.
|
|
*
|
|
* RETURNS: OK or ERROR if packet is not sent to the RADIUS interface.
|
|
*
|
|
* NOMANUAL
|
|
*/
|
|
STATUS dot1xAuthServerEapMsgSend
|
|
(
|
|
UINT32 msgHandle, /* user specified handle */
|
|
UINT8 * pEap, /* EAP packet */
|
|
int len, /* length of EAP packet */
|
|
DOT1X_AS_REQUIRED_INFO * pInfo /* required User information */
|
|
)
|
|
{
|
|
Ipradius_header* msg = IP_NULL;
|
|
Ipradius_request request = {0};
|
|
int retval;
|
|
Ip_u8 errattr = 0;
|
|
AUTH_REQUEST_MSG_NODE * pNode;
|
|
int i;
|
|
UINT8 svrIdx = 0;
|
|
char strHolder[DOT1X_AS_STATION_ID_LEN*2 + 1];
|
|
UINT16 sizeRemaining;
|
|
UINT8 eapFragLen;
|
|
|
|
/* The user should not be calling this function unless
|
|
* the server has been initialized */
|
|
if ( (authLibInitDone == DOT1X_AS_FALSE) ||
|
|
(authLibInitDone == DOT1X_AS_IN_PROGRESS) )
|
|
return (ERROR);
|
|
|
|
/* required user parameter check */
|
|
if(pInfo == NULL)
|
|
{
|
|
return(ERROR);
|
|
}
|
|
|
|
/* print user information block */
|
|
DBG_PRINT("radiusIF: usrInfoBlock,usrCallBack: 0x%x, mtu: 0x%x, pId:0x%x",
|
|
(UINT32)pInfo->pUserCallback,pInfo->mtu,(UINT32)pInfo->pId);
|
|
DBG_PRINT("len: 0x%x, pState: 0x%x, sLen: 0x%x\n",pInfo->len,
|
|
(UINT32)pInfo->pState, pInfo->sLen);
|
|
|
|
pNode = DOT1X_CALLOC(1,sizeof(AUTH_REQUEST_MSG_NODE));
|
|
if(pNode == NULL)
|
|
{
|
|
return(ERROR);
|
|
}
|
|
|
|
msg = ipradius_access_request_pkt_malloc();
|
|
if (msg == IP_NULL)
|
|
{
|
|
DOT1X_FREE(pNode);
|
|
return ERROR;
|
|
}
|
|
|
|
pNode->pUserCallback = pInfo->pUserCallback;
|
|
pNode->reqHandle = msgHandle;
|
|
pNode->seqNum = authServerCtrl.seqNum;
|
|
|
|
/* MTU attribute */
|
|
if(pInfo->mtu)
|
|
{
|
|
if(ipradius_attr_add_u32(msg,IPRADIUS_ATTR_Framed_MTU,
|
|
(Ip_u32)pInfo->mtu) != IPCOM_SUCCESS)
|
|
{
|
|
DBG_PRINT("dot1xAuthServerEapMsgSend:"
|
|
" ipradius_attr_add_u32 failed\n");
|
|
goto errout;
|
|
}
|
|
|
|
DBG_PRINT("Radius MTU %d",pInfo->mtu);
|
|
}
|
|
|
|
/* calling station ID */
|
|
for(i = 0; i < DOT1X_AS_STATION_ID_LEN; i++)
|
|
{
|
|
sprintf(&strHolder[i*2],"%02x",(UINT8)pInfo->callingId[i]);
|
|
}
|
|
|
|
if(ipradius_attr_add_array(msg,IPRADIUS_ATTR_Calling_Station_Id,
|
|
(Ip_u8*)strHolder,DOT1X_AS_STATION_ID_LEN*2) != IPCOM_SUCCESS)
|
|
{
|
|
DBG_PRINT("dot1xAuthServerEapMsgSend:"
|
|
" ipradius_attr_add_array failed\n");
|
|
goto errout;
|
|
}
|
|
|
|
/* user name attribute */
|
|
if(ipradius_attr_add_array(msg,IPRADIUS_ATTR_User_Name,(Ip_u8*)pInfo->pId,
|
|
(Ip_u8)pInfo->len) != IPCOM_SUCCESS)
|
|
{
|
|
DBG_PRINT("dot1xAuthServerEapMsgSend:"
|
|
" ipradius_attr_add_array failed\n");
|
|
goto errout;
|
|
}
|
|
|
|
DBG_PRINT("Radius Username %s %d\n", pInfo->pId, pInfo->len);
|
|
|
|
/* for now added the NAS port type as wireless type */
|
|
if(pInfo->portType)
|
|
{
|
|
if(ipradius_attr_add_u32(msg,IPRADIUS_ATTR_NAS_Port_Type,
|
|
(Ip_u32)pInfo->portType) != IPCOM_SUCCESS)
|
|
{
|
|
DBG_PRINT("dot1xAuthServerEapMsgSend:"
|
|
" ipradius_attr_add_u32 failed\n");
|
|
goto errout;
|
|
}
|
|
|
|
DBG_PRINT("Radius port type %d\n",pInfo->portType);
|
|
}
|
|
|
|
/* NAS Port */
|
|
if(msgHandle)
|
|
{
|
|
/* the low 8 bits should contain the interface number */
|
|
UINT32 portNum = 0;
|
|
*((UINT8 *)(&portNum) + 3) = msgHandle & 0xFF;
|
|
if(ipradius_attr_add_array(msg,IPRADIUS_ATTR_NAS_Port,
|
|
(Ip_u8*)&portNum,4) != IPCOM_SUCCESS)
|
|
{
|
|
DBG_PRINT("dot1xAuthServerEapMsgSend:"
|
|
" ipradius_attr_add_array failed\n");
|
|
goto errout;
|
|
}
|
|
|
|
DBG_PRINT("Radius NAS port %d\n", portNum);
|
|
}
|
|
|
|
/* NAS IP address */
|
|
if(ipradius_attr_add_array(msg, IPRADIUS_ATTR_NAS_IP_Address,
|
|
(Ip_u8*)&authServerCtrl.nasIpAddr,4) != IPCOM_SUCCESS)
|
|
{
|
|
DBG_PRINT("dot1xAuthServerEapMsgSend:"
|
|
" ipradius_attr_add_array failed\n");
|
|
goto errout;
|
|
}
|
|
|
|
/* send the state attribute if there is one */
|
|
if(pInfo->pState)
|
|
{
|
|
if(ipradius_attr_add_array(msg, IPRADIUS_ATTR_State,
|
|
(Ip_u8*)pInfo->pState,(Ip_u8)pInfo->sLen) != IPCOM_SUCCESS)
|
|
{
|
|
DBG_PRINT("dot1xAuthServerEapMsgSend:"
|
|
" ipradius_attr_add_array failed\n");
|
|
goto errout;
|
|
}
|
|
|
|
DBG_PRINT("Radius State %d\n", pInfo->pState);
|
|
}
|
|
|
|
|
|
#ifdef INCLUDE_IPEAP_UPDATE
|
|
|
|
/* Add EAP_Key_Name Attribute rfc5247,p67 , for eap-update */
|
|
if(ipradius_attr_add_array(msg,IPRADIUS_ATTR_EAP_Key_Name,NULL,0) != IPCOM_SUCCESS)
|
|
{
|
|
DBG_PRINT("dot1xAuthServerEapMsgSend:"
|
|
" ipradius_attr_add_array adding attribute IPRADIUS_ATTR_EAP_Key_Name failed\n");
|
|
goto errout;
|
|
}
|
|
#endif
|
|
|
|
|
|
/* EAP Message */
|
|
sizeRemaining = len;
|
|
while( sizeRemaining > 0 ) {
|
|
|
|
if( sizeRemaining > DOT1X_AUTH_MAX_EAP_FRAG_SIZE ) {
|
|
eapFragLen = DOT1X_AUTH_MAX_EAP_FRAG_SIZE;
|
|
sizeRemaining -= DOT1X_AUTH_MAX_EAP_FRAG_SIZE;
|
|
} else {
|
|
eapFragLen = sizeRemaining;
|
|
sizeRemaining = 0;
|
|
}
|
|
/* Here hoping that ipradius_access_request_pkt_malloc() allocated enough buffer space */
|
|
if(ipradius_attr_add_array(msg, IPRADIUS_ATTR_EAP_Message, (Ip_u8*)pEap,
|
|
(Ip_u8)eapFragLen) != IPCOM_SUCCESS)
|
|
{
|
|
DBG_PRINT("dot1xAuthServerEapMsgSend:"
|
|
" ipradius_attr_add_array failed\n");
|
|
goto errout;
|
|
}
|
|
|
|
/* Go to the next eap fragment */
|
|
if(sizeRemaining > 0)
|
|
pEap += DOT1X_AUTH_MAX_EAP_FRAG_SIZE;
|
|
}
|
|
|
|
DBG_PRINT("Radius EAP Msg len %d\n", len);
|
|
|
|
semTake(authServerCtrl.semId,WAIT_FOREVER);
|
|
authServerCtrl.seqNum++;
|
|
sllPutAtTail(&authServerCtrl.list,(SL_NODE *)pNode);
|
|
semGive(authServerCtrl.semId);
|
|
|
|
/* Populate request server list */
|
|
for ( svrIdx = 0; svrIdx < authServerCtrl.num_radius_servers; svrIdx++ )
|
|
{
|
|
memcpy(&request.serv_list[svrIdx].server_addr,
|
|
&authServerCtrl.radius_server_addr[svrIdx],
|
|
sizeof(union Ip_sockaddr_union));
|
|
request.serv_list[svrIdx].server_type = IPRADIUS_ACCESS_SERVER;
|
|
request.serv_list[svrIdx].pollnum = authServerCtrl.server_info[svrIdx].retry_count;
|
|
request.serv_list[svrIdx].pollsec = authServerCtrl.server_info[svrIdx].timeout;
|
|
}
|
|
|
|
/* Make the access request */
|
|
#ifdef INET
|
|
if( authServerCtrl.server_info[0].domain == IP_AF_INET )
|
|
request.fd = authServerCtrl.radius_fd;
|
|
else
|
|
#endif
|
|
#ifdef INET6
|
|
if( authServerCtrl.server_info[0].domain == IP_AF_INET6 )
|
|
request.fd6 = authServerCtrl.radius_fd;
|
|
else
|
|
#endif
|
|
DBG_PRINT("dot1xAuthServerEapMsgSend: Invalid domain %d\n",
|
|
authServerCtrl.server_info[0].domain);
|
|
|
|
request.status = IPCOM_SUCCESS;
|
|
request.unique = pNode->seqNum;
|
|
request.event_callback = ipradiusRequestNormalReply;
|
|
request.num_servers = authServerCtrl.num_radius_servers;
|
|
retval = ipradius_access_request(msg, &request, &errattr);
|
|
if (retval != IPCOM_SUCCESS)
|
|
{
|
|
DBG_PRINT("radius access request failed :%d (attr=%d)\n",retval,
|
|
errattr);
|
|
return ERROR;
|
|
}
|
|
|
|
return(OK);
|
|
errout:
|
|
ipradius_access_request_pkt_free (msg);
|
|
return (ERROR);
|
|
}
|
|
|
|
/***************************************************************************
|
|
* ipradiusRequestNormalReply - callback to receive the reply from the RADIUS
|
|
* server
|
|
*
|
|
* This is the pre-installed callback routine. It is invoked by the RADIUS
|
|
* client to receive a reply from the RADIUS server. This routine further calls
|
|
* the user specified callback to deliver the result back.
|
|
*
|
|
* RETURNS: NONE
|
|
*/
|
|
LOCAL Ip_err ipradiusRequestNormalReply
|
|
(
|
|
int event,
|
|
struct Ipradius_request_struct *request,
|
|
Ipradius_header *request_packet,
|
|
Ipradius_header *response_packet,
|
|
Ipradius_server *server
|
|
)
|
|
{
|
|
AUTH_REQUEST_MSG_NODE *pMsgNode;
|
|
enum DOT1X_AS_CODE asCode;
|
|
UINT8 *pMsg = NULL;
|
|
UINT8 *pBuf = NULL;
|
|
Ip_u8 *pAttr;
|
|
int len = 0;
|
|
int retCode = -1;
|
|
|
|
#ifdef DOT1X_AS_DEBUG
|
|
DBG_PRINT("ipradiusRequestNormalReply: "
|
|
"Got reply packet: %s\n",svrRsp[event]);
|
|
#endif
|
|
|
|
/* retrive the request node */
|
|
semTake(authServerCtrl.semId,WAIT_FOREVER);
|
|
pMsgNode = requestMsgNodeGet(&authServerCtrl.list,request->unique,
|
|
GET_TYPE_FIND_AND_REMOVE);
|
|
semGive(authServerCtrl.semId);
|
|
|
|
if (pMsgNode == NULL)
|
|
{
|
|
/* Not an expected reply */
|
|
DBG_PRINT("Could not find request node: ID: %d\n",
|
|
request->unique);
|
|
return IPCOM_ERR_NOT_FOUND;
|
|
}
|
|
|
|
/* translate the received code */
|
|
if (event == IPRADIUS_EVENT_ACCESS_ACCEPT)
|
|
asCode = DOT1X_AS_ACCESS_ACCEPT;
|
|
else if (event == IPRADIUS_EVENT_ACCESS_REJECT)
|
|
asCode = DOT1X_AS_ACCESS_REJECT;
|
|
else if (event == IPRADIUS_EVENT_ACCOUNTING_RESPONSE)
|
|
asCode = DOT1X_AS_ACCOUNTING_RESPONSE;
|
|
else if (event == IPRADIUS_EVENT_ACCESS_CHALLENGE)
|
|
asCode = DOT1X_AS_ACCESS_CHALLENGE;
|
|
else if ((event == IPRADIUS_EVENT_ACCESS_CANCELLED)
|
|
|| (event == IPRADIUS_EVENT_ACCESS_TIMEOUT))
|
|
{
|
|
asCode = DOT1X_AS_GEN_ERROR;
|
|
ipradiusRequestErrorReply((UINT32)request->unique,(UINT32)event);
|
|
}
|
|
else
|
|
{
|
|
DBG_PRINT("ipradiusRequestNormalReply: Unknown RADIUS event\n");
|
|
return IPCOM_ERR_NOT_FOUND;
|
|
}
|
|
|
|
if (asCode == DOT1X_AS_ACCESS_ACCEPT ||
|
|
asCode == DOT1X_AS_ACCESS_REJECT ||
|
|
asCode == DOT1X_AS_ACCOUNTING_RESPONSE ||
|
|
asCode == DOT1X_AS_ACCESS_CHALLENGE)
|
|
{
|
|
struct RCVD_ATTR
|
|
{
|
|
UINT8 * pBuf;
|
|
int len;
|
|
} rcvdAttrs[NUM_OF_ATTRS_SEND];
|
|
int i = 0;
|
|
bzero((char *)&rcvdAttrs[0],
|
|
NUM_OF_ATTRS_SEND*sizeof(struct RCVD_ATTR));
|
|
|
|
/* Go through the attribute list */
|
|
for( pAttr = ipradius_attr_get_next(response_packet, IP_NULL);
|
|
pAttr;
|
|
pAttr = ipradius_attr_get_next(response_packet, pAttr) )
|
|
{
|
|
int attrInx = 0;
|
|
Ip_u8 attrType = pAttr[DOT1X_AUTH_RADIUS_ATTR_TYPE_IDX];
|
|
Ip_u8 *pAttrData = &pAttr[DOT1X_AUTH_RADIUS_ATTR_DATA_IDX];
|
|
|
|
if (attrType == IPRADIUS_ATTR_EAP_Message ||
|
|
attrType == IPRADIUS_ATTR_Vendor_Specific ||
|
|
attrType == IPRADIUS_ATTR_State
|
|
#ifdef INCLUDE_IPEAP_UPDATE
|
|
|| attrType == IPRADIUS_ATTR_EAP_Key_Name
|
|
#endif
|
|
)
|
|
{
|
|
DOT1X_AS_ATTR_HEADER_T *pAttrHeader;
|
|
|
|
/* Only handle the above type of attributes for now */
|
|
|
|
/* Special processing for EAP messages as they may be
|
|
* chained attributes */
|
|
if(attrType == IPRADIUS_ATTR_EAP_Message)
|
|
{
|
|
/* Get the length of data for the entire EAP message chain */
|
|
if( (dot1xAuthSvrLibProcessEAPMsg(response_packet,
|
|
pAttr,&len,NULL)) == NULL )
|
|
return IPCOM_ERR_BAD_PACKET;
|
|
}
|
|
else
|
|
len = pAttr[DOT1X_AUTH_RADIUS_ATTR_LEN_IDX] -
|
|
DOT1X_AUTH_RADIUS_ATTR_HEADER_LEN;
|
|
|
|
if (!len)
|
|
continue;
|
|
|
|
pBuf = DOT1X_CALLOC(1, len + DOT1X_AS_ATTR_HEADER_LEN);
|
|
if(pBuf == NULL)
|
|
{
|
|
DBG_PRINT("radiusEventCallback: Error getting buffer\n");
|
|
return IPCOM_ERR_NO_MEMORY;
|
|
}
|
|
|
|
/* If its and EAP message we may have to walk the chain */
|
|
if (attrType == IPRADIUS_ATTR_EAP_Message)
|
|
{
|
|
pMsg = pBuf;
|
|
/* retrieve the response attributes */
|
|
if( (pAttr = dot1xAuthSvrLibProcessEAPMsg(response_packet,
|
|
pAttr,NULL,pMsg)) == NULL)
|
|
return IPCOM_ERR_BAD_PACKET;
|
|
}
|
|
else
|
|
{
|
|
pMsg = pBuf + DOT1X_AS_ATTR_HEADER_LEN;
|
|
/* retrieve the response attributes */
|
|
memcpy(pMsg,pAttrData,len);
|
|
}
|
|
|
|
pAttrHeader = (DOT1X_AS_ATTR_HEADER_T *)pBuf;
|
|
|
|
/* extract the info from vendor specific message */
|
|
if (attrType == IPRADIUS_ATTR_Vendor_Specific)
|
|
{
|
|
|
|
/* Decrypt the vendor specific packet */
|
|
dot1xAuthSvrLibProcessVendorMsg(pMsg,len,
|
|
(UINT8*)&request_packet->auth[0],
|
|
(char *)&server->shared_secret[0],
|
|
server->shared_secret_len);
|
|
|
|
if ((retCode = vendorAttributeKeyExtract(&pMsg,(int *)&len))
|
|
> 0)
|
|
{
|
|
/* set type */
|
|
if (retCode == ATTR_SK_INX)
|
|
{
|
|
/* B0562 - Create session key message */
|
|
#define WPA_RX_KEY_OFFSET (DOT1X_AS_ATTR_HEADER_LEN + 6)
|
|
char *p = (char*)pBuf + WPA_RX_KEY_OFFSET;
|
|
|
|
/* Copy the attribute */
|
|
pAttrHeader->type = DOT1X_AS_SESSION_SEND_KEY;
|
|
pAttrHeader->len = len;
|
|
bcopy((char*)pMsg, p, len);
|
|
|
|
attrInx = retCode;
|
|
|
|
/* deliver this key to dot1xKeyLib. It is used as
|
|
* the random key for key generation. */
|
|
dot1xKeyRandomKeySet(pMsg);
|
|
}
|
|
else
|
|
pAttrHeader->type = DOT1X_AS_SESSION_RCEV_KEY;
|
|
|
|
/* set the length and copy the key */
|
|
pAttrHeader->len = len;
|
|
bcopy((char*)pMsg,
|
|
(char*)pBuf+DOT1X_AS_ATTR_HEADER_LEN, len);
|
|
attrInx = retCode;
|
|
}
|
|
}
|
|
else if (attrType == IPRADIUS_ATTR_EAP_Message)
|
|
{
|
|
attrInx = ATTR_EP_INX;
|
|
}
|
|
else if (attrType == IPRADIUS_ATTR_State)
|
|
{
|
|
pAttrHeader->type = 0; /* type = 0 for now */;
|
|
pAttrHeader->len = len;
|
|
attrInx = ATTR_ST_INX;
|
|
}
|
|
|
|
#ifdef INCLUDE_IPEAP_UPDATE
|
|
else if (attrType == IPRADIUS_ATTR_EAP_Key_Name)
|
|
{
|
|
DBG_PRINT("ipradiusRequestNormalReply: ******* EAP_Key_Name Response ******* \n ");
|
|
{
|
|
Ip_u8 p;
|
|
for(p=0;p<len ; p++)
|
|
DBG_PRINT("%x",pMsg[p]);
|
|
DBG_PRINT("\n");
|
|
}
|
|
}
|
|
#endif
|
|
else
|
|
{
|
|
/* Will never happen */
|
|
DBG_PRINT("radiusIF: Fatal error,Unexpected "
|
|
"program branch\n");
|
|
break;
|
|
}
|
|
|
|
/* If it wasn't the attribute we wanted, free pBuf */
|
|
if(attrInx < 0)
|
|
{
|
|
DOT1X_FREE(pBuf);
|
|
}
|
|
else
|
|
{
|
|
if (rcvdAttrs[attrInx].pBuf)
|
|
{
|
|
/* possible duplicated attribute, discard the old
|
|
one */
|
|
DOT1X_FREE((rcvdAttrs[attrInx].pBuf));
|
|
}
|
|
|
|
rcvdAttrs[attrInx].pBuf = pBuf;
|
|
rcvdAttrs[attrInx].len = len;
|
|
}
|
|
}
|
|
|
|
} /* end for */
|
|
|
|
/* Now deliver the received attributes, The first message is always
|
|
* the EAP attribute, the second message is a combination of the
|
|
* received attributes
|
|
*/
|
|
pBuf = NULL;
|
|
len = 0;
|
|
if (asCode == DOT1X_AS_ACCESS_ACCEPT)
|
|
{
|
|
/* deliver the received keys if there is one */
|
|
|
|
if (rcvdAttrs[ATTR_SK_INX].pBuf)
|
|
len = rcvdAttrs[ATTR_SK_INX].len + DOT1X_AS_ATTR_HEADER_LEN;
|
|
|
|
if (rcvdAttrs[ATTR_RK_INX].pBuf)
|
|
len += rcvdAttrs[ATTR_RK_INX].len + DOT1X_AS_ATTR_HEADER_LEN;
|
|
|
|
if (len != 0)
|
|
{
|
|
if ((pBuf = DOT1X_CALLOC(1,len)) == NULL)
|
|
{
|
|
DBG_PRINT("requestNormalReply: Error2 getting buffer\n");
|
|
return IPCOM_ERR_NO_MEMORY;
|
|
}
|
|
|
|
/* copy to a new buffer */
|
|
bcopy((char *)rcvdAttrs[ATTR_SK_INX].pBuf,
|
|
(char *)pBuf,
|
|
rcvdAttrs[ATTR_SK_INX].len + DOT1X_AS_ATTR_HEADER_LEN);
|
|
|
|
/* free the previous buffer */
|
|
DOT1X_FREE(rcvdAttrs[ATTR_SK_INX].pBuf);
|
|
|
|
/* allow to free the new buffer later */
|
|
rcvdAttrs[ATTR_SK_INX].pBuf = pBuf;
|
|
|
|
if (rcvdAttrs[ATTR_RK_INX].pBuf)
|
|
{
|
|
bcopy((char *)rcvdAttrs[ATTR_RK_INX].pBuf,
|
|
(char *)(pBuf + rcvdAttrs[ATTR_SK_INX].len +
|
|
DOT1X_AS_ATTR_HEADER_LEN),
|
|
rcvdAttrs[ATTR_RK_INX].len +
|
|
DOT1X_AS_ATTR_HEADER_LEN);
|
|
|
|
/* free the previous buffer */
|
|
DOT1X_FREE(rcvdAttrs[ATTR_RK_INX].pBuf);
|
|
|
|
/* prevent free again */
|
|
rcvdAttrs[ATTR_RK_INX].pBuf = NULL;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* deliver the STATE attribute if there is one*/
|
|
if (rcvdAttrs[ATTR_ST_INX].pBuf)
|
|
{
|
|
len = rcvdAttrs[ATTR_ST_INX].len + DOT1X_AS_ATTR_HEADER_LEN;
|
|
pBuf = rcvdAttrs[ATTR_ST_INX].pBuf;
|
|
}
|
|
}
|
|
|
|
pMsgNode->pUserCallback(pMsgNode->reqHandle, asCode, rcvdAttrs[0].pBuf,
|
|
rcvdAttrs[0].len, pBuf, len);
|
|
|
|
for (i = 0; i < NUM_OF_ATTRS_SEND; i++)
|
|
if (rcvdAttrs[i].pBuf)
|
|
DOT1X_FREE(rcvdAttrs[i].pBuf);
|
|
}
|
|
|
|
DOT1X_FREE(pMsgNode);
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
/***************************************************************************
|
|
* ipradiusRequestErrorReply - error callback
|
|
*
|
|
* This routine is called when RADIUS client is unable to send the request
|
|
* to the RADIUS server. The routine will try to send the request to the
|
|
* another RADIUS server until successfully sent or the server list is
|
|
* exhausted.
|
|
*
|
|
* RETURNS: NONE
|
|
*
|
|
* NOMANUAL
|
|
*/
|
|
LOCAL void ipradiusRequestErrorReply
|
|
(
|
|
UINT32 reqHandle, /* RADIUS request handle */
|
|
UINT32 code /* RADIUS result error code */
|
|
)
|
|
{
|
|
AUTH_REQUEST_MSG_NODE * pMsgNode;
|
|
enum DOT1X_AS_CODE asCode;
|
|
|
|
DBG_PRINT ("RadiusIF: Callback Error Case.\n");
|
|
DBG_PRINT ("RadiusIF: ReqHandle: %d, Code: %d\n", reqHandle, code);
|
|
|
|
/* Set the server error indicator */
|
|
authServerCtrl.serverError = TRUE;
|
|
|
|
/* convert the error code */
|
|
if (code == IPRADIUS_EVENT_ACCESS_CANCELLED)
|
|
asCode = DOT1X_AS_ACCESS_CANCELLED;
|
|
else if (code == IPRADIUS_EVENT_ACCESS_TIMEOUT)
|
|
asCode = DOT1X_AS_ACCESS_TIMEOUT;
|
|
else
|
|
{
|
|
/* for now, treated as the general error case */
|
|
asCode = DOT1X_AS_GEN_ERROR;
|
|
}
|
|
|
|
semTake(authServerCtrl.semId,WAIT_FOREVER);
|
|
|
|
pMsgNode = requestMsgNodeGet(&authServerCtrl.list,reqHandle,
|
|
GET_TYPE_FIND_AND_REMOVE);
|
|
|
|
semGive(authServerCtrl.semId);
|
|
|
|
if (pMsgNode != NULL)
|
|
{
|
|
/* report the error to the user */
|
|
|
|
(*pMsgNode->pUserCallback)(pMsgNode->reqHandle,
|
|
asCode,NULL,0,NULL,0);
|
|
|
|
DOT1X_FREE(pMsgNode);
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* dot1xAuthSvrLibGetEAPMsg - Process the RADIUS EAP attribute chain.
|
|
*
|
|
* This routine walks the RADIUS EAP attribute chain to get the length of the
|
|
* chained EAP message or copies the chained EAP message into the provided
|
|
* buffer or both. If len is NULL only the data will be copied to the
|
|
* provided buffer. If pBuf is NULL only the length of the data of the
|
|
* chained EAP message will be returned. Typically this function is called
|
|
* twice. The first call is to obtain the length of data for the chained
|
|
* EAP message and the second call is to copy the chained EAP message into
|
|
* the provided buffer that was allocated with the length obtained from the
|
|
* first call. If pBuf is provided, this function assumes that enough buffer
|
|
* has been allocated to copy the entire EAP message chain into said buffer.
|
|
*
|
|
* RETURNS: Pointer to the last RADIUS EAP attribute
|
|
*
|
|
* NOMANUAL
|
|
*/
|
|
LOCAL Ip_u8 * dot1xAuthSvrLibProcessEAPMsg
|
|
(
|
|
Ipradius_header *response_packet,
|
|
Ip_u8 *pAttr,
|
|
int *dataLen,
|
|
UINT8 *pBuf
|
|
)
|
|
{
|
|
Ip_u8 *pAttrPrev;
|
|
UINT32 attrDataLen;
|
|
UINT32 myDataLen = 0;
|
|
|
|
if(pAttr == NULL)
|
|
{
|
|
DBG_PRINT("pAttr is NULL\n");
|
|
return NULL;
|
|
}
|
|
|
|
/* Only process EAP attributes */
|
|
if(pAttr[DOT1X_AUTH_RADIUS_ATTR_TYPE_IDX] != IPRADIUS_ATTR_EAP_Message)
|
|
return NULL;
|
|
|
|
/* Walk the EAP message chain */
|
|
while( pAttr[DOT1X_AUTH_RADIUS_ATTR_TYPE_IDX] == IPRADIUS_ATTR_EAP_Message )
|
|
{
|
|
|
|
/* The length of the data is the length
|
|
* of the attribute less the attribute
|
|
* header */
|
|
attrDataLen = pAttr[DOT1X_AUTH_RADIUS_ATTR_LEN_IDX] -
|
|
DOT1X_AUTH_RADIUS_ATTR_HEADER_LEN;
|
|
myDataLen += attrDataLen;
|
|
|
|
if(pBuf)
|
|
{
|
|
memcpy(pBuf,&pAttr[DOT1X_AUTH_RADIUS_ATTR_DATA_IDX],
|
|
attrDataLen);
|
|
pBuf += attrDataLen;
|
|
}
|
|
|
|
pAttrPrev = pAttr;
|
|
pAttr = ipradius_attr_get_next(response_packet, pAttr);
|
|
|
|
/* Break out if we got to the last attribute */
|
|
if(pAttr == NULL) break;
|
|
}
|
|
|
|
if(dataLen)
|
|
*dataLen = myDataLen;
|
|
|
|
return pAttrPrev;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* vendorAttributeKeyExtract - process the vendor specific RADIUS attribute
|
|
*
|
|
* This routine mainly handles Cisco and Microsoft session key attributes.
|
|
* When the key extraction from the original message is done, the *ppMsg will
|
|
* contain the starting position of the key and *pLen will have the actual
|
|
* length of the key.
|
|
*
|
|
* RETURNS: key index
|
|
*/
|
|
LOCAL int vendorAttributeKeyExtract
|
|
(
|
|
UINT8 **ppMsg, /* point to pointer to the first byte of the vendor value */
|
|
int *pLen /* point to the len */
|
|
)
|
|
{
|
|
char * pMsg = (char *)*ppMsg;
|
|
UINT32 vendorId;
|
|
int len = 0;
|
|
int returnCode = -1;
|
|
|
|
/* the vendor value should start with 4 byte Enterprise code as defined
|
|
* in SNMP SMI in network byte order. The first byte is 0.
|
|
*/
|
|
vendorId = *(UINT32 *)pMsg;
|
|
vendorId = ntohl(vendorId);
|
|
|
|
if (vendorId == VENDOR_ID_CISCO) /* CISCO */
|
|
{
|
|
DBG_PRINT("RadiusIF: Vendor Specific CISCO attribute received\n");
|
|
|
|
/* for now, only process the session key */
|
|
|
|
pMsg += 4; /* advance to sub type */
|
|
|
|
/* The Cisco AV pair, if leap:session-key, has already been decrpted */
|
|
if (*pMsg == VENDOR_TYPE_CISCO_AVPAIR)
|
|
{
|
|
++pMsg; /* advance to length field */
|
|
len = *pMsg; /* length */
|
|
|
|
++pMsg; /* advance to start of value */
|
|
|
|
#if 0 /* RADIUS Client has already stripped the "leap:session-key:" */
|
|
if(bcmp(pMsg, "leap:session-key=", strlen("leap:session-key="))
|
|
!= 0)
|
|
{
|
|
DBG_PRINT("RadiusIF: Cisco AV-pair not leap:session-key "
|
|
"not supported\n");
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
len = len - 2; /* key length is len - 2 */
|
|
returnCode = ATTR_SK_INX;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* other CISCO sub types, not supported yet */
|
|
DBG_PRINT("RadiusIF: Cisco non-AV-pair subtype:%x not supported\n",
|
|
*pMsg);
|
|
}
|
|
}
|
|
else if (vendorId == VENDOR_ID_MICROSOFT)
|
|
{
|
|
/* Microsoft has the code 311 */
|
|
|
|
DBG_PRINT("RadiusIF: Vendor Specific MicroSoft attribute received\n");
|
|
|
|
pMsg += 4; /* advance to sub type */
|
|
|
|
if (*pMsg == VENDOR_TYPE_MS_MPPE_SEND_KEY ||
|
|
*pMsg == VENDOR_TYPE_MS_MPPE_RECV_KEY)
|
|
{
|
|
returnCode = ((*pMsg) == VENDOR_TYPE_MS_MPPE_SEND_KEY) ?
|
|
ATTR_SK_INX : ATTR_RK_INX;
|
|
|
|
/* 16 is MS-MPPE-SEND-Key and 17 is MS-MPPE-Recv-Key */
|
|
|
|
pMsg++; /* advance to Vendor-length */
|
|
len = *pMsg++; /* length, and advance to start of key */
|
|
|
|
/* key length = len - 2 (1 byte type and length fields) */
|
|
len = len - 2;
|
|
}
|
|
else
|
|
{
|
|
/* not the key message*/
|
|
DBG_PRINT("RadiusIF: other sub types, not supported\n");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DBG_PRINT("RadiusIF: Vendor ID %#08x not supported\n", vendorId);
|
|
}
|
|
|
|
/* store the adjusted message position and length */
|
|
if (returnCode > 0)
|
|
{
|
|
*ppMsg = (UINT8 *)pMsg;
|
|
*pLen = len;
|
|
}
|
|
|
|
return returnCode;
|
|
}
|
|
|
|
LOCAL STATUS dot1xAuthSvrLibProcessVendorMsg
|
|
(
|
|
UINT8* pVendorMsg,
|
|
UINT32 pVendorMsgLen,
|
|
UINT8* requestAuth,
|
|
char *secret,
|
|
UINT32 secretLen
|
|
)
|
|
{
|
|
UINT8 newAuth[DOT1X_AUTH_RADIUS_SIZE_OF_AUTHENTICATOR +
|
|
DOT1X_AUTH_VENDOR_SALT_LENGTH];
|
|
UINT32 saltPos;
|
|
UINT32 vendorId;
|
|
BOOL decrypted;
|
|
UINT32 headerLen = DOT1X_AUTH_VENDOR_SPECIFIC_ID_LENGTH +
|
|
DOT1X_AUTH_VENDOR_TYPE_LENGTH +
|
|
DOT1X_AUTH_VENDOR_LENGTH;
|
|
|
|
/* Retrieve the vendor ID */
|
|
bcopy((const char *)pVendorMsg,(char *)&vendorId,4);
|
|
vendorId = ip_ntohl(vendorId);
|
|
|
|
if (vendorId == VENDOR_ID_CISCO)
|
|
{
|
|
char sKeyId[] = "leap:session-key=";
|
|
|
|
/* Calculate the Salt position */
|
|
saltPos = headerLen + strlen(sKeyId);
|
|
}
|
|
|
|
else if (vendorId == VENDOR_ID_MICROSOFT)
|
|
{
|
|
/* Get the salt position*/
|
|
saltPos = headerLen;
|
|
}
|
|
|
|
else
|
|
{
|
|
/* Not supported for now */
|
|
DBG_PRINT("RADIUS: Not supported VendorID\n");
|
|
return (ERROR);
|
|
}
|
|
|
|
/* Zero the new authenticator buffer */
|
|
memset ((unsigned char *) newAuth, 0,
|
|
DOT1X_AUTH_RADIUS_SIZE_OF_AUTHENTICATOR +
|
|
DOT1X_AUTH_VENDOR_SALT_LENGTH);
|
|
|
|
/* Copy request authenticator */
|
|
memcpy (newAuth, requestAuth, DOT1X_AUTH_RADIUS_SIZE_OF_AUTHENTICATOR);
|
|
|
|
/* Copy the salt value */
|
|
bcopy((const char *)&pVendorMsg[saltPos],
|
|
(char *)(&newAuth[0] + DOT1X_AUTH_RADIUS_SIZE_OF_AUTHENTICATOR),
|
|
DOT1X_AUTH_VENDOR_SALT_LENGTH);
|
|
|
|
if (((pVendorMsgLen - saltPos -
|
|
DOT1X_AUTH_VENDOR_SALT_LENGTH) %
|
|
DOT1X_AUTH_RADIUS_PASSWORD_MODULUS) == 0x00)
|
|
{
|
|
decrypted = dot1xAuthSvrLibMd5Decryption(
|
|
(unsigned char *)&newAuth[0],
|
|
DOT1X_AUTH_RADIUS_SIZE_OF_AUTHENTICATOR +
|
|
DOT1X_AUTH_VENDOR_SALT_LENGTH,
|
|
|
|
(unsigned char *)secret,
|
|
(USHORT) secretLen,
|
|
|
|
(unsigned char *)&pVendorMsg[saltPos +
|
|
DOT1X_AUTH_VENDOR_SALT_LENGTH],
|
|
(USHORT) (pVendorMsgLen - saltPos -
|
|
DOT1X_AUTH_VENDOR_SALT_LENGTH));
|
|
|
|
if (decrypted == FALSE)
|
|
{
|
|
DBG_PRINT("Decryption failed\n");
|
|
return (ERROR);
|
|
}
|
|
|
|
DBG_PRINT("Decryption passed, vendor lenth: %d\n",headerLen +
|
|
(UINT8)pVendorMsg[saltPos + DOT1X_AUTH_VENDOR_SALT_LENGTH]
|
|
- DOT1X_AUTH_VENDOR_SPECIFIC_ID_LENGTH);
|
|
|
|
/* Set the vendor length field */
|
|
pVendorMsg[DOT1X_AUTH_VENDOR_SPECIFIC_ID_LENGTH +
|
|
DOT1X_AUTH_VENDOR_TYPE_LENGTH] = headerLen +
|
|
(UINT8)pVendorMsg[saltPos + DOT1X_AUTH_VENDOR_SALT_LENGTH] -
|
|
DOT1X_AUTH_VENDOR_SPECIFIC_ID_LENGTH;
|
|
|
|
/* Copy the decrypted key */
|
|
bcopy((const char *)&pVendorMsg[saltPos + DOT1X_AUTH_VENDOR_SALT_LENGTH +
|
|
DOT1X_AUTH_VENDOR_KEY_LENGTH],(char *)&pVendorMsg[headerLen],
|
|
(UINT8)pVendorMsg[saltPos + DOT1X_AUTH_VENDOR_SALT_LENGTH]);
|
|
}
|
|
|
|
return (OK);
|
|
}
|
|
|
|
|
|
LOCAL BOOL dot1xAuthSvrLibMd5Decryption
|
|
(
|
|
unsigned char *cptr_identifier,
|
|
USHORT identifier_length,
|
|
unsigned char *p_secret,
|
|
USHORT secret_length,
|
|
unsigned char *p_data,
|
|
USHORT data_length
|
|
)
|
|
{
|
|
unsigned char md5_result[DOT1X_AUTH_MD5_RESULT_LENGTH];
|
|
unsigned char *bptr_md5_input;
|
|
ULONG md5_input_length;
|
|
USHORT xored_data_length;
|
|
unsigned char * bptr_data;
|
|
USHORT md5_result_index;
|
|
ULONG md5_result_length = DOT1X_AUTH_MD5_RESULT_LENGTH;
|
|
ULONG temp_value;
|
|
|
|
if ((p_data == NULL) || (p_secret == NULL))
|
|
{
|
|
return (FALSE);
|
|
}
|
|
|
|
if ((data_length % DOT1X_AUTH_RADIUS_PASSWORD_MODULUS) != 0)
|
|
{
|
|
return (FALSE);
|
|
}
|
|
|
|
md5_input_length = identifier_length + secret_length;
|
|
|
|
bptr_md5_input = (unsigned char *) calloc (1, md5_input_length);
|
|
|
|
if (bptr_md5_input == NULL)
|
|
{
|
|
return (FALSE);
|
|
}
|
|
|
|
memcpy (bptr_md5_input, p_secret, secret_length);
|
|
memcpy ((bptr_md5_input + secret_length), cptr_identifier, identifier_length);
|
|
|
|
if (!dot1xAuthSvrLibMd5Digest(bptr_md5_input, md5_input_length,
|
|
&md5_result[0], md5_result_length))
|
|
{
|
|
printf("RADIUS: do_radius_md5_decryption: MD5 error\n");
|
|
free (bptr_md5_input);
|
|
return (FALSE);
|
|
}
|
|
|
|
xored_data_length = data_length;
|
|
bptr_data = p_data;
|
|
md5_input_length = secret_length + DOT1X_AUTH_MD5_RESULT_LENGTH;
|
|
|
|
while (xored_data_length > 0)
|
|
{
|
|
for (md5_result_index = 0;
|
|
(md5_result_index < DOT1X_AUTH_MD5_RESULT_LENGTH) &&
|
|
(xored_data_length > 0);
|
|
--xored_data_length, ++md5_result_index, ++bptr_data)
|
|
{
|
|
temp_value = *bptr_data;
|
|
temp_value = temp_value ^ md5_result[md5_result_index];
|
|
md5_result[md5_result_index] = *bptr_data;
|
|
*bptr_data = (unsigned char) temp_value;
|
|
}
|
|
if (xored_data_length > 0)
|
|
{
|
|
memcpy ((bptr_md5_input + secret_length), md5_result,
|
|
DOT1X_AUTH_MD5_RESULT_LENGTH);
|
|
|
|
if (!dot1xAuthSvrLibMd5Digest(bptr_md5_input, md5_input_length,
|
|
&md5_result[0], md5_result_length))
|
|
{
|
|
printf("RADIUS: do_radius_md5_decryption: MD5 error\n");
|
|
free (bptr_md5_input);
|
|
return (FALSE);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
free (bptr_md5_input);
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* dot1xAuthSvrLibMd5Digest - compute MD5 digest.
|
|
*
|
|
* RETURNS: TRUE if no errors, FALSE otherwise
|
|
*/
|
|
LOCAL BOOL dot1xAuthSvrLibMd5Digest
|
|
(
|
|
unsigned char *input,
|
|
ULONG input_length,
|
|
unsigned char *result,
|
|
ULONG result_length
|
|
)
|
|
{
|
|
MD5_CTX ctx;
|
|
|
|
if (result_length < MD5_DIGEST_LENGTH)
|
|
return (FALSE);
|
|
|
|
#ifdef OPENSSL_FIPS
|
|
/* MD5 cannot be used */
|
|
if (FIPS_mode())
|
|
{
|
|
/* Authentication failed. */
|
|
printf("dot1x: MD5 is not supported when FIPS mode is enabled!");
|
|
return(FALSE);
|
|
}
|
|
#endif
|
|
|
|
if (!MD5_Init(&ctx))
|
|
return (FALSE);
|
|
if (!MD5_Update(&ctx, input, input_length))
|
|
return (FALSE);
|
|
if (!MD5_Final(result, &ctx))
|
|
return (FALSE);
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* requestMsgNodeGet - get the node matching the value of parameter reqHandle.
|
|
*
|
|
* RETURNS: the message node or NULL if not found.
|
|
*/
|
|
LOCAL AUTH_REQUEST_MSG_NODE * requestMsgNodeGet
|
|
(
|
|
SL_LIST * pList, /* the searched single link list */
|
|
UINT32 reqHandle, /* the unique value identifies the message node */
|
|
int getType /* find or find and remove from the list */
|
|
)
|
|
{
|
|
SL_NODE * pNode;
|
|
SL_NODE * pPreNode;
|
|
|
|
pNode = SLL_FIRST(pList);
|
|
pPreNode = NULL;
|
|
|
|
while (pNode)
|
|
{
|
|
if (((AUTH_REQUEST_MSG_NODE *)pNode)->seqNum == (UINT32)reqHandle)
|
|
{
|
|
if (getType == GET_TYPE_FIND_AND_REMOVE)
|
|
sllRemove(pList,pNode,pPreNode);
|
|
break;
|
|
}
|
|
pPreNode = pNode;
|
|
pNode = SLL_NEXT(pNode);
|
|
}
|
|
|
|
return (AUTH_REQUEST_MSG_NODE *)pNode;
|
|
}
|
|
|
|
LOCAL IPCOM_PROCESS(tDot1xRecv)
|
|
{
|
|
#ifdef DOT1X_AS_DEBUG
|
|
Ip_err retval = IPCOM_SUCCESS;
|
|
#endif
|
|
Ip_fd_set rd_set;
|
|
int num_ready_socks;
|
|
int svrIdx;
|
|
ipcom_proc_init();
|
|
|
|
IP_FD_ZERO(&rd_set);
|
|
|
|
/*********** Eternal radius response loop **********/
|
|
for(;;)
|
|
{
|
|
int radius_sock = authServerCtrl.radius_fd;
|
|
|
|
if (IP_UNLIKELY(radius_sock < 0))
|
|
break;
|
|
|
|
IP_FD_SET(radius_sock, &rd_set);
|
|
num_ready_socks = ipcom_socketselect(radius_sock + 1,
|
|
&rd_set,
|
|
IP_NULL,
|
|
IP_NULL,
|
|
IP_NULL);
|
|
|
|
if (num_ready_socks < 0)
|
|
{
|
|
if (ipcom_errno == IP_ERRNO_EINTR)
|
|
continue;
|
|
else
|
|
{
|
|
DBG_PRINT("ipcom_authd :: select failed %s\n",
|
|
ipcom_strerror(ipcom_errno));
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (IP_FD_ISSET(radius_sock, &rd_set))
|
|
{
|
|
Ipcom_msg_auth *authmsg;
|
|
int msglen;
|
|
|
|
if (ipcom_socketioctl(radius_sock, IP_FIONREAD, &msglen) < 0)
|
|
{
|
|
/* Just drop the message */
|
|
(void)ipcom_recv(radius_sock, IP_NULL, 0, 0);
|
|
continue;
|
|
}
|
|
|
|
if( (authmsg = ipcom_malloc(sizeof(Ipcom_msg_auth) + msglen)) == IP_NULL ) {
|
|
DBG_PRINT("tDot1xRecv :: recv() failed: %s\n",
|
|
ipcom_strerror(ipcom_errno));
|
|
continue;
|
|
}
|
|
|
|
authmsg->len = ipcom_recv(radius_sock, authmsg->data, msglen, 0);
|
|
if (authmsg->len > 0)
|
|
{
|
|
/* Parse RADIUS input packet. */
|
|
for (svrIdx = 0; svrIdx < authServerCtrl.num_radius_servers; svrIdx++)
|
|
{
|
|
if(ipradius_read(authmsg->data, (Ip_size_t)authmsg->len,
|
|
&authServerCtrl.radius_server_addr[svrIdx].sa) == IPCOM_SUCCESS)
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DBG_PRINT("tDot1xRecv :: recv() failed: %s\n",
|
|
ipcom_strerror(ipcom_errno));
|
|
}
|
|
ipcom_free(authmsg);
|
|
}
|
|
}
|
|
#ifdef DOT1X_AS_DEBUG
|
|
/* Failure. */
|
|
DBG_PRINT("tDot1xRecv: failed, retval = %d", retval);
|
|
#endif
|
|
|
|
ipcom_proc_exit();
|
|
}
|