/* ****************************************************************************** * 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 ****************************************************************************** */ /* **************************************************************************** * 1 DESCRIPTION **************************************************************************** */ /* **************************************************************************** * 2 CONFIGURATION **************************************************************************** */ #include "ipradius_config.h" /* **************************************************************************** * 3 INCLUDE FILES **************************************************************************** */ #define IPCOM_USE_ANDORNOT #define IPCOM_USE_CLIB_PROTO /* ipcom */ #include #include #include #include #include #include #include #include #include #include #include #include #ifdef IPRADIUS_ENABLE_REENTRANT #include #endif /* ipradius */ #include "ipradius.h" /* IPCOM crypto */ #ifdef IPMCRYPTO #include #else #include #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, ""); 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) : "", 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 **************************************************************************** */