Files
m241Neelima/components/ip_net2-6.9/ipradius/src/ipradius.c
T
2026-07-30 11:19:44 +05:30

3435 lines
110 KiB
C

/*
******************************************************************************
* SOURCE FILE
*
* Document no: @(#) $Name: VXWORKS_ITER40_RCPL8_2024030303 $ $RCSfile: ipradius.c,v $ $Revision: 1.37.16.11 $
* $Source: /home/interpeak/CVSRoot/ipradius/src/ipradius.c,v $
* $Author: lwang7 $ $Date: 2022-09-01 02:40:15 $
* $State: Exp $ $Locker: $
*
* INTERPEAK_COPYRIGHT_STRING
* Design and implementation by Lennart Bang <lob@interpeak.se>
******************************************************************************
*/
/*
****************************************************************************
* 1 DESCRIPTION
****************************************************************************
*/
/*
****************************************************************************
* 2 CONFIGURATION
****************************************************************************
*/
#include "ipradius_config.h"
/*
****************************************************************************
* 3 INCLUDE FILES
****************************************************************************
*/
#define IPCOM_USE_ANDORNOT
#define IPCOM_USE_CLIB_PROTO
/* ipcom */
#include <ipcom_type.h>
#include <ipcom_cstyle.h>
#include <ipcom_clibmap.h>
#include <ipcom_clib.h>
#include <ipcom_list.h>
#include <ipcom_tags.h>
#include <ipcom_tmo.h>
#include <ipcom_err.h>
#include <ipcom_time.h>
#include <ipcom_os.h>
#include <ipcom_syslog.h>
#include <ipcom_sysvar.h>
#ifdef IPRADIUS_ENABLE_REENTRANT
#include <ipcom_os.h>
#endif
/* ipradius */
#include "ipradius.h"
/* IPCOM crypto */
#ifdef IPMCRYPTO
#include <ip/openssl/md5.h>
#else
#include <openssl/md5.h>
#endif
/*
****************************************************************************
* 4 EXTERN PROTOTYPES
****************************************************************************
*/
/*
****************************************************************************
* 5 DEFINES
****************************************************************************
*/
/* Syslog priority and facility. */
#define IPCOM_SYSLOG_PRIORITY IPRADIUS_SYSTEM_PRIORITY
#define IPCOM_SYSLOG_FACILITY IPCOM_LOG_IPRADIUS
#define IPRADIUS_LEAVE(xerrval) do { retval = xerrval; goto leave; } while(0)
#define IPRADIUS_ERROUT(xerrval) do { retval = xerrval; goto errout; } while(0)
#ifdef IPRADIUS_ENABLE_REENTRANT
#define IPRADIUS_LOCK() ipcom_mutex_lock(ipradius.code_mutex)
#define IPRADIUS_UNLOCK() ipcom_mutex_unlock(ipradius.code_mutex)
#else
#define IPRADIUS_LOCK() IP_NOOP
#define IPRADIUS_UNLOCK() IP_NOOP
#endif
/*
*===========================================================================
* SIZES
*===========================================================================
*/
#define IPRADIUS_AUTH_SIZE 16
#define IPRADIUS_CHAP_LEN 16
#define IPRADIUS_HIGHEST_ATTR_NO 240
/* Maximum RADIUS packet length. See RFC 2865, Chapter 3. */
#define IPRADIUS_MAX_PKT_SIZE 4096
/*
*===========================================================================
* IPRADIUS_ACCOUNT_PACKET
*===========================================================================
*/
#define IPRADIUS_ACCESS_PACKET(xxpkt) (NOT IPRADIUS_ACCOUNT_PACKET(xxpkt))
#define IPRADIUS_ACCOUNT_PACKET(xxpkt) \
((xxpkt)->code == IPRADIUS_CODE_Accounting_Request OR \
(xxpkt)->code == IPRADIUS_CODE_Accounting_Response)
/*
****************************************************************************
* 6 TYPES
****************************************************************************
*/
/*
****************************************************************************
* 7 LOCAL PROTOTYPES
****************************************************************************
*/
IP_PUBLIC Ip_err ipradius_create(void);
IP_PUBLIC Ip_err ipradius_start(void);
IP_PUBLIC const char *ipradius_version(void);
IP_STATIC Ip_bool ipradius_inaddr_equal(union Ip_sockaddr_union *addr1, union Ip_sockaddr_union *addr2);
IP_STATIC Ip_bool ipradius_server_equal(Ipradius_server *server1, Ipradius_server *server2);
IP_PUBLIC char *ipradius_addr_to_str(union Ip_sockaddr_union *addr);
IP_STATIC Ip_err ipradius_attr_check(Ipradius_header *hdr, Ipradius_request_entry *reqent, Ip_u8 *errattr);
IP_STATIC void ipradius_md5_encrypt (Ip_u8 *out, Ip_u8 *in, Ip_u32 length);
IP_STATIC Ipradius_session *ipradius_session_find(Ip_u32 unique);
IP_STATIC void ipradius_password_insert(Ipradius_request_entry *reqent);
IP_STATIC Ipradius_request_entry *ipradius_request_entry_find(Ipradius_header *hdr, union Ip_sockaddr_union *from_addr);
IP_STATIC Ip_err ipradius_request_packet_send(Ipradius_request_entry *reqent, Ip_bool start_timer);
IP_STATIC Ip_err
ipradius_packet_validate(union Ip_sockaddr_union *from_addr, Ipradius_header *response, Ipradius_request_entry **reqentp);
IP_STATIC Ip_err ipradius_packet_id_alloc(Ipradius_request_entry *reqent);
IP_STATIC void ipradius_packet_id_free(Ipradius_request_entry *reqent);
IP_STATIC void ipradius_set_server(Ipradius_request_entry *reqent);
IP_STATIC Ip_err ipradius_access_accept(Ipradius_header *hdr, Ipradius_request_entry *reqent);
IP_STATIC Ip_err ipradius_access_reject(Ipradius_header *hdr, Ipradius_request_entry *reqent);
IP_STATIC Ip_err ipradius_access_challenge(Ipradius_header *hdr, Ipradius_request_entry *reqent);
IP_STATIC Ip_err ipradius_accounting_response(Ipradius_header *hdr, Ipradius_request_entry *reqent);
IP_STATIC Ip_s32 ipradius_tick(Ipcom_tmo *tmo, void *cookie);
IP_STATIC Ip_err ipradius_accounting_request_create(Ip_u8 request_type, Ipradius_session *session, Ip_u32 acct_interim_interval, Ip_u32 terminate_cause);
IP_STATIC void ipradius_servent_free(Ipradius_server_entry *servent);
IP_STATIC void ipradius_reqent_free(Ipradius_request_entry *reqent);
IP_STATIC void ipradius_session_free(Ipradius_session *session);
/*
****************************************************************************
* 8 DATA
****************************************************************************
*/
/*
*===========================================================================
* Global IPRADIUS data
*===========================================================================
* All global IPRADIUS data is located in this structure.
*/
IP_GLOBAL Ipradius_data ipradius;
#define IPRADIUS_MAX_KNOWN_ATTRS 100
/* min length, max length, 0=don't care */
IP_STATIC Ip_u8 ipradius_attr_expected_len[(IPRADIUS_MAX_KNOWN_ATTRS+1) * 2]=
{
0,0, /* not used */
3,0, /* 1 User-Name */
18,130, /* 2 User-Password */
19,19, /* 3 CHAP-Password */
6,6, /* 4 NAS-IP-Address */
6,6, /* 5 NAS-Port */
6,6, /* 6 Service-Type */
6,6, /* 7 Framed-Protocol */
6,6, /* 8 Framed-IP-Address */
6,6, /* 9 Framed-IP-Netmask */
6,6, /* 10 Framed-Routing */
3,0, /* 11 Filter-Id */
6,6, /* 12 Framed-MTU */
6,6, /* 13 Framed-Compression */
6,6, /* 14 Login-IP-Host */
6,6, /* 15 Login-Service */
6,6, /* 16 Login-TCP-Port */
0,0, /* 17 */
3,0, /* 18 Reply-Message */
3,0, /* 19 Callback-Number */
3,0, /* 20 Callback-ID */
0,0, /* 21 */
3,0, /* 22 Framed-Route */
6,6, /* 23 Framed-IPX-Network */
3,0, /* 24 State */
3,0, /* 25 Class */
7,0, /* 26 Vendor-Specific */
6,6, /* 27 Session-Timeout */
6,6, /* 28 Idle-Timeout */
6,6, /* 29 Termination-Action */
3,0, /* 30 Called-Station-Id */
3,0, /* 31 Calling-Station-Id */
3,0, /* 32 NAS-Identifier */
3,0, /* 33 Proxy-State */
3,0, /* 34 Login-LAT-Service */
3,0, /* 35 Login-LAT-Node */
34,34, /* 36 Login-LAT-Group */
6,6, /* 37 Framed-Appletalk-Link */
6,6, /* 38 Framed-Appletalk-Network */
3,0, /* 39 Framed-Appletalk-Zone */
6,6, /* 40 Acct-Status-Type */
6,6, /* 41 Acct-Delay-Time */
6,6, /* 42 Acct-Input-Octets */
6,6, /* 43 Acct-Output-Octets */
3,0, /* 44 Acct-Session-Id */
6,6, /* 45 Acct-Authentic */
6,6, /* 46 Acct-Session-Time */
6,6, /* 47 Acct-Input-Packets */
6,6, /* 48 Acct-Output-Packets */
6,6, /* 49 Acct-Terminate-Cause */
3,0, /* 50 Acct-Multi-Session-Id */
6,6, /* 51 Acct-Link-Account */
6,6, /* 52 Acct-Input-Gigawords */
6,6, /* 53 Acct-Output-Gigawords */
0,0, /* 54 */
6,6, /* 55 Event-Timestamp */
0,0, /* 56 */
0,0, /* 57 */
0,0, /* 58 */
0,0, /* 59 */
7,0, /* 60 CHAP-Challenge */
6,6, /* 61 NAS-Port-Type */
6,6, /* 62 Port-Limit */
3,0, /* 63 Login-LAT-Port */
6,6, /* 64 Tunnel-Type */
6,6, /* 65 Tunnel-Medium-Type */
3,0, /* 66 Tunnel-Client-Endpoint */
3,0, /* 67 Tunnel-Server-Endpoint */
3,0, /* 68 Acct-Tunnel-Connection */
3,0, /* 69 Tunnel-Password */
18,18, /* 70 ARAP-Password */
16,16, /* 71 ARAP-Features */
6,6, /* 72 ARAP-Zone-Access */
6,6, /* 73 ARAP-Security */
3,0, /* 74 ARAP-Security-Data */
6,6, /* 75 Password-Retry */
6,6, /* 76 Prompt */
3,0, /* 77 Connect-Info */
3,0, /* 78 Configuration-Token */
3,0, /* 79 EAP-Message */
18,18, /* 80 Message-Authenticator */
3,0, /* 81 Tunnel-Private-Group-ID */
3,0, /* 82 Tunnel-Assignment-ID */
6,6, /* 83 Tunnel-Preference */
10,10, /* 84 ARAP-Challenge-Response */
6,6, /* 85 Acct-Interim-Interval */
6,6, /* 86 Acct-Tunnel-Packets-Lost */
3,0, /* 87 NAS-Port-Id */
3,0, /* 88 Framed-Pool */
0,0, /* 89 */
3,0, /* 90 Tunnel-Client-Auth-ID */
3,0, /* 91 Tunnel-Server-Auth-ID */
0,0, /* 92 */
0,0, /* 93 */
0,0, /* 94 */
18,18, /* 95 NAS_IPv6_Address */
10,10, /* 96 Framed_Interface_Id */
4,20, /* 97 Framed_IPv6_Prefix */
18,18, /* 98 Login_IPv6_Host */
3,0, /* 99 Framed_IPv6_Route */
3,0 /* 100 Framed_IPv6_Pool */
};
/*
****************************************************************************
* 9 LOCAL FUNCTIONS
****************************************************************************
*/
/*
*===========================================================================
* ipradius_inaddr_equal
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC Ip_bool
ipradius_inaddr_equal(union Ip_sockaddr_union *addr1, union Ip_sockaddr_union *addr2)
{
if (addr1->sa.sa_family != addr2->sa.sa_family)
return IP_FALSE;
if (addr1->sin.sin_port != addr2->sin.sin_port)
return IP_FALSE;
switch(addr1->sa.sa_family)
{
#ifdef IPCOM_USE_INET
case IP_AF_INET :
if (addr1->sin.sin_addr.s_addr != addr2->sin.sin_addr.s_addr)
return IP_FALSE;
return IP_TRUE;
#endif
#ifdef IPCOM_USE_INET6
case IP_AF_INET6 :
if (ipcom_memcmp(&addr1->sin6.sin6_addr, &addr2->sin6.sin6_addr, sizeof(struct Ip_in6_addr)))
return IP_FALSE;
return IP_TRUE;
#endif
default:
return IP_FALSE;
}
}
/*
*===========================================================================
* ipradius_server_equal
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC Ip_bool
ipradius_server_equal(Ipradius_server *server1, Ipradius_server *server2)
{
if (server1->index AND server1->index == server2->index)
return IP_TRUE;
if (NOT ipradius_inaddr_equal(&server1->server_addr, &server2->server_addr))
return IP_FALSE;
if (server1->server_type != server2->server_type)
return IP_FALSE;
if (server1->pollnum != server2->pollnum)
return IP_FALSE;
if (server1->pollsec != server2->pollsec)
return IP_FALSE;
return IP_TRUE;
}
/*
*===========================================================================
* ipradius_addr_to_str
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_PUBLIC char *
ipradius_addr_to_str(union Ip_sockaddr_union *addr)
{
switch(addr->sa.sa_family)
{
#ifdef IPCOM_USE_INET
case IP_AF_INET :
(void)ipcom_inet_ntop(IP_AF_INET, &addr->sin.sin_addr, ipradius.buf, sizeof(ipradius.buf));
break;
#endif
#ifdef IPCOM_USE_INET6
case IP_AF_INET6 :
(void)ipcom_inet_ntop(IP_AF_INET6, &addr->sin6.sin6_addr, ipradius.buf, sizeof(ipradius.buf));
break;
#endif
default:
ipcom_strcpy(ipradius.buf, "<unknown sa_family>");
break;
}
return ipradius.buf;
}
/*
*===========================================================================
* ipradius_get_lexicographic_server
*===========================================================================
* Description: Get next server in index order.
* Parameters: index 0 for first server else IP index of last server.
* Returns: Pointer to a server entry.
*
*/
IP_STATIC Ipradius_server_entry *
ipradius_get_lexicographic_server(Ip_u32 indx)
{
Ipradius_server_entry *servent, *best;
best = IP_NULL;
for (servent = IPCOM_LIST_FIRST(&ipradius.server_head);
servent;
servent = IPCOM_LIST_NEXT(&servent->server_list))
{
/* Get next server with lowest IP address. */
if ((indx == 0 OR servent->server.index > indx) AND
(best == IP_NULL OR servent->server.index < best->server.index))
{
best = servent;
}
}
return best;
}
/*
*===========================================================================
* ipradius_reqent_free
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC void
ipradius_reqent_free(Ipradius_request_entry *reqent)
{
/* Return ID to its server. */
ipradius_packet_id_free(reqent);
/* Remove from list and free reqent */
ipcom_list_remove(&reqent->request_list);
ipcom_free(reqent);
ip_assert(ipradius.mem.requests > 0);
ipradius.mem.requests--;
}
/*
*===========================================================================
* ipradius_session_free
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC void
ipradius_session_free(Ipradius_session *session)
{
ip_assert(ipradius.mem.sessions > 0);
ipradius.mem.sessions--;
ipcom_list_remove(&session->session_list);
ipcom_free(session);
}
/*
*===========================================================================
* ipradius_servent_free
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC void
ipradius_servent_free(Ipradius_server_entry *servent)
{
Ipradius_request_entry *request;
Ipradius_session *session;
/* Deleting a server, it may be in use, clean up some. */
/* first clear it from any request that is currently using it. */
for (request = IPCOM_LIST_FIRST(&ipradius.request_head); request; request = IPCOM_LIST_NEXT(&request->request_list))
if (request->servent == servent)
request->servent = IP_NULL;
/* then clear it from sessions that cached it. */
for (session = IPCOM_LIST_FIRST(&ipradius.session_head); session; session = IPCOM_LIST_NEXT(&session->session_list))
if (session->servent == servent)
session->servent = IP_NULL;
/* Remove packet holder. */
servent->pkt_ids->ref_count--;
ip_assert(servent->pkt_ids->ref_count >= 0);
if (servent->pkt_ids->ref_count == 0)
{
ipradius.mem.id_holders--;
ipcom_free(servent->pkt_ids);
}
/* finally remove the server. */
ipradius.server_num--;
ipcom_list_remove(&servent->server_list);
ipcom_free(servent);
ip_assert(ipradius.mem.servers > 0);
ipradius.mem.servers--;
}
/*
*===========================================================================
* ipradius_packet_id_alloc
*===========================================================================
* Description: Insert into request entry one packet ID that is not currently
* in use by this server.
*
* Parameters:
* Returns:
*
*/
IP_STATIC Ip_err
ipradius_packet_id_alloc(Ipradius_request_entry *reqent)
{
Ipradius_id *ids;
ip_assert(reqent->num_ids == 0);
ip_assert(reqent->servent);
switch(reqent->hdr.code)
{
case IPRADIUS_CODE_Access_Request:
ids = &reqent->servent->pkt_ids->access_ids;
break;
case IPRADIUS_CODE_Accounting_Request:
ids = &reqent->servent->pkt_ids->account_ids;
break;
case IPRADIUS_CODE_Access_Challenge:
default:
IP_PANIC();
return IPCOM_ERR_FAILED;
}
ip_assert(ids->num <= 256); /*lint !e644 */
if (ids->num == 256)
return IPCOM_ERR_FAILED;
reqent->hdr.id = ids->ids[ids->get++];
ids->num++;
ipradius.mem.ids++;
reqent->num_ids++;
IPCOM_LOG2(DEBUG2, "+++ getting id=%d (total =%d)", reqent->hdr.id, ids->num);
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipradius_packet_id_free
*===========================================================================
* Description: Return packet id to its server.
* Parameters:
* Returns:
*
*/
IP_STATIC void
ipradius_packet_id_free(Ipradius_request_entry *reqent)
{
Ipradius_id *ids;
ip_assert(reqent->num_ids >= 0);
/* No ID to return, ignore. */
if (reqent->num_ids == 0)
return;
ip_assert(reqent->num_ids == 1);
/* No server to return ID to, ignore. Still need to update counters though. */
if (reqent->servent == IP_NULL)
{
ip_assert(ipradius.mem.ids > 0);
ipradius.mem.ids--;
reqent->num_ids--;
return;
}
switch(reqent->hdr.code)
{
case IPRADIUS_CODE_Access_Request:
ids = &reqent->servent->pkt_ids->access_ids;
break;
case IPRADIUS_CODE_Accounting_Request:
ids = &reqent->servent->pkt_ids->account_ids;
break;
case IPRADIUS_CODE_Access_Challenge:
default:
IP_PANIC();
return;
}
ip_assert(ipradius.mem.ids > 0);
ipradius.mem.ids--;
ip_assert(ids->num > 0); /*lint !e644 */
ids->ids[ids->put] = reqent->hdr.id;
ids->put++;
ids->num--;
reqent->num_ids--;
IPCOM_LOG2(DEBUG2, "+++ returning id=%d (total =%d)", reqent->hdr.id, ids->num);
}
/*
*===========================================================================
* ipradius_set_server
*===========================================================================
* Description: Pick a server to request service from. The server shall match
* the user requested access and/or accounting services and not previously
* refused access.
*
* If the entry already had a server and a packet ID in use for that server,
* free the packet ID for that server.
*
* Parameters:
* Returns:
*
*/
IP_STATIC void
ipradius_set_server(Ipradius_request_entry *reqent)
{
Ip_u32 i;
reqent->sec_since_poll = 0;
reqent->servers_tried++;
reqent->pollnum = 0;
/* Return ID in case we change server. */
ipradius_packet_id_free(reqent);
if (reqent->server_fixed == IP_TRUE)
{
IPCOM_LOG1(DEBUG2, "+++ keeping fixed server %s",
reqent->servent ?
ipradius_addr_to_str(&reqent->servent->server.server_addr) : "NULL");
return;
}
/* specified server choice. */
if (reqent->request.num_servers >= reqent->servers_tried)
{
Ipradius_server_entry *servent;
/* Look for next desired server in the server list. */
for (servent = IPCOM_LIST_FIRST(&ipradius.server_head); servent; servent = IPCOM_LIST_NEXT(&servent->server_list))
if (ipradius_server_equal(&servent->server,
(Ipradius_server *)&reqent->request.serv_list[reqent->servers_tried-1]))
{
/* Found a server match, use it. */
reqent->servent = servent;
return;
}
}
/* The user has asked for a specific number of servers and if we have reached
* this place, all have been tried.
*/
if (reqent->request.num_servers != 0)
{
/* Clear server entry pointer.*/
reqent->servent = IP_NULL;
return;
}
/* Default server choice, pick next one in list. */
for (i = 0; i < ipradius.server_num; i++)
{
/* Pick up the first server in the server list at the first time */
/* Traverse the server list afterwards */
if (!reqent->servent)
reqent->servent = IPCOM_LIST_FIRST(&ipradius.server_head);
else
reqent->servent = IPCOM_LIST_NEXT(&reqent->servent->server_list);
/* If no more server found, return NULL */
if (!reqent->servent)
return;
/* Make sure our request supports the domain. */
#ifdef IPCOM_USE_INET
if (reqent->request.fd == IP_INVALID_SOCKET &&
reqent->servent->server.server_addr.sa.sa_family == IP_AF_INET)
continue;
#endif
#ifdef IPCOM_USE_INET6
if (reqent->request.fd6 == IP_INVALID_SOCKET &&
reqent->servent->server.server_addr.sa.sa_family == IP_AF_INET6)
continue;
#endif
/* Server entry of both types, pick this one. */
if (reqent->servent->server.server_type == IPRADIUS_FULL_SERVER)
return;
/* Use entry if server of the correct type. */
if (((reqent->servent->server.server_type == IPRADIUS_ACCESS_SERVER) &&
(reqent->hdr.code == IPRADIUS_CODE_Access_Request))
||
((reqent->servent->server.server_type == IPRADIUS_ACCOUNTING_SERVER) &&
(reqent->hdr.code == IPRADIUS_CODE_Accounting_Request)))
return;
}
/* none found, clear server entry pointer. */
reqent->servent = IP_NULL;
}
/*
*===========================================================================
* ipradius_attr_check
*===========================================================================
* Description: Check that all attributes in the packet have the correct
* length and a legal type.
*
* Parameters:
* Returns:
*
*/
IP_STATIC Ip_err
ipradius_attr_check(Ipradius_header *hdr, Ipradius_request_entry *reqent, Ip_u8 *errattr)
{
Ip_u8 *p = hdr->attr;
Ip_u8 *endp = &hdr->attr[ip_ntohs(hdr->length) - IPRADIUS_HDRSIZE];
Ip_u8 type = 0, minlen, maxlen;
while(p < endp)
{
type = *p++;
if (!type || type > IPRADIUS_HIGHEST_ATTR_NO)
{
IPCOM_LOG1(NOTICE, "ipradius_attr_check() :: found an attribute with a bad type: %d", type);
goto errout;
}
/*ipcom_debug_printf("attr = %2d, len = %3d"IP_LF, (int)type, (int)*p);*/
if (type <= IPRADIUS_MAX_KNOWN_ATTRS)
{
minlen = ipradius_attr_expected_len[2*type];
maxlen = ipradius_attr_expected_len[(2*type)+1];
if (minlen == 0)
{
minlen = 2; /* at least include type and length */
}
if (maxlen == 0) /* 0 => any length is OK */
{
maxlen = 255;
}
if ((*p < minlen) || (*p > maxlen))
{
IPCOM_LOG2(NOTICE, "ipradius_attr_check() :: Found an attribute with a bad length = %d, type = %d", *p, type);
errout:
if (errattr)
*errattr = type;
if (reqent->servent)
{
if (IPRADIUS_ACCESS_PACKET(&reqent->hdr))
reqent->servent->AuthMalformedAccessResponses++;
else
reqent->servent->AccMalformedResponses++;
}
IPCOM_LOG3(NOTICE, "ipradius_attr_check() :: illegal attribute type (%d) or length (%d) from server %s",
type, *p,
reqent->servent ?
ipradius_addr_to_str(&reqent->servent->server.server_addr) : "NULL");
return IPRADIUS_ERR_BAD_ATTRIBUTE;
}
}
else
{
if (*p < 2)
{
IPCOM_LOG2(NOTICE, "ipradius_attr_check() :: Found an unsupported attribute type (%d) with bad length (%d)", type, *p);
goto errout;
}
}
p += *p - 1;
}
if (p != endp)
{
IPCOM_LOG0(NOTICE, "ipradius_attr_check() :: end of packet is not good");
goto errout;
}
/*IPCOM_LOG0(DEBUG, "ipradius_attr_check() :: found packet attributes to be good");*/
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipradius_access_accept
*===========================================================================
* Description: Called when an access accept packet is received.
* Parameters:
* Returns:
*
*/
IP_STATIC Ip_err
ipradius_access_accept(Ipradius_header *hdr, Ipradius_request_entry *reqent)
{
reqent->request.status = IPCOM_SUCCESS;
/* Notify user of access accept packets. */
(void)reqent->request.event_callback(IPRADIUS_EVENT_ACCESS_ACCEPT,
&reqent->request, &reqent->hdr, hdr, &reqent->servent->server);
/* free reqent. */
ipradius_reqent_free(reqent);
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipradius_access_reject
*===========================================================================
* Description: Report reject to user and remove request entry.
*
* Parameters:
* Returns:
*
*/
IP_STATIC Ip_err
ipradius_access_reject(Ipradius_header *hdr, Ipradius_request_entry *reqent)
{
reqent->request.status = IPCOM_ERR_FAILED;
/* Notify user of access reject packets. */
(void)reqent->request.event_callback(IPRADIUS_EVENT_ACCESS_REJECT,
&reqent->request, &reqent->hdr, hdr, &reqent->servent->server);
/* free reqent. */
ipradius_reqent_free(reqent);
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipradius_access_challenge
*===========================================================================
* Description: Called when an access challenge packet is received.
* Parameters:
* Returns:
*
*/
IP_STATIC Ip_err
ipradius_access_challenge(Ipradius_header *hdr, Ipradius_request_entry *reqent)
{
reqent->request.status = IPCOM_SUCCESS;
/* Notify user of access challenge packets. */
(void)reqent->request.event_callback(IPRADIUS_EVENT_ACCESS_CHALLENGE,
&reqent->request, &reqent->hdr, hdr, &reqent->servent->server);
/* free reqent. */
ipradius_reqent_free(reqent);
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipradius_accounting_response
*===========================================================================
* Description: Call user callback.
* Remove request entry.
*
* Parameters:
* Returns:
*
*/
IP_STATIC Ip_err
ipradius_accounting_response(Ipradius_header *hdr, Ipradius_request_entry *reqent)
{
reqent->request.status = IPCOM_SUCCESS;
/* Notify user of accounting response packets. */
(void)reqent->request.event_callback(IPRADIUS_EVENT_ACCOUNTING_RESPONSE,
&reqent->request, &reqent->hdr, hdr, &reqent->servent->server);
/* Clear pending request and set start_timer (if session still exists). */
if (reqent->session != IP_NULL)
{
Ip_u32 type;
if (ipradius_attr_get_u32(&reqent->hdr, IPRADIUS_ATTR_Acct_Status_Type, &type) == IPCOM_SUCCESS)
{
if (type == IPRADIUS_ACCT_Start)
{
reqent->session->start_time = ipcom_time(0);
ip_assert(reqent->session->start_time != 0);
}
}
else
{
IP_PANIC();
}
/* Remember the server which initially replied. */
if (ipradius.same_acct_server == IP_TRUE AND reqent->session->server_fixed == IP_FALSE)
{
reqent->session->servent = reqent->servent;
reqent->session->server_fixed = IP_TRUE;
}
/* No request pending. */
reqent->session->request_pending = IP_FALSE;
}
/* free reqent. */
ipradius_reqent_free(reqent);
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipradius_md5_encrypt
*===========================================================================
* Description: Encrypt a buffer to MD5 encrypted format and put result in
* another buffer.
*
* Parameters:
* Returns:
*
*/
IP_STATIC void
ipradius_md5_encrypt (Ip_u8 *out, Ip_u8 *in, Ip_u32 length)
{
MD5_CTX ctx;
#ifdef OPENSSL_FIPS
/* MD5 cannot be used */
if (FIPS_mode())
{
/* Authentication failed. */
IPCOM_LOG0(WARNING, "ipradius: MD5 is not supported when FIPS mode is enabled!");
(void)ipcom_errno_set(IPCOM_ERR_AUTH_FAILED);
return;
}
#endif
(void)MD5_Init(&ctx);
(void)MD5_Update(&ctx, in, length);
(void)MD5_Final(out, &ctx);
}
/*
*===========================================================================
* ipradius_hmac_md5
*===========================================================================
* Description: Create a md5 hmac of key and data
* Parameters:
* Returns:
*
*/
IP_STATIC void
ipradius_hmac_md5(Ip_u8 *key, Ip_u32 key_len, Ip_u8 *data, Ip_u32 len, Ip_u8 *mac)
{
MD5_CTX context;
Ip_u8 k_ipad[65];
Ip_u8 k_opad[65];
Ip_u8 tk[16];
int i;
#ifdef OPENSSL_FIPS
/* MD5 cannot be used */
if (FIPS_mode())
{
/* Authentication failed. */
IPCOM_LOG0(WARNING, "ipradius: MD5 is not supported when FIPS mode is enabled!");
(void)ipcom_errno_set(IPCOM_ERR_AUTH_FAILED);
return;
}
#endif
/* Key longer than 64 bytes reset it to key = MD5(key) */
if (key_len > 64)
{
MD5_Init(&context);
MD5_Update(&context, key, key_len);
MD5_Final(tk, &context);
key = tk;
key_len = 16;
}
/* Start storing key in pads */
ipcom_memset(k_ipad, 0, sizeof(k_ipad));
ipcom_memset(k_opad, 0, sizeof(k_opad));
ipcom_memcpy(k_ipad, key, key_len);
ipcom_memcpy(k_opad, key, key_len);
/* XOR key with ipad and opad values */
for (i = 0; i < 64; i++)
{
k_ipad[i] ^= 0x36;
k_opad[i] ^= 0x5c;
}
/* Do inner MD5 */
MD5_Init(&context);
MD5_Update(&context, k_ipad, 64);
MD5_Update(&context, data, len);
MD5_Final(mac, &context);
/* Do outer MD5 */
MD5_Init(&context);
MD5_Update(&context, k_opad, 64);
MD5_Update(&context, mac, 16);
MD5_Final(mac, &context);
}
/*
*===========================================================================
* ipradius_session_find
*===========================================================================
* Description: Go through session list to find a session with matching
* unique value.
*
* Parameters:
* Returns: found entry or IP_NULL
*
*/
IP_STATIC Ipradius_session *
ipradius_session_find(Ip_u32 unique)
{
Ipradius_session *session, *nextsession;
for (session = IPCOM_LIST_FIRST(&ipradius.session_head); session; session = nextsession)
{
nextsession = IPCOM_LIST_NEXT(&session->session_list);
if (session->request.unique == unique)
return session;
}
return IP_NULL;
}
/*
*===========================================================================
* ipradius_request_entry_find
*===========================================================================
* Description: Go through request list to find an entry with matching request
* type (access, accounting) and ID.
*
* Parameters:
* Returns: found entry or IP_NULL
*
*/
IP_STATIC Ipradius_request_entry *
ipradius_request_entry_find(Ipradius_header *hdr, union Ip_sockaddr_union *from_addr)
{
Ipradius_request_entry *reqent;
IPCOM_LOG3(DEBUG2, "Looking for id: %d, from: %s, response code: %d",
hdr->id, ipradius_addr_to_str(from_addr), hdr->code);
for (reqent = IPCOM_LIST_FIRST(&ipradius.request_head);
reqent != IP_NULL;
reqent = IPCOM_LIST_NEXT(&reqent->request_list))
{
/* Find a request entry with same ID that was sent to this server. */
if (reqent->hdr.id == hdr->id AND
reqent->servent != IP_NULL AND
ipradius_inaddr_equal(&reqent->servent->server.server_addr, from_addr))
{
if (((reqent->hdr.code == IPRADIUS_CODE_Access_Request) && (hdr->code == IPRADIUS_CODE_Access_Accept))||
((reqent->hdr.code == IPRADIUS_CODE_Access_Request) && (hdr->code == IPRADIUS_CODE_Access_Reject))||
((reqent->hdr.code == IPRADIUS_CODE_Access_Request) && (hdr->code == IPRADIUS_CODE_Access_Challenge))||
((reqent->hdr.code == IPRADIUS_CODE_Accounting_Request) && (hdr->code == IPRADIUS_CODE_Accounting_Response)))
{
IPCOM_LOG4(DEBUG2, "Match for id: %d, from: %s, request code: %d, response code: %d",
hdr->id,
ipradius_addr_to_str(from_addr),
reqent->hdr.code, hdr->code);
return reqent;
}
else
{
IPCOM_LOG4(DEBUG2, "Skipping id: %d, from: %s, request code: %d, response code: %d",
reqent->hdr.id,
ipradius_addr_to_str(from_addr),
reqent->hdr.code, hdr->code);
}
}
else
{
IPCOM_LOG3(DEBUG2, "Skipping id: %d, server: %s, request code: %d",
reqent->hdr.id,
reqent->servent ? ipradius_addr_to_str(&reqent->servent->server.server_addr) : "<none>",
reqent->hdr.code);
}
}
IPCOM_LOG3(DEBUG2, "Failed to match id: %d, from: %s, response code: %d",
hdr->id, ipradius_addr_to_str(from_addr), hdr->code);
return IP_NULL;
}
/*
*===========================================================================
* ipradius_packet_validate
*===========================================================================
* Description: Find an outstanding request that matches the response and
* check that the response authentication is valid.
*
* Parameters:
* Returns: Reason if there was no matching entry or response was invalid,
* otherwise IPCOM_SUCCESS.
*
*/
IP_STATIC Ip_err
ipradius_packet_validate(union Ip_sockaddr_union *from_addr, Ipradius_header *response, Ipradius_request_entry **reqentp)
{
Ip_u8 expected_digest[IPRADIUS_AUTH_SIZE];
Ip_u8 response_digest[IPRADIUS_AUTH_SIZE];
Ipradius_request_entry *reqent;
MD5_CTX ctx;
/* Clear request pointer. */
*reqentp = IP_NULL;
/***** Find the request entry. *****/
reqent = ipradius_request_entry_find(response, from_addr);
if (reqent == IP_NULL)
{
#if IPRADIUS_SYSTEM_PRIORITY >= IPCOM_LOG_DEBUG
const char *code_text;
switch(response->code)
{
case IPRADIUS_CODE_Access_Accept :
code_text = "Access-Accept";
break;
case IPRADIUS_CODE_Access_Reject :
code_text = "Access-Reject";
break;
case IPRADIUS_CODE_Accounting_Response :
code_text = "Accounting-Response";
break;
case IPRADIUS_CODE_Access_Challenge :
code_text = "Access-Challenge";
break;
default:
code_text = "Unknown Code";
break;
}
IPCOM_LOG3(DEBUG,
"ipradius_packet_validate() :: No %s request entry found for ID (%d) from %s",
code_text, response->id,
ipradius_addr_to_str(from_addr));
(void)code_text;
#endif /* IPRADIUS_SYSTEM_PRIORITY >= IPCOM_LOG_DEBUG */
return IPRADIUS_ERR_DISCARDED_PACKET;
}
ip_assert(reqent->servent != IP_NULL);
/***** Validate authentication in the response *****/
/* save copy of authentication in response */
ipcom_memcpy(response_digest, response->auth, IPRADIUS_AUTH_SIZE);
/* replace authentication in response with authentication from request */
ipcom_memcpy(response->auth, reqent->hdr.auth, IPRADIUS_AUTH_SIZE);
#ifdef OPENSSL_FIPS
/* MD5 cannot be used */
if (FIPS_mode())
{
/* Authentication failed. */
IPCOM_LOG0(WARNING, "ipradius: MD5 is not supported when FIPS mode is enabled!");
return IPCOM_ERR_AUTH_FAILED;
}
#endif
(void)MD5_Init(&ctx);
(void)MD5_Update(&ctx, &response->code, ip_ntohs(response->length));
(void)MD5_Update(&ctx, reqent->servent->server.shared_secret, reqent->servent->server.shared_secret_len);
(void)MD5_Final(expected_digest, &ctx);
if (ipcom_memcmp(response_digest, expected_digest, IPRADIUS_AUTH_SIZE) != 0)
{
IPCOM_LOG2(NOTICE, "ipradius_packet_validate() :: bad authentication ID (%d) in reply from RADIUS server %s",
reqent->hdr.id, ipradius_addr_to_str(&reqent->servent->server.server_addr));
if (IPRADIUS_ACCESS_PACKET(&reqent->hdr))
{
reqent->servent->AuthPacketsDropped++;
reqent->servent->AuthBadAuthenticators++;
}
else
{
reqent->servent->AccPacketsDropped++;
reqent->servent->AccBadAuthenticators++;
}
return IPRADIUS_ERR_BAD_AUTHENTICATION;
}
/* Valid response, remember it. */
*reqentp = reqent;
/* Check the response attributes. */
return ipradius_attr_check(response, reqent, IP_NULL);
}
/*
*===========================================================================
* ipradius_password_insert
*===========================================================================
* Description: If the user has requested a PAP or CHAP password, encrypt
* the password. If there is already a password attribute
* in the packet, replace it, otherwise add this attribute.
*
* Note that authentication in the packet must already be
* correct, that there must not be more than one password
* attribute and that the length of the password can not
* be different from that in an existing attribute.
*
* Parameters:
* Returns:
*
*/
IP_STATIC void
ipradius_password_insert(Ipradius_request_entry *reqent)
{
Ip_u8 *attrp;
Ip_u8 len;
Ip_u8 attr_pwd_len;
Ip_u8 pwd[IPRADIUS_MAX_PASSWORD_SIZE];
char plain[IPRADIUS_MAX_SECRET_SIZE+IPRADIUS_AUTH_SIZE];
Ip_u8 crypt[IPRADIUS_MAX_SECRET_SIZE+IPRADIUS_AUTH_SIZE];
int i,j;
attr_pwd_len = 0;
switch(reqent->request.pwd_type)
{
case IPRADIUS_PAP_Password :
ip_assert(reqent->request.pwd_len <= IPRADIUS_MAX_PASSWORD_SIZE);
len = (Ip_u8)((Ip_u8)(reqent->request.pwd_len+(IPRADIUS_AUTH_SIZE-1)) & ~(IPRADIUS_AUTH_SIZE-1));
ip_assert(len > 0);
attrp = ipradius_attr_find(&reqent->hdr, IPRADIUS_ATTR_User_Password, &attr_pwd_len);
if (attrp == IP_NULL)
{
/* no password attribute existed, add it. */
/*
* Check the length of the resulting packet:
* Existing packet length (reqent->hdr.length)
* + length of attribute value (len)
* + 2 (for attribute type and length fileds)
*/
if (ip_ntohs(reqent->hdr.length) + len + 2 > reqent->pkt_length)
{
IPCOM_LOG0(ERR, "Exceeded size limit of access request attribute field");
break;
}
attrp = &reqent->hdr.attr[ip_ntohs(reqent->hdr.length) - IPRADIUS_HDRSIZE];
*attrp++ = IPRADIUS_ATTR_User_Password;
*attrp++ = (Ip_u8)(len + 2);
reqent->hdr.length = ip_ntohs(reqent->hdr.length);
reqent->hdr.length += (Ip_u16)(len + 2);
reqent->hdr.length = ip_htons(reqent->hdr.length);
IPCOM_LOG1(DEBUG, "ipradius_password_insert() :: [add] server = %s",
ipradius_addr_to_str(&reqent->servent->server.server_addr));
}
else
IPCOM_LOG1(DEBUG, "ipradius_password_insert() :: [update] server = %s",
ipradius_addr_to_str(&reqent->servent->server.server_addr));
/* pad password with zeroes to nearest 16-byte boundary */
ipcom_memset(pwd, '\0', sizeof(pwd));
ipcom_memcpy(pwd, reqent->request.pwd, reqent->request.pwd_len);
/* create encrypted password */
for (i = 0; i < len; i += IPRADIUS_AUTH_SIZE) /*lint !e644 */
{
/* data to encrypt = secret + request authentication */
ipcom_strcpy(plain, (char *)reqent->servent->server.shared_secret);
if (i == 0)
ipcom_memcpy(plain + reqent->servent->server.shared_secret_len,
reqent->hdr.auth, IPRADIUS_AUTH_SIZE);
else
ipcom_memcpy(plain + reqent->servent->server.shared_secret_len,
attrp - IPRADIUS_AUTH_SIZE, IPRADIUS_AUTH_SIZE);
/* encrypt data into attribute */
ipradius_md5_encrypt(crypt, (Ip_u8*)plain, reqent->servent->server.shared_secret_len + IPRADIUS_AUTH_SIZE);
ip_assert(attrp);
ipcom_memcpy(attrp, crypt, IPRADIUS_AUTH_SIZE);
/* Xor the password onto the encrypted data in the attribute */
for (j = i; j < (i + IPRADIUS_AUTH_SIZE); j++)
{
*attrp++ ^= pwd[j];
}
}
break;
case IPRADIUS_CHAP_Password :
{
Ip_u8 flen;
if (ipradius_attr_find(&reqent->hdr, IPRADIUS_ATTR_Framed_Protocol, &flen) == IP_NULL)
(void)ipradius_attr_add_u32(&reqent->hdr, IPRADIUS_ATTR_Framed_Protocol, 1 /* PPP */);
if (ipradius_attr_find(&reqent->hdr, IPRADIUS_ATTR_Service_Type, &flen) == IP_NULL)
(void)ipradius_attr_add_u32(&reqent->hdr, IPRADIUS_ATTR_Service_Type, 2 /* Framed */);
if (ipradius_attr_find(&reqent->hdr, IPRADIUS_ATTR_CHAP_Password, &attr_pwd_len) == 0)
{
MD5_CTX ctx;
char *challenge_attrp;
Ip_u8 challenge_len;
/* no password attribute existed, add it. */
/*
* Check the length of the resulting packet:
* Existing packet length (reqent->hdr.length)
* + length of CHAP String (IPRADIUS_CHAP_LEN)
* + 3 (for attribute type, length and CHAP Identifier fileds)
*/
if (ip_ntohs(reqent->hdr.length) + IPRADIUS_CHAP_LEN + 3 > reqent->pkt_length)
{
IPCOM_LOG0(ERR, "Exceeded size limit of access request attribute field");
break;
}
attrp = &reqent->hdr.attr[ip_ntohs(reqent->hdr.length) - IPRADIUS_HDRSIZE];
*attrp++ = IPRADIUS_ATTR_CHAP_Password;
*attrp++ = IPRADIUS_CHAP_LEN + 3;
*attrp++ = (Ip_u8)reqent->request.chap_ident;
reqent->hdr.length = ip_ntohs(reqent->hdr.length);
reqent->hdr.length += (Ip_u16)(IPRADIUS_CHAP_LEN + 3);
reqent->hdr.length = ip_htons(reqent->hdr.length);
/* Get challenge. */
challenge_attrp = ipradius_attr_find(&reqent->hdr, IPRADIUS_ATTR_CHAP_Challenge,
&challenge_len);
ip_assert(challenge_attrp);
ip_assert(challenge_len >= 7);
#ifdef OPENSSL_FIPS
/* MD5 cannot be used */
if (FIPS_mode())
{
/* Authentication failed. */
IPCOM_LOG0(WARNING, "ipradius: MD5 is not supported when FIPS mode is enabled!");
(void)ipcom_errno_set(IPCOM_ERR_AUTH_FAILED);
return;
}
#endif
/* CHAP - calculate MD5 sum over CHAP-ID,plain-text password, and the Chap-Challenge. */
(void)MD5_Init(&ctx);
(void)MD5_Update(&ctx, &reqent->request.chap_ident, 1);
(void)MD5_Update(&ctx, (Ip_u8 *)reqent->request.pwd, reqent->request.pwd_len);
(void)MD5_Update(&ctx, (Ip_u8 *)challenge_attrp, (Ip_u32)(challenge_len - 2));
(void)MD5_Final(crypt, &ctx);
ipcom_memcpy (attrp, crypt, IPRADIUS_CHAP_LEN);
}
}
break;
case IPRADIUS_No_Password :
break;
default:
IP_PANIC();
break;
}
}
/*
*===========================================================================
* ipradius_tick
*===========================================================================
* Description: Called at certain intervals by a timer from the local operating
* system.
* Parameters:
* Returns:
*
*/
IP_STATIC Ip_s32
ipradius_tick(Ipcom_tmo *tmo, void *cookie)
{
Ipradius_request_entry *reqent, *nextentry;
Ipradius_session *session;
(void)tmo;
(void)cookie;
IPRADIUS_LOCK();
ipradius.tick++;
/*IPCOM_LOG1(DEBUG, "ipradius_tick() :: TICK %ld", ipcom_times(0));*/
/* Go through list of outstanding requests for service.
* Send request to another server if timeout
*/
for (reqent = IPCOM_LIST_FIRST(&ipradius.request_head); reqent; reqent = nextentry)
{
Ip_u32 poll_delay;
nextentry = IPCOM_LIST_NEXT(&reqent->request_list);
/* get a server if the old one disappeared */
if (reqent->servent == IP_NULL)
{
IPCOM_LOG0(DEBUG, "ipradius_tick() :: no request server");
ipradius_set_server(reqent);
/* no more servers to request service from */
if (reqent->servent == IP_NULL)
{
IPCOM_LOG0(DEBUG, "ipradius_tick() :: no server found, deleting session and request");
reqent->request.status = IPRADIUS_ERR_NO_SERVER_RESPONSE;
(void)reqent->request.event_callback(reqent->hdr.code == IPRADIUS_CODE_Accounting_Request ?
IPRADIUS_EVENT_ACCOUNTING_TIMEOUT :
IPRADIUS_EVENT_ACCESS_TIMEOUT,
&reqent->request, &reqent->hdr, IP_NULL, IP_NULL);
if (reqent->session)
ipradius_session_free(reqent->session);
/* free reqent. */
ipradius_reqent_free(reqent);
continue;
}
}
/* Calculate poll delay. */
poll_delay = reqent->servent->server.pollsec;
if (!poll_delay || (poll_delay > IPRADIUS_MAX_POLL_DELAY))
poll_delay = IPRADIUS_MAX_POLL_DELAY;
/* Send the request if we have waited long enough since last poll */
reqent->sec_since_poll++;
if (reqent->sec_since_poll >= poll_delay)
{
reqent->sec_since_poll = 0;
/* Check if we have polled this server enough and need to switch. */
if (reqent->pollnum >= reqent->servent->server.pollnum)
{
/* update MIB counters */
if (IPRADIUS_ACCESS_PACKET(&reqent->hdr))
reqent->servent->AuthTimeouts++;
else
reqent->servent->AccTimeouts++;
/* switch server. */
ipradius_set_server(reqent);
}
else
{
if (IPRADIUS_ACCESS_PACKET(&reqent->hdr))
reqent->servent->AuthAccessRetransmissions++;
else
reqent->servent->AccRetransmissions++;
}
/* Send the request to the RADIUS server if not exceeded the number of servers. */
if (reqent->servers_tried > ipradius.server_num OR
ipradius_request_packet_send(reqent, IP_FALSE) == IPRADIUS_ERR_NO_SERVER)
{
IPCOM_LOG0(DEBUG, "ipradius_tick() :: no more servers to poll, deleting session and request");
reqent->request.status = IPRADIUS_ERR_NO_SERVER_RESPONSE;
(void)reqent->request.event_callback(reqent->hdr.code == IPRADIUS_CODE_Accounting_Request ?
IPRADIUS_EVENT_ACCOUNTING_TIMEOUT :
IPRADIUS_EVENT_ACCESS_TIMEOUT,
&reqent->request, &reqent->hdr, IP_NULL, IP_NULL);
if (reqent->session)
ipradius_session_free(reqent->session);
/* free reqent. */
ipradius_reqent_free(reqent);
}
}
else
{
/*IPCOM_LOG2(DEBUG, "ipradius_tick() :: Timeout: sec since poll = %d, poll interval = %d",
reqent->sec_since_poll, reqent->servent->server.pollsec);*/
}
}
/*
* Go through list of ongoing accounting sessions (once per minimum interval)
* and send interim accounting if required.
*/
if ((ipradius.tick % IPRADIUS_MIN_ACCT_INTERVAL) == 0)
{
for (session = IPCOM_LIST_FIRST(&ipradius.session_head);
session != IP_NULL;
session = IPCOM_LIST_NEXT(&session->session_list))
{
if (session->acct_interim_interval > 0)
{
ip_assert(session->sec_to_next_interim_acct <= (Ip_s32)session->acct_interim_interval);
session->sec_to_next_interim_acct -= IPRADIUS_MIN_ACCT_INTERVAL;
/* send interim accounting request if the time is up */
if (session->sec_to_next_interim_acct <= 0)
{
if (session->request_pending == IP_FALSE)
(void)ipradius_accounting_request_create(IPRADIUS_ACCT_Interim_Update, session, 0, 0);
session->sec_to_next_interim_acct = (Ip_s32)session->acct_interim_interval;
}
}
}
}
/***** Start 1 second timer if outstanding requests or if accounting sessions in progress. *****/
if (NOT IPCOM_TMO_PENDING(&ipradius.tmo))
{
if ((IPCOM_LIST_FIRST(&ipradius.request_head)) || (IPCOM_LIST_FIRST(&ipradius.session_head)))
{
IPRADIUS_UNLOCK();
return 1;
}
}
IPRADIUS_UNLOCK();
return 0;
}
/*
*===========================================================================
* ipradius_attr_add_message_auth
*===========================================================================
* Description: Add message authenticator attribute to request.
* Parameters: reqent - the radius request
* Returns: IPCOM_SUCCESS or IPCOM_ERR_xx from ipcom_err.h
*
*/
IP_STATIC void
ipradius_attr_add_message_auth(Ipradius_request_entry *reqent)
{
Ip_u8 auth[16];
Ip_u16 len;
ipcom_memset(auth, 0, sizeof(auth));
if (ipradius_attr_add_array(&reqent->hdr,
IPRADIUS_ATTR_Message_Authenticator,
auth,
sizeof(auth)) == 0)
{
/* Auth is added last, so create a hmac and replace last 16 bytes */
len = ip_htons(reqent->hdr.length);
ipradius_hmac_md5(reqent->servent->server.shared_secret,
reqent->servent->server.shared_secret_len,
(Ip_u8*)&reqent->hdr,
len,
(Ip_u8*)&reqent->hdr + len - sizeof(auth));
}
return;
}
IP_STATIC void
ipradius_attr_update_message_auth(Ipradius_request_entry *reqent, Ip_u8 *auth_attrp)
{
Ip_u16 len = ip_htons(reqent->hdr.length);
memset((Ip_u8*)&reqent->hdr + len - 16, 0, 16);
ipradius_hmac_md5(reqent->servent->server.shared_secret,
reqent->servent->server.shared_secret_len,
(Ip_u8*)&reqent->hdr,
len,
auth_attrp);
}
/*
*===========================================================================
* ipradius_request_packet_send
*===========================================================================
* Description: Complete an Access-Request or an Accounting-Request packet and
* send to the RADIUS server.
*
* The server previously used may have been removed, in which
* case another server must be chosen.
*
*
* Parameters:
* Returns: result
*
*/
IP_STATIC Ip_err
ipradius_request_packet_send(Ipradius_request_entry *reqent, Ip_bool start_timer)
{
Ip_u32 i;
Ip_u8 crypt[IPRADIUS_AUTH_SIZE];
Ip_fd fd;
void * auth_attrp = IP_NULL;
if (reqent->servent == IP_NULL)
ipradius_set_server(reqent);
if (reqent->servent == IP_NULL)
return IPRADIUS_ERR_NO_SERVER;
/* Pick matching IPv4 or IPv6 socket. */
fd = IP_INVALID_SOCKET;
#ifdef IPCOM_USE_INET
if (reqent->servent->server.server_addr.sa.sa_family == IP_AF_INET)
fd = reqent->request.fd;
#endif
#ifdef IPCOM_USE_INET6
if (reqent->servent->server.server_addr.sa.sa_family == IP_AF_INET6)
fd = reqent->request.fd6;
#endif
if (fd == IP_INVALID_SOCKET)
{
IPCOM_LOG1(ERR, "ipradius_request_packet_send() :: No valid socket descriptor to send to server %s",
ipradius_addr_to_str(&reqent->servent->server.server_addr));
return IPRADIUS_ERR_NO_SERVER;
}
/* get a new packet ID if first copy being sent of this request packet */
if (!reqent->pollnum)
{
if (IPRADIUS_ACCESS_PACKET(&reqent->hdr))
reqent->servent->AuthAccessRequests++;
else
reqent->servent->AccRequests++;
if (ipradius_packet_id_alloc(reqent) == IPCOM_ERR_FAILED)
{
IPCOM_LOG1(ERR, "ipradius_request_packet_send() :: too many outstanding packet ID:s to RADIUS server %s",
ipradius_addr_to_str(&reqent->servent->server.server_addr));
return IPRADIUS_ERR_TOO_MANY_OUTSTANDING_REQUESTS;
}
/*!!FIXME: NEED EVENT HOOK:
* If tunnel client specified, change it if changing servers.
* (void)ipradius_attr_change_u32(&reqent->hdr, IPRADIUS_ATTR_Tunnel_Client_Endpoint,
reqent->servent->server.client_ip_n);
*/
/* Access requests have a random number for authentication,
* this number is changed when changing server, not otherwise
*/
if (reqent->hdr.code == IPRADIUS_CODE_Access_Request)
{
Ip_u32 randval;
for (i = 0; i < IPRADIUS_AUTH_SIZE; i += (Ip_u32)sizeof(Ip_u32))
{
randval = ipcom_random();
ipcom_memcpy(&reqent->hdr.auth[i], &randval, sizeof(Ip_u32));
}
/*
insert password if the user requested. Note that authentication
must be calculated before password can be inserted
*/
ipradius_password_insert(reqent);
}
/*
* Accounting requests have md5 encrypted packet+secret as authentication,
* this number is changed when changing server, not otherwise
*/
else if (reqent->hdr.code == IPRADIUS_CODE_Accounting_Request)
{
Ip_u16 len = reqent->servent->server.shared_secret_len;
/* authentication is zeroes when calculating authentication */
ipcom_memset(reqent->hdr.auth, 0, IPRADIUS_AUTH_SIZE);
/* Check the remaing space for this request packet */
if (ip_ntohs(reqent->hdr.length) + len > reqent->pkt_length)
{
IPCOM_LOG0(ERR, "Exceeded size limit of access request attribute field");
return IPCOM_ERR_FAILED;
}
/* append secret shared with RADIUS server to the request packet */
ipcom_memcpy((char *)&reqent->hdr + ip_ntohs(reqent->hdr.length),
reqent->servent->server.shared_secret, len);
/* create authentication */
ipradius_md5_encrypt(crypt, (Ip_u8 *)&reqent->hdr,
len+ip_ntohs(reqent->hdr.length));
/* insert authentication in packet */
ipcom_memcpy(reqent->hdr.auth, (char *) crypt, IPRADIUS_AUTH_SIZE);
}
else
{
IP_PANIC();
}
}
/* Add EAP message authenticator if missing */
if (reqent->hdr.code == IPRADIUS_CODE_Access_Request
&& ipradius_attr_find(&reqent->hdr, IPRADIUS_ATTR_EAP_Message, IP_NULL) != IP_NULL)
{
if ((auth_attrp = ipradius_attr_find(&reqent->hdr, IPRADIUS_ATTR_Message_Authenticator, IP_NULL)) == IP_NULL)
ipradius_attr_add_message_auth(reqent);
else
ipradius_attr_update_message_auth(reqent,(Ip_u8* )auth_attrp);
}
ip_assert(ipradius_attr_check(&reqent->hdr, reqent, IP_NULL) == IPCOM_SUCCESS);
reqent->request_start_time = ipcom_time(0);
/* UDP output */
reqent->pollnum++;
IPCOM_LOG7(DEBUG, " -> %stransmitting RADIUS packet [code=%d, id=%d, length=%d] to server %s:%d [poll %d]",
start_timer == IP_TRUE ? "" :"re",
reqent->hdr.code,
reqent->hdr.id,
ip_ntohs(reqent->hdr.length),
ipradius_addr_to_str(&reqent->servent->server.server_addr),
(int)ip_ntohs(reqent->servent->server.server_addr.sin.sin_port),
reqent->pollnum);
/* Start 1 second tick if its not running. */
if (start_timer == IP_TRUE AND NOT IPCOM_TMO_PENDING(&ipradius.tmo))
{
if (ipcom_tmo_request(&ipradius.tmo, ipradius_tick, IP_NULL, 1000) != IPCOM_SUCCESS)
return IPCOM_ERR_FAILED;
}
/* Use ipcom_sendto() to transmit the RADIUS UDP packet to the RADIUS server. */
if (ipcom_sendto(fd, &reqent->hdr, ip_ntohs(reqent->hdr.length), 0,
&reqent->servent->server.server_addr.sa,
(Ip_socklen_t)IPCOM_SA_LEN_GET(&reqent->servent->server.server_addr.sa)) == IP_SOCKERR)
{
Ip_socklen_t optlen;
int error;
IPCOM_LOG3(WARNING, " ipcom_sendto(%d) failed : %s, server %s",
fd,
ipcom_strerror(ipcom_errno),
ipradius_addr_to_str(&reqent->servent->server.server_addr));
/* Clear socket errno. */
optlen = sizeof(error);
(void)ipcom_getsockopt(fd, IP_SOL_SOCKET, IP_SO_ERROR, &error, &optlen);
}
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipradius_accounting_request_create
*===========================================================================
* Description: Create an accounting_request packet, insert it in list of
* pending requests and send it to the RADIUS server.
*
* Parameters:
* Returns:
*
*/
IP_STATIC Ip_err
ipradius_accounting_request_create(Ip_u8 request_type, Ipradius_session *session, Ip_u32 acct_interim_interval, Ip_u32 terminate_cause)
{
Ipradius_request_entry *reqent;
Ip_u32 length;
Ip_u32 now = (Ip_u32)ipcom_time(0);
Ip_err retval;
IPCOM_LOG4(DEBUG, "<+> new accounting request (seq #%d, pending req %s, interval %ld, nxt %ld sec)",
session->interim_int, session->request_pending ? "y" : "n",
session->acct_interim_interval, session->sec_to_next_interim_acct);
length = sizeof(Ipradius_request_entry);
length += (Ip_u32)ipradius.accounting_request_attribute_size;
reqent = (Ipradius_request_entry *)ipcom_malloc(length);
if (reqent == IP_NULL)
{
IPCOM_LOG1(ERR, "ipradius_accounting_request_create() :: out of memory ('reqent' %ld bytes)", length);
return IPCOM_ERR_NO_MEMORY;
}
ipradius.mem.requests++;
/* set entry data. */
ipcom_memset(reqent, 0, sizeof(Ipradius_request_entry));
ipcom_memcpy(&reqent->request, &session->request, sizeof(Ipradius_request));
reqent->sec_since_poll = 0xffffffff;
reqent->session = session;
reqent->pkt_length = (Ip_s32)(IPRADIUS_accounting_request_attribute_size + IPRADIUS_HDRSIZE);
if (session->server_fixed == IP_TRUE)
{
reqent->server_fixed = session->server_fixed;
reqent->servent = session->servent;
IPCOM_LOG1(DEBUG2, "+++ fixing accounting server to %s",
session->servent ?
ipradius_addr_to_str(&session->servent->server.server_addr) : "NULL");
}
session->request_pending = IP_TRUE;
/* set radius header */
reqent->hdr.code = IPRADIUS_CODE_Accounting_Request;
reqent->hdr.length = ip_htons(IPRADIUS_HDRSIZE);
(void)ipradius_attr_add_u32(&reqent->hdr, IPRADIUS_ATTR_Acct_Status_Type, request_type);
(void)ipradius_attr_add_u32(&reqent->hdr, IPRADIUS_ATTR_Acct_Delay_Time, 0);
(void)ipradius_attr_add_u32(&reqent->hdr, IPRADIUS_ATTR_Acct_Authentic, IPRADIUS_ACCT_Radius);
(void)ipradius_attr_add_u32(&reqent->hdr, IPRADIUS_ATTR_Event_Timestamp, now);
if (request_type == IPRADIUS_ACCT_Start)
{
if (acct_interim_interval)
(void)ipradius_attr_add_u32(&reqent->hdr, IPRADIUS_ATTR_Acct_Interim_Interval, acct_interim_interval);
}
else if (request_type == IPRADIUS_ACCT_Stop)
{
(void)ipradius_attr_add_u32(&reqent->hdr, IPRADIUS_ATTR_Acct_Session_Time, (Ip_u32)(now - session->start_time));
(void)ipradius_attr_add_u32(&reqent->hdr, IPRADIUS_ATTR_Acct_Terminate_Cause, terminate_cause);
}
else if (request_type == IPRADIUS_ACCT_Interim_Update)
{
(void)ipradius_attr_add_u32(&reqent->hdr, IPRADIUS_ATTR_Acct_Session_Time, (Ip_u32)(now - session->start_time));
}
/* insert request entry into request list */
ipcom_list_insert_last(&ipradius.request_head, &reqent->request_list);
/* Allow user to change packet. */
(void)reqent->request.event_callback(IPRADIUS_EVENT_ACCOUNTING_REQUEST,
&reqent->request, &reqent->hdr, IP_NULL, &reqent->servent->server);
/* perform error checking on user's changes */
/* Send first RADIUS access request. */
retval = ipradius_request_packet_send(reqent, IP_TRUE);
if (retval != IPCOM_SUCCESS)
{
/* free reqent. */
ipradius_reqent_free(reqent);
return retval;
}
return IPCOM_SUCCESS;
}
/*
****************************************************************************
* 10 PUBLIC FUNCTIONS
****************************************************************************
*/
/*
*===========================================================================
* ipradius_attr_find
*===========================================================================
* Description: Return a poiter to the start location of the designated
* attribute. Return 0 if attribute was not present in the
* packet. Note that if there is more than one attribute with
* the same ID, the first of those will be returned.
*
* If the len parameter is non-zero, attribute length
* will be written to this address.
*
* Parameters:
* Returns:
*
*/
IP_PUBLIC void *
ipradius_attr_find(Ipradius_header *hdr, Ip_u8 type, Ip_u8 *len)
{
Ip_u8 *p = hdr->attr;
Ip_u8 *endp = &hdr->attr[ip_ntohs(hdr->length) - IPRADIUS_HDRSIZE];
while(p < endp)
{
if (*p++ == type)
{
if (len)
*len = *p;
return (void *)((Ip_u8 *)p+1);
}
p += *p-1;
}
return IP_NULL;
}
/*
*===========================================================================
* ipradius_attr_add_u32
*===========================================================================
* Description: Add desired 32-bit attribute to packet, convert to network
* byte order before adding.
* Parameters:
* Returns:
*
*/
IP_PUBLIC Ip_err
ipradius_attr_add_u32(Ipradius_header *hdr, Ip_u8 type, Ip_u32 data)
{
Ip_u32 loc;
loc = (Ip_u32)ip_htonl(data);
return ipradius_attr_add_array(hdr, type, (Ip_u8 *)&loc, 4);
}
/*
*===========================================================================
* ipradius_attr_add_array
*===========================================================================
* Description: Add desired byte array attribute to packet.
* Parameters:
* Returns:
*
*/
IP_PUBLIC Ip_err
ipradius_attr_add_array(Ipradius_header *hdr, Ip_u8 type, Ip_u8 *data, Ip_u8 len)
{
Ip_u16 newlen;
Ip_u8 *attrp;
Ipradius_request_entry *reqent;
/* Step back to get request entry pointer */
reqent = (Ipradius_request_entry *)((Ip_u8 *)hdr + sizeof(Ipradius_header) - sizeof(Ipradius_request_entry));
/*
* Check the length of the resulting request packet:
* Existing packet length (reqent->hdr.length)
* + length of attribute value (len)
* + 2 (for attribute type and length fileds)
*/
if (ip_ntohs(hdr->length) + len + 2 > reqent->pkt_length)
{
IPCOM_LOG0(WARNING, "Exceeded size limit of access request attribute field");
return IPCOM_ERR_REACHED_MAX;
}
attrp = &hdr->attr[ip_ntohs(hdr->length) - IPRADIUS_HDRSIZE]; /* i=1:st free slot */
*attrp = type;
attrp++;
*attrp = (Ip_u8)(2 + len);
attrp++;
ipcom_memcpy(attrp, data, len);
newlen = ip_ntohs(hdr->length);
newlen += (Ip_u16)(2 + len);
hdr->length = ip_htons(newlen);
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipradius_attr_get_u32
*===========================================================================
* Description: Get the 32 bit value of requested attribute. Convert from
* network byte order.
*
* Parameters:
* Returns: Return IPCOM_SUCCESS if attribute is found.
* Return IPCOM_ERR_NOT_FOUND if the attribute not found.
*
*/
IP_PUBLIC Ip_err
ipradius_attr_get_u32(Ipradius_header *hdr, Ip_u8 type, Ip_u32 *data)
{
Ip_u32 result;
Ip_u8 len, *p;
p = ipradius_attr_find(hdr, type, &len);
if (p)
{
if (len != 6)
{
IPCOM_LOG0(NOTICE, "ipradius_attr_get_u32() :: not an Ip_u32 attribute");
return IPCOM_ERR_FAILED;
}
ipcom_memcpy(&result, p, 4);
*data = (Ip_u32)ip_ntohl(result);
return IPCOM_SUCCESS;
}
return IPCOM_ERR_NOT_FOUND;
}
/*
*===========================================================================
* ipradius_attr_get_array
*===========================================================================
* Description: Return pointer to the attribute and set '*len' to its length.
* Return IP_NULL if the attribute not found.
* Parameters:
* Returns: Pointer to attribute data or IP_NULL.
*
*/
IP_PUBLIC Ip_u8 *
ipradius_attr_get_array(Ipradius_header *hdr, Ip_u8 type, Ip_u8 *len)
{
return (Ip_u8 *)ipradius_attr_find(hdr, type, len);
}
/*
*===========================================================================
* ipradius_attr_get_next
*===========================================================================
* Description: Get next attribute. Set attr to IP_NULL to get first.
* Return IP_NULL if no more attributes.
* Parameters:
* Returns:
*
*/
IP_PUBLIC void *
ipradius_attr_get_next(Ipradius_header *hdr, Ip_u8 *attr)
{
if (!attr)
{
/* Only RADIUS header -> no attributes. */
if (ip_ntohs(hdr->length) == IPRADIUS_HDRSIZE)
return IP_NULL;
/* First */
return (void *)hdr->attr;
}
attr++;
attr += ((*attr) - 1);
/* last? */
if (attr >= &hdr->attr[ip_ntohs(hdr->length) - IPRADIUS_HDRSIZE])
return IP_NULL;
return attr;
}
/*
*===========================================================================
* ipradius_attr_change_u32
*===========================================================================
* Description: Replace current Ip_u32 value of given attribute with data.
* Return IPCOM_ERR_NOT_FOUND if the attribute not found.
* Parameters:
* Returns:
*
*/
IP_PUBLIC Ip_err
ipradius_attr_change_u32(Ipradius_header *hdr, Ip_u8 type, Ip_u32 data)
{
Ip_u32 *p;
p = (Ip_u32 *)ipradius_attr_find(hdr, type, 0);
if (p)
{
Ip_u32 loc_n = (Ip_u32)ip_htonl(data);
ipcom_memcpy(p, &loc_n, 4);
return IPCOM_SUCCESS;
}
else
return IPCOM_ERR_NOT_FOUND;
}
/*
*===========================================================================
* ipradius_attr_verify_message_auth
*===========================================================================
* Description: Verify message authenticator attribute.
* Parameters: pkt - the radius packet
* secret - buffer with the shared secret
* secret_len - length of shared secret buffer
* Returns: IPCOM_SUCCESS or IPCOM_ERR_xx from ipcom_err.h
*
*/
IP_PUBLIC Ip_err
ipradius_attr_verify_message_auth(Ipradius_header *pkt, Ip_u8 *secret, Ip_u32 secret_len)
{
Ip_err err;
Ip_u8 auth[16];
Ip_u8 auth2[16];
Ip_u8 len;
Ip_u8* p;
err = IPCOM_ERR_FAILED;
p = ipradius_attr_get_array(pkt,
IPRADIUS_ATTR_Message_Authenticator,
&len);
if (p != IP_NULL)
{
len = sizeof(auth);
ipcom_memcpy(auth, p, len);
ipcom_memset(p, 0, len);
ipradius_hmac_md5(secret,
secret_len,
(Ip_u8*)pkt,
ip_htons(pkt->length),
auth2);
if (ipcom_memcmp(auth, auth2, sizeof(auth)) == 0)
err = IPCOM_SUCCESS;
ipcom_memcpy(p, auth, len);
}
return err;
}
/*
*===========================================================================
* ipradius_init
*===========================================================================
* Description: Initialize global IPRADIUS data 'ipradius'.
* Parameters: .
* Returns: .
*
*/
IP_PUBLIC void
ipradius_init(void)
{
ipcom_memset(&ipradius, 0, sizeof(Ipradius_data));
/* Default configuration values. */
ipradius.same_acct_server = IP_TRUE;
/* Initialize timers and lists. */
IPCOM_TMO_INIT(&ipradius.tmo);
ipcom_list_init(&ipradius.server_head);
ipcom_list_init(&ipradius.request_head);
ipcom_list_init(&ipradius.session_head);
#ifdef IPRADIUS_ENABLE_REENTRANT
(void)ipcom_mutex_create(&ipradius.code_mutex);
#endif
}
/*
*===========================================================================
* ipradius_exit
*===========================================================================
* Description:
* Parameters: .
* Returns: .
*
*/
IP_PUBLIC void
ipradius_exit(void)
{
/* Call close just in case. */
(void)ipradius_close();
#ifdef IPRADIUS_ENABLE_REENTRANT
(void)ipcom_mutex_delete(&ipradius.code_mutex);
#endif
/* Leave no secrets. */
ipcom_memset(&ipradius, 0, sizeof(Ipradius_data));
}
/*
*===========================================================================
* ipradius_open
*===========================================================================
* Description: Open and configure Radius with global information.
* Parameters: config - A pointer to an initialized Ipradius_open structure:
* flags
* conf - Optional configuration (none in use yet).
* Returns: IPCOM_ERR_ALREADY_OPEN - ipradius_open() should only be
* called once.
* IPCOM_ERR_UNSUPPORTED_TAG - No optional configuration is
* in use and conf[0] must be set to IP_TAG_END.
* IPCOM_ERR_IPTIME_NOT_OPENED - IPTIME is not opened.
* IPCOM_SUCCESS - Success.
*
*/
IP_PUBLIC Ip_err
ipradius_open(Ipradius_open *config)
{
int num = 0;
Ip_err retval;
IPRADIUS_LOCK();
if (ipradius.opened == IP_TRUE)
IPRADIUS_LEAVE(IPCOM_ERR_ALREADY_OPEN);
if (config->conf[0] != IP_TAG_END)
IPRADIUS_LEAVE(IPCOM_ERR_UNSUPPORTED_TAG);
/* Must have a working timer starting at non-zero. */
while(ipcom_time(0) == 0)
{
(void)ipcom_sleep(1);
num++;
ip_assert(num < 10);
(void)num;
}
/* store user config. */
ipradius.access_request_attribute_size = config->access_request_attribute_size;
ipradius.accounting_request_attribute_size = config->accounting_request_attribute_size;
ipradius.ipradius_NAS_Identifier_size = config->ipradius_NAS_Identifier_size;
ipcom_memcpy(ipradius.ipradius_NAS_Identifier,config->ipradius_NAS_Identifier,config->ipradius_NAS_Identifier_size);
ipradius.ipradius_NAS_Identifier[ipradius.ipradius_NAS_Identifier_size] = '\0';
IPCOM_LOG0(DEBUG, "ipradius_open() :: opened");
ipradius.opened = IP_TRUE;
retval = IPCOM_SUCCESS;
leave:
IPRADIUS_UNLOCK();
return retval;
}
/*
*===========================================================================
* ipradius_close
*===========================================================================
* Description: Close an opened IPRADIUS.
* Parameters: .
* Returns: IPCOM_ERR_ALREADY_CLOSED - Failed to close since not open.
* IPCOM_SUCCESS - Success.
*
*/
IP_PUBLIC Ip_err
ipradius_close(void)
{
Ipradius_request_entry *reqent, *nextreq;
Ipradius_server_entry *servent, *nextserv;
Ip_err retval;
IPRADIUS_LOCK();
if (ipradius.opened == IP_FALSE)
IPRADIUS_LEAVE(IPCOM_ERR_ALREADY_CLOSED);
/* Cancel the tick. */
if (IPCOM_TMO_PENDING(&ipradius.tmo))
(void)ipcom_tmo_cancel(&ipradius.tmo);
/* Remove all requests and their sessions. */
for (reqent = IPCOM_LIST_FIRST(&ipradius.request_head); reqent; reqent = nextreq)
{
nextreq = IPCOM_LIST_NEXT(&reqent->request_list);
reqent->request.status = IPRADIUS_ERR_CLOSED;
(void)reqent->request.event_callback(reqent->hdr.code == IPRADIUS_CODE_Accounting_Request ?
IPRADIUS_EVENT_ACCOUNTING_CANCELLED :
IPRADIUS_EVENT_ACCESS_CANCELLED,
&reqent->request, &reqent->hdr, IP_NULL, IP_NULL);
if (reqent->session)
ipradius_session_free(reqent->session);
/* free reqent. */
ipradius_reqent_free(reqent);
}
/* Remove all servers. */
for (servent = IPCOM_LIST_FIRST(&ipradius.server_head); servent; servent = nextserv)
{
nextserv = IPCOM_LIST_NEXT(&servent->server_list);
ipradius_servent_free(servent);
}
ip_assert(ipradius.mem.servers == 0);
ip_assert(ipradius.mem.sessions == 0);
ip_assert(ipradius.mem.requests == 0);
ip_assert(ipradius.mem.id_holders == 0);
ip_assert(IPCOM_LIST_FIRST(&ipradius.server_head) == IP_NULL);
ip_assert(IPCOM_LIST_FIRST(&ipradius.request_head) == IP_NULL);
ip_assert(IPCOM_LIST_FIRST(&ipradius.session_head) == IP_NULL);
ip_assert(IPCOM_TMO_PENDING(&ipradius.tmo) == IP_NULL);
IPCOM_LOG0(DEBUG, "ipradius_close() :: closed");
ipradius.opened = IP_FALSE;
retval = IPCOM_SUCCESS;
leave:
IPRADIUS_UNLOCK();
return retval;
}
/*
*===========================================================================
* ipradius_ctrl
*===========================================================================
* Description:
* Parameters: ctrltags A IP_TAG_END terminated tag array containing the
* desired information tags and space for the return value.
* Returns: IPRADIUS_ERR_UNSUPPORTED_TAG - The optional configuration
* tag array contained an unknown tag.
* IPRADIUS_SUCCESS - Success.
*
*/
IP_PUBLIC Ip_err
ipradius_ctrl(Ip_tag *ctrltags)
{
Ip_tag *tagp;
Ipradius_server_entry *servent;
Ip_err retval;
IPRADIUS_LOCK();
if (ipradius.opened == IP_FALSE)
IPRADIUS_LEAVE(IPCOM_ERR_NOT_OPENED);
for (tagp = ctrltags; *tagp != IP_TAG_END; tagp += 2)
{
switch(*tagp)
{
case IPRADIUS_CTRLT_SET_SAME_ACCT_SERVER :
if (tagp[1] != IP_TRUE AND tagp[1] != IP_FALSE)
IPRADIUS_LEAVE(IPCOM_ERR_INVALID_TAG_VALUE);
ipradius.same_acct_server = (Ip_bool)tagp[1];
break;
case IPRADIUS_CTRLT_GET_NEXT_SERVER :
servent = ipradius_get_lexicographic_server((Ip_u32)tagp[1]);
tagp[1] = servent ? (Ip_tag)servent->server.index : 0;
break;
/* MIB RFC2618 variable. */
case IPRADIUS_CTRLT_GET_AuthInvalidServerAddresses :
tagp[1] = (Ip_tag)ipradius.mib_AuthInvalidServerAddresses;
break;
/* MIB RFC2620 variable. */
case IPRADIUS_CTRLT_GET_AccInvalidServerAddresses :
tagp[1] = (Ip_tag)ipradius.mib_AccInvalidServerAddresses;
break;
/* MIB RFC2618 variable. */
case IPRADIUS_CTRLT_GET_AuthIdentifier :
/* MIB RFC2620 variable. */
case IPRADIUS_CTRLT_GET_AccIdentifier :
tagp[1] = (Ip_tag)ipradius.ipradius_NAS_Identifier;
break;
default:
IPRADIUS_LEAVE(IPCOM_ERR_UNSUPPORTED_TAG);
}
}
retval = IPCOM_SUCCESS;
leave:
IPRADIUS_UNLOCK();
return retval;
}
/*
*===========================================================================
* ipradius_server_add
*===========================================================================
* Description: Add/replace a RADIUS server entry. If an entry with the same
* address already exists, it will be replaced.
* Parameters:
* Returns:
*
*/
IP_PUBLIC Ip_err
ipradius_server_add(Ipradius_server *server)
{
Ip_u32 i;
Ipradius_server_entry *servent, *tmpent; /*lint !e429 */
Ip_err retval;
IPRADIUS_LOCK();
if (ipradius.opened == IP_FALSE)
IPRADIUS_LEAVE(IPCOM_ERR_NOT_OPENED);
if (server->shared_secret_len < 1)
{
IPCOM_LOG1(NOTICE, "ipradius_set_server() :: shared secret too short (%d)",
server->shared_secret_len);
IPRADIUS_LEAVE(IPRADIUS_ERR_SECRET_TOO_SHORT);
}
if (server->shared_secret_len > IPRADIUS_MAX_SECRET_SIZE)
{
IPCOM_LOG1(NOTICE, "ipradius_set_server() :: shared secret too long (%d)", server->shared_secret_len);
IPRADIUS_LEAVE(IPRADIUS_ERR_SECRET_TOO_LONG);
}
/* Check for duplicate server, count the number of times referenced. */
for (servent = IPCOM_LIST_FIRST(&ipradius.server_head); servent; servent = IPCOM_LIST_NEXT(&servent->server_list))
if (ipradius_server_equal(server, &servent->server))
{
servent->ref_count++;
IPRADIUS_LEAVE(IPCOM_SUCCESS);
}
/* Create a new server entry. */
servent = (Ipradius_server_entry *)ipcom_malloc(sizeof(Ipradius_server_entry));
if (servent == IP_NULL)
{
IPCOM_LOG1(ERR, "ipradius_server_add() :: out of memory ('servent' %ld bytes)",
sizeof(Ipradius_server_entry));
IPRADIUS_LEAVE(IPCOM_ERR_NO_MEMORY);
}
/* Init new server entry. */
ipcom_memset(servent,0,sizeof(Ipradius_server_entry));
servent->ref_count = 1;
/* Find or create packet ID place holder. */
for (tmpent = IPCOM_LIST_FIRST(&ipradius.server_head); tmpent; tmpent = IPCOM_LIST_NEXT(&tmpent->server_list))
if (ipradius_inaddr_equal(&server->server_addr, &tmpent->server.server_addr))
{
ip_assert(tmpent->pkt_ids != IP_NULL);
servent->pkt_ids = tmpent->pkt_ids;
servent->pkt_ids->ref_count++;
break;
}
if (servent->pkt_ids == IP_NULL)
{
Ipradius_ids *pkt_ids;
pkt_ids = (Ipradius_ids *)ipcom_malloc(sizeof(Ipradius_ids));
if (pkt_ids == IP_NULL)
{
IPCOM_LOG1(ERR, "ipradius_server_add() :: out of memory ('pkt_ids' %ld bytes)",
sizeof(Ipradius_ids));
ipcom_free(servent);
IPRADIUS_LEAVE(IPCOM_ERR_NO_MEMORY);
}
ipcom_memset(pkt_ids, 0, sizeof(Ipradius_ids));
/* init pkt_ids. */
pkt_ids->ref_count = 1;
for (i = 0; i < 256; i++)
{
pkt_ids->access_ids.ids[i] = (Ip_u8)i;
pkt_ids->account_ids.ids[i] = (Ip_u8)i;
}
ipradius.mem.id_holders++;
servent->pkt_ids = pkt_ids;
}
/* Add to the end of server list. */
ipradius.mem.servers++;
ipradius.server_num++;
ipcom_list_insert_last(&ipradius.server_head, &servent->server_list);
/* Pick a server index for the application. */
if (server->index == 0)
{
server->index = ipradius.server_index++;
/* Make sure the chosen index is unique. */
again:
for (tmpent = IPCOM_LIST_FIRST(&ipradius.server_head);
tmpent;
tmpent = IPCOM_LIST_NEXT(&tmpent->server_list))
if (server->index == tmpent->server.index)
{
server->index = ipradius.server_index++;
goto again;
}
}
/* Copy over server info. */
ipcom_memcpy(&servent->server, server, sizeof(Ipradius_server));
retval = IPCOM_SUCCESS;
leave:
IPRADIUS_UNLOCK();
return retval; /*lint !e429 */
}
/*
*===========================================================================
* ipradius_server_remove
*===========================================================================
* Description: Remove a RADIUS server entry.
* Parameters:
* Returns:
*
*/
IP_PUBLIC Ip_err
ipradius_server_remove(Ipradius_server *server)
{
Ipradius_server_entry *servent;
Ip_err retval;
IPRADIUS_LOCK();
ip_assert(ipradius.infunc == 0);
IP_ASSERTLINE(ipradius.infunc++);
if (ipradius.opened == IP_FALSE)
IPRADIUS_LEAVE(IPCOM_ERR_NOT_OPENED);
for (servent = IPCOM_LIST_FIRST(&ipradius.server_head); servent; servent = IPCOM_LIST_NEXT(&servent->server_list))
if (ipradius_server_equal(server, &servent->server))
{
servent->ref_count--;
ip_assert(servent->ref_count >= 0);
/* No more references to this server, free it. */
if (servent->ref_count == 0)
ipradius_servent_free(servent);
IPRADIUS_LEAVE(IPCOM_SUCCESS);
}
retval = IPCOM_ERR_NOT_FOUND;
leave:
ip_assert(ipradius.infunc == 1);
IP_ASSERTLINE(ipradius.infunc--);
IPRADIUS_UNLOCK();
return retval;
}
/*
*===========================================================================
* ipradius_server_ctrl
*===========================================================================
* Description:
* Parameters: server which server to get/set info on.
* ctrltags A IP_TAG_END terminated tag array containing the
* desired information tags and space for the return value.
* Returns: IPRADIUS_ERR_UNSUPPORTED_TAG - The optional configuration
* tag array contained an unknown tag.
* IPRADIUS_SUCCESS - Success.
*
*/
IP_PUBLIC Ip_err
ipradius_server_ctrl(Ipradius_server *server, Ip_tag *ctrltags)
{
Ip_tag *tagp;
Ipradius_server_entry *servent;
Ip_err retval;
IPRADIUS_LOCK();
if (ipradius.opened == IP_FALSE)
IPRADIUS_LEAVE(IPCOM_ERR_NOT_OPENED);
for (servent = IPCOM_LIST_FIRST(&ipradius.server_head); servent; servent = IPCOM_LIST_NEXT(&servent->server_list))
if (ipradius_server_equal(server, &servent->server) &&
((servent->server.server_type == IPRADIUS_FULL_SERVER) ||
(servent->server.server_type == server->server_type)))
{
/* found */
break;
}
/* not found */
if (servent == IP_NULL)
IPRADIUS_LEAVE(IPCOM_ERR_NOT_FOUND);
for (tagp = ctrltags; *tagp != IP_TAG_END; tagp += 2)
{
switch(*tagp)
{
case IPRADIUS_SCTRLT_GET_AuthServerAddress : /* same as IPRADIUS_SCTRLT_GET_AccServerAddress */
/*compatibility of ipv6,first save af_net_family,other 4 used for ipv4/6 addr*/
tagp[1] = (Ip_tag)servent->server.server_addr.sa.sa_family;
#ifdef IPCOM_USE_INET
if(tagp[1] == IP_AF_INET)
{
tagp[2] = (Ip_tag)servent->server.server_addr.sin.sin_addr.s_addr;
}
#endif
#ifdef IPCOM_USE_INET6
if(tagp[1] == IP_AF_INET6)
{
ipcom_memcpy(&(tagp[2]),&(servent->server.server_addr.sin6.sin6_addr),sizeof(struct Ip_in6_addr));
}
#endif
tagp += 4;
break;
case IPRADIUS_SCTRLT_GET_AuthServerPortNumber : /* same as IPRADIUS_SCTRLT_GET_AccServerPortNumber */
tagp[1] = (Ip_tag)servent->server.server_addr.sin.sin_port;
break;
case IPRADIUS_SCTRLT_GET_AuthRoundTripTime :
tagp[1] = (Ip_tag)servent->AuthRoundTripTime;
break;
case IPRADIUS_SCTRLT_GET_AccRoundTripTime :
tagp[1] = (Ip_tag)servent->AccRoundTripTime;
break;
case IPRADIUS_SCTRLT_GET_AuthAccessRequests :
tagp[1] = (Ip_tag)servent->AuthAccessRequests;
break;
case IPRADIUS_SCTRLT_GET_AccRequests :
tagp[1] = (Ip_tag)servent->AccRequests;
break;
case IPRADIUS_SCTRLT_GET_AuthAccessRetransmissions :
tagp[1] = (Ip_tag)servent->AuthAccessRetransmissions;
break;
case IPRADIUS_SCTRLT_GET_AccRetransmissions :
tagp[1] = (Ip_tag)servent->AccRetransmissions;
break;
case IPRADIUS_SCTRLT_GET_AuthAccessAccepts :
tagp[1] = (Ip_tag)servent->AuthAccessAccepts;
break;
case IPRADIUS_SCTRLT_GET_AccResponses :
tagp[1] = (Ip_tag)servent->AccResponses;
break;
case IPRADIUS_SCTRLT_GET_AuthAccessRejects :
tagp[1] = (Ip_tag)servent->AuthAccessRejects;
break;
case IPRADIUS_SCTRLT_GET_AuthAccessChallenges :
tagp[1] = (Ip_tag)servent->AuthAccessChallenges;
break;
case IPRADIUS_SCTRLT_GET_AuthMalformedAccessResponses :
tagp[1] = (Ip_tag)servent->AuthMalformedAccessResponses;
break;
case IPRADIUS_SCTRLT_GET_AccMalformedResponses :
tagp[1] = (Ip_tag)servent->AccMalformedResponses;
break;
case IPRADIUS_SCTRLT_GET_AuthBadAuthenticators :
tagp[1] = (Ip_tag)servent->AuthBadAuthenticators;
break;
case IPRADIUS_SCTRLT_GET_AccBadAuthenticators :
tagp[1] = (Ip_tag)servent->AccBadAuthenticators;
break;
case IPRADIUS_SCTRLT_GET_AuthPendingRequests :
tagp[1] = (Ip_tag)servent->pkt_ids->access_ids.num; /*lint !e571 (not correct if servers with same IP). */
break;
case IPRADIUS_SCTRLT_GET_AccPendingRequests :
tagp[1] = (Ip_tag)servent->pkt_ids->account_ids.num; /*lint !e571 (not correct if servers with same IP). */
break;
case IPRADIUS_SCTRLT_GET_AuthTimeouts :
tagp[1] = (Ip_tag)servent->AuthTimeouts;
break;
case IPRADIUS_SCTRLT_GET_AccTimeouts :
tagp[1] = (Ip_tag)servent->AccTimeouts;
break;
case IPRADIUS_SCTRLT_GET_AuthUnknownTypes :
tagp[1] = (Ip_tag)servent->AuthUnknownTypes;
break;
case IPRADIUS_SCTRLT_GET_AccUnknownTypes :
tagp[1] = (Ip_tag)servent->AccUnknownTypes;
break;
case IPRADIUS_SCTRLT_GET_AuthPacketsDropped :
tagp[1] = (Ip_tag)servent->AuthPacketsDropped;
break;
case IPRADIUS_SCTRLT_GET_AccPacketsDropped :
tagp[1] = (Ip_tag)servent->AccPacketsDropped;
break;
default:
IPRADIUS_LEAVE(IPCOM_ERR_UNSUPPORTED_TAG);
}
}
retval = IPCOM_SUCCESS;
leave:
IPRADIUS_UNLOCK();
return retval;
}
/*
*===========================================================================
* ipradius_check_request_unlocked
*===========================================================================
* Description: IP_TRUE if there is a pending request with an identical
* value in the unique field.
* Parameters:
* Returns:
*
*/
IP_STATIC Ip_bool
ipradius_check_request_unlocked(Ip_u32 unique)
{
Ipradius_request_entry *reqent;
/* Check if identical request is pending. */
for (reqent = IPCOM_LIST_FIRST(&ipradius.request_head); reqent; reqent = IPCOM_LIST_NEXT(&reqent->request_list))
if (reqent->request.unique == unique)
return IP_TRUE;
return IP_FALSE;
}
/*
*===========================================================================
* ipradius_check_request
*===========================================================================
* Description: IP_TRUE if there is a pending request with an identical
* value in the unique field.
* Parameters:
* Returns:
*
*/
IP_PUBLIC Ip_bool
ipradius_check_request(Ip_u32 unique)
{
Ip_bool retval;
IPRADIUS_LOCK();
if (ipradius.opened == IP_FALSE)
IPRADIUS_LEAVE(IP_FALSE);
retval = ipradius_check_request_unlocked(unique);
leave:
IPRADIUS_UNLOCK();
return retval;
}
/*
*===========================================================================
* ipradius_access_request_pkt_malloc_common
*===========================================================================
* Description: Allocate and prepare a RADIUS access request packet.
* Parameters: Length of the access request packet.
* Returns: Pointer to RADIUS packet or IP_NULL if out of memory.
*
*/
IP_INLINE Ipradius_header *
ipradius_access_request_pkt_malloc_common(Ip_u16 len)
{
Ipradius_request_entry *reqent;
Ip_u32 length;
if (ipradius.opened == IP_FALSE)
{
IPCOM_LOG0(NOTICE, "ipradius_access_request_pkt_malloc() :: not opened");
return IP_NULL;
}
/* Malloc the request entry structure. */
length = (Ip_u32)(sizeof(Ipradius_request_entry) - IPRADIUS_HDRSIZE + len);
reqent = (Ipradius_request_entry *)ipcom_malloc(length);
if (reqent == IP_NULL)
{
IPCOM_LOG1(ERR, "ipradius_access_request_pkt_malloc() :: out of memory ('reqent' %ld bytes)",length);
return IP_NULL;
}
ipradius.mem.requests++;
/* preinit reqent. */
ipcom_memset(reqent, 0, sizeof(Ipradius_request_entry));
reqent->magic = IPRADIUS_REQUEST_ENTRY_MAGIC;
reqent->pkt_length = len;
/* set request structure data */
reqent->hdr.code = IPRADIUS_CODE_Access_Request;
reqent->hdr.length = ip_htons(IPRADIUS_HDRSIZE);
/* add initial attributes */
(void)ipradius_attr_add_array(&reqent->hdr, IPRADIUS_ATTR_NAS_Identifier, ipradius.ipradius_NAS_Identifier, (Ip_u8)ipradius.ipradius_NAS_Identifier_size);
/* Return pointer to the RADIUS packet header. */
return &reqent->hdr;
}
/*
*===========================================================================
* ipradius_access_request_pkt_malloc
*===========================================================================
* Description: Allocate and prepare a RADIUS access request packet.
* Parameters:
* Returns: Pointer to RADIUS packet or IP_NULL if out of memory or if
* the packet length does not comply with RFC 2865.
*
*/
IP_PUBLIC Ipradius_header *
ipradius_access_request_pkt_malloc(void)
{
Ip_u16 length = (Ip_u16)(ipradius.access_request_attribute_size + IPRADIUS_HDRSIZE);
/* RFC 2865 */
if (length < IPRADIUS_HDRSIZE || length > IPRADIUS_MAX_PKT_SIZE)
return IP_NULL;
return ipradius_access_request_pkt_malloc_common(length);
}
/*
*===========================================================================
* ipradius_access_request_pkt_malloc_with_len
*===========================================================================
* Description: Allocate and prepare a RADIUS access request packet.
* Parameters: Length of the access request packet.
* Returns: Pointer to RADIUS packet or IP_NULL if out of memory or if
* the packet length does not comply with RFC 2865.
*
*/
IP_PUBLIC Ipradius_header *
ipradius_access_request_pkt_malloc_with_len(Ip_u16 length)
{
/* RFC 2865 */
if (length < IPRADIUS_HDRSIZE || length > IPRADIUS_MAX_PKT_SIZE)
return IP_NULL;
return ipradius_access_request_pkt_malloc_common(length);
}
/*
*===========================================================================
* ipradius_access_request_pkt_free
*===========================================================================
* Description: Free a RADIUS access request packet.
* Parameters: Packet allocated by ipradius_access_request_pkt_malloc().
* Returns: IPCOM_SUCESS or error code.
*
*/
IP_PUBLIC Ip_err
ipradius_access_request_pkt_free(Ipradius_header *packet)
{
Ipradius_request_entry *reqent;
Ip_u8 *pek;
if (ipradius.opened == IP_FALSE)
return IPCOM_ERR_NOT_OPENED;
/* Step back to get request entry pointer. */
pek = (Ip_u8 *)packet;
pek += sizeof(Ipradius_header);
pek -= sizeof(Ipradius_request_entry);
reqent = (Ipradius_request_entry *)pek;
/* Simple test to see if this is a request entry structure. */
if (((unsigned long)reqent & 0x3) || reqent->magic != IPRADIUS_REQUEST_ENTRY_MAGIC)
return IPCOM_ERR_INVALID_ARG;
/* Release memory. */
ip_assert(reqent->request_list.head == IP_NULL);
ip_assert(reqent->num_ids == 0);
ipcom_free(reqent);
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipradius_access_request
*===========================================================================
* Description: Handle user request for RADIUS login.
* Insert request in list of pending requests. Send a RADIUS
* request to the Radius server.
* Parameters:
* Returns:
*
*/
IP_PUBLIC Ip_err
ipradius_access_request(Ipradius_header *packet, Ipradius_request *request, Ip_u8 *errattr)
{
Ipradius_request_entry *reqent;
Ip_err retval;
Ip_u8 attrlen, *pek;
if (errattr)
*errattr = 0;
IPRADIUS_LOCK();
if (packet == IP_NULL)
IPRADIUS_LEAVE(IPCOM_ERR_INVALID_ARG);
/* Step back to get request entry pointer. */
pek = (Ip_u8 *)packet;
pek += sizeof(Ipradius_header);
pek -= sizeof(Ipradius_request_entry);
reqent = (Ipradius_request_entry *)pek;
/* Simple test to see if this is a request entry structure. */
if (((unsigned long)reqent & 0x3) || reqent->magic != IPRADIUS_REQUEST_ENTRY_MAGIC)
IPRADIUS_LEAVE(IPCOM_ERR_INVALID_ARG);
/* Check if identical request is pending. */
if (ipradius_check_request_unlocked(request->unique) == IP_TRUE)
{
/* Insert request into list to not fail ipradius_reqent_free() */
ipcom_list_insert_last(&ipradius.request_head, &reqent->request_list);
IPRADIUS_ERROUT(IPCOM_ERR_PENDING);
}
/* Insert request into list (note: must be done prior to errout). */
ipcom_list_insert_last(&ipradius.request_head, &reqent->request_list);
/* Radius client is not opened. */
if (ipradius.opened == IP_FALSE)
IPRADIUS_ERROUT(IPCOM_ERR_NOT_OPENED);
/* Clear and set request entry data. */
ipcom_memcpy(&reqent->request, request, sizeof(Ipradius_request));
reqent->sec_since_poll = 0xffffffff;
/* Check request password length if there. */
switch(request->pwd_type)
{
case IPRADIUS_CHAP_Password:
if (ipradius_attr_find(packet, IPRADIUS_ATTR_CHAP_Challenge, &attrlen) == 0)
{
retval = IPRADIUS_ERR_MISSING_CHAP_ATTR;
goto errout;
}
else if (attrlen < 7)
{
retval = IPRADIUS_ERR_INVAL_PWD_LEN;
goto errout;
}
else if (ipradius_attr_find(packet, IPRADIUS_ATTR_CHAP_Password, &attrlen))
{
/* Do not verify request->pwd_len. */
break;
}
/* Fall through on purpose for next test. */
case IPRADIUS_PAP_Password:
if (request->pwd_len > IPRADIUS_MAX_PASSWORD_SIZE || request->pwd_len == 0)
{
badpwdlen:
retval = IPRADIUS_ERR_INVAL_PWD_LEN;
goto errout;
}
break;
case IPRADIUS_No_Password:
if (request->pwd_len != 0)
goto badpwdlen;
break;
default:
retval = IPRADIUS_ERR_INVAL_PWD_TYPE;
goto errout;
}
/* perform error checking on user's attributes */
retval = ipradius_attr_check(&reqent->hdr, reqent, errattr);
if (retval != IPCOM_SUCCESS)
{
goto errout;
}
/* Send first RADIUS access request. */
retval = ipradius_request_packet_send(reqent, IP_TRUE);
if (retval != IPCOM_SUCCESS)
{
goto errout;
}
IPRADIUS_LEAVE(IPCOM_SUCCESS);
errout:
/* free reqent. */
ipradius_reqent_free(reqent);
leave:
IPRADIUS_UNLOCK();
return retval;
}
/*
*===========================================================================
* ipradius_accounting_session_start
*===========================================================================
* Description: Handle user request for an accounting session.
* Create a new session. Send a RADIUS
* accounting request to the Radius server.
*
* Parameters:
* Returns:
*
*/
IP_PUBLIC Ip_err
ipradius_accounting_session_start(Ipradius_request *request, Ip_u32 acct_interim_interval)
{
Ipradius_session *session;
Ip_err retval;
IPRADIUS_LOCK();
if (ipradius.opened == IP_FALSE)
IPRADIUS_LEAVE(IPCOM_ERR_NOT_OPENED);
IPCOM_LOG2(DEBUG, "calling ipradius_accounting_session_start(%ld,%d)",
request->unique, acct_interim_interval);
if (acct_interim_interval > 0 && acct_interim_interval < IPRADIUS_MIN_ACCT_INTERVAL)
{
IPCOM_LOG1(NOTICE, "ipradius_accounting_session_start() :: Interim-Acct-Interval too small (%d)",
acct_interim_interval);
IPRADIUS_LEAVE(IPRADIUS_ERR_INVAL_INTERIM_ACCT_INTERVAL);
}
/* Check if identical session is in progress. */
if (ipradius_session_find(request->unique) != 0)
IPRADIUS_LEAVE(IPCOM_ERR_PENDING);
session = (Ipradius_session *)ipcom_malloc(sizeof(Ipradius_session));
if (session == IP_NULL)
{
IPCOM_LOG1(ERR, "ipradius_accounting_session_start() :: out of memory ('session' %ld bytes)",
sizeof(Ipradius_session));
IPRADIUS_LEAVE(IPCOM_ERR_NO_MEMORY);
}
ipradius.mem.sessions++;
/* Set session data and insert into session list */
ipcom_memset(session, 0, sizeof(Ipradius_session));
session->acct_interim_interval = acct_interim_interval;
session->sec_to_next_interim_acct = (Ip_s32)acct_interim_interval;
ipcom_memcpy(&session->request, request, sizeof(Ipradius_request));
ipcom_list_insert_last(&ipradius.session_head, &session->session_list);
/* Create and send the first accounting request. */
retval = ipradius_accounting_request_create(IPRADIUS_ACCT_Start, session, acct_interim_interval, 0);
/* Free session if request was not created. */
if (retval != IPCOM_SUCCESS)
{
/* Copy back our copy of request struct in case user changed it in a callback. */
ipcom_memcpy(request, &session->request, sizeof(Ipradius_request));
ipradius_session_free(session);
goto leave;
}
retval = IPCOM_SUCCESS;
leave:
IPRADIUS_UNLOCK();
return retval;
}
/*
*===========================================================================
* ipradius_accounting_session_stop
*===========================================================================
* Description: Handle user request for stopping an accounting session.
* Delete the session. Send a RADIUS
* accounting request (stop) to the Radius server.
*
* Parameters:
* Returns:
*
*/
IP_PUBLIC Ip_err
ipradius_accounting_session_stop(Ip_u32 unique, Ip_u32 terminate_cause)
{
Ipradius_request_entry *reqent;
Ipradius_session *session;
Ip_err retval;
IPRADIUS_LOCK();
if (ipradius.opened == IP_FALSE)
IPRADIUS_LEAVE(IPCOM_ERR_NOT_OPENED);
IPCOM_LOG2(DEBUG, "calling ipradius_accounting_session_stop(%ld,%ld)", unique, terminate_cause);
session = ipradius_session_find(unique);
if (session == IP_NULL)
IPRADIUS_LEAVE(IPRADIUS_ERR_NO_SESSION);
/* 'Start' never acknowledged, remove any pending 'Start' request. */
if (session->start_time == 0)
{
for (reqent = IPCOM_LIST_FIRST(&ipradius.request_head);
reqent != IP_NULL;
reqent = IPCOM_LIST_NEXT(&reqent->request_list))
if (reqent->session == session)
{
IPCOM_LOG1(DEBUG, "removing unacked 'Start' request for id = %ld", unique);
ipradius_reqent_free(reqent);
break;
}
}
/* Send 'Stop' regardless if ack'd since the server may have gotten the request (only ack lost). */
retval = ipradius_accounting_request_create(IPRADIUS_ACCT_Stop, session, 0, terminate_cause);
if (retval != IPCOM_SUCCESS)
goto leave;
/* Detach this session from all pending request entries. */
for (reqent = IPCOM_LIST_FIRST(&ipradius.request_head); reqent; reqent = IPCOM_LIST_NEXT(&reqent->request_list))
if (reqent->session == session)
reqent->session = IP_NULL;
/* Release the session. */
ipradius_session_free(session);
retval = IPCOM_SUCCESS;
leave:
IPRADIUS_UNLOCK();
return retval;
}
/*
*===========================================================================
* ipradius_accounting_session_update
*===========================================================================
* Description: Force an accounting session update.
* Parameters:
* Returns:
*
*/
IP_PUBLIC Ip_err
ipradius_accounting_session_update(Ip_u32 unique)
{
Ipradius_session *session;
Ip_err retval;
IPRADIUS_LOCK();
if (ipradius.opened == IP_FALSE)
IPRADIUS_LEAVE(IPCOM_ERR_NOT_OPENED);
session = ipradius_session_find(unique);
if (session == IP_NULL)
IPRADIUS_LEAVE(IPRADIUS_ERR_NO_SESSION);
if (session->acct_interim_interval >= IPRADIUS_MIN_ACCT_INTERVAL)
session->sec_to_next_interim_acct = IPRADIUS_MIN_ACCT_INTERVAL;
retval = IPCOM_SUCCESS;
leave:
IPRADIUS_UNLOCK();
return retval;
}
/*
*===========================================================================
* ipradius_read
*===========================================================================
* Description: Parse incoming RADIUS packets from server.
* Parameters:
* Returns:
*
*/
IP_PUBLIC Ip_err
ipradius_read(void *buf, Ip_size_t len, struct Ip_sockaddr *from_addr)
{
Ipradius_header *hdr;
Ipradius_request_entry *reqent;
Ip_err retval;
IPRADIUS_LOCK();
ip_assert(ipradius.infunc == 0);
IP_ASSERTLINE(ipradius.infunc++);
if (ipradius.opened == IP_FALSE)
IPRADIUS_LEAVE(IPCOM_ERR_NOT_OPENED);
ip_assert(buf);
hdr = (Ipradius_header *)buf;
IPCOM_LOG3(DEBUG, " <- receiving RADIUS packet, code=%d, id=%d, length=%d",
hdr->code,
hdr->id,
ip_ntohs(hdr->length));
/*RFC 2865*/
if(ip_ntohs(hdr->length)< IPRADIUS_HDRSIZE ||ip_ntohs(hdr->length)> IPRADIUS_MAX_PKT_SIZE)
{
retval = IPCOM_ERR_FAILED;
goto leave ;
}
if(ip_ntohs(hdr->length)> len)
{
retval = IPCOM_ERR_FAILED;
goto leave ;
}
/* coverity[overrun-call] : FALSE */
retval = ipradius_packet_validate((union Ip_sockaddr_union *)from_addr, hdr, &reqent);
if (retval != IPCOM_SUCCESS OR reqent == IP_NULL)
goto leave;
ip_assert(reqent);
ip_assert(reqent->servent);
switch(hdr->code)
{
case IPRADIUS_CODE_Access_Accept :
reqent->servent->AuthAccessAccepts++;
reqent->servent->AuthRoundTripTime = (Ip_u32)(ipcom_time(0) - reqent->request_start_time);
retval = ipradius_access_accept(hdr, reqent);
break;
case IPRADIUS_CODE_Access_Reject :
reqent->servent->AuthAccessRejects++;
reqent->servent->AuthRoundTripTime = (Ip_u32)(ipcom_time(0) - reqent->request_start_time);
retval = ipradius_access_reject(hdr, reqent);
break;
case IPRADIUS_CODE_Access_Challenge :
reqent->servent->AuthAccessChallenges++;
reqent->servent->AuthRoundTripTime = (Ip_u32)(ipcom_time(0) - reqent->request_start_time);
retval = ipradius_access_challenge(hdr, reqent);
break;
case IPRADIUS_CODE_Accounting_Response :
reqent->servent->AccResponses++;
reqent->servent->AccRoundTripTime = (Ip_u32)(ipcom_time(0) - reqent->request_start_time);
retval = ipradius_accounting_response(hdr, reqent);
break;
case IPRADIUS_CODE_Accounting_Request :
retval = IPRADIUS_ERR_UNSUPPORTED_PACKET_CODE;
break;
default:
if (IPRADIUS_ACCESS_PACKET(&reqent->hdr))
{
reqent->servent->AuthPacketsDropped++;
reqent->servent->AuthUnknownTypes++;
}
else
{
reqent->servent->AccPacketsDropped++;
reqent->servent->AccUnknownTypes++;
}
retval = IPRADIUS_ERR_UNKNOWN_PACKET_CODE;
break;
}
leave:
ip_assert(ipradius.infunc == 1);
IP_ASSERTLINE(ipradius.infunc--);
IPRADIUS_UNLOCK();
return retval;
}
/*
*===========================================================================
* ipradius_get_numattempts
*===========================================================================
* Description: Returns the number of attempts made to reach the server for
* RADIUS packet pointed by 'packet'.
* Parameters:
* Returns:
*
*/
IP_PUBLIC Ip_err
ipradius_get_numattempts(Ipradius_header *packet, Ip_u32 *attempts)
{
Ipradius_request_entry *reqent;
Ip_u8 *pek;
if (ipradius.opened == IP_FALSE)
return IPCOM_ERR_NOT_OPENED;
if (packet == IP_NULL OR attempts == IP_NULL)
return IPCOM_ERR_INVALID_ARG;
/* Step back to get request entry pointer. */
pek = (Ip_u8 *)packet;
pek += sizeof(Ipradius_header);
pek -= sizeof(Ipradius_request_entry);
reqent = (Ipradius_request_entry *)pek;
/* Simple test to see if this is a request entry structure. */
if (((unsigned long)reqent & 0x3) || reqent->magic != IPRADIUS_REQUEST_ENTRY_MAGIC)
return IPCOM_ERR_INVALID_ARG;
/* Store number of attempts in attempts. */
*attempts = reqent->pollnum;
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipradius_create
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_PUBLIC Ip_err
ipradius_create(void)
{
/* init IPRADIUS */
ipradius_init();
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipradius_start
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_PUBLIC Ip_err
ipradius_start(void)
{
Ipradius_open conf;
char value_buf[64];
Ip_size_t value_size;
char *value;
/* Open IPRADIUS */
value_size = sizeof(value_buf);
value = ipcom_sysvar_get("ipradius.nas.identifier", value_buf, &value_size);
if ((value != IP_NULL)&&(ipcom_strlen(value)< IPRADIUS_MAX_NAS_IDENTIFIER_SIZE))
ipcom_strcpy(conf.ipradius_NAS_Identifier, value);
else
ipcom_strcpy(conf.ipradius_NAS_Identifier, "Interpeak NAS");
conf.ipradius_NAS_Identifier_size = (Ip_u32)ipcom_strlen(conf.ipradius_NAS_Identifier);
conf.access_request_attribute_size = IPRADIUS_access_request_attribute_size;
conf.accounting_request_attribute_size = IPRADIUS_accounting_request_attribute_size;
conf.conf[0] = IP_TAG_END;
return ipradius_open(&conf);
}
/*
*===========================================================================
* ipradius_version
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_PUBLIC const char *
ipradius_version(void)
{
return "@(#) IPRADIUS $Name: VXWORKS_ITER40_RCPL8_2024030303 $ - INTERPEAK_COPYRIGHT_STRING";
}
/*
****************************************************************************
* END OF FILE
****************************************************************************
*/