3435 lines
110 KiB
C
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
|
|
****************************************************************************
|
|
*/
|
|
|
|
|