diff options
Diffstat (limited to 'accel-pppd/radius')
29 files changed, 4293 insertions, 0 deletions
diff --git a/accel-pppd/radius/CMakeLists.txt b/accel-pppd/radius/CMakeLists.txt new file mode 100644 index 00000000..322bee51 --- /dev/null +++ b/accel-pppd/radius/CMakeLists.txt @@ -0,0 +1,19 @@ +SET(sources + dict.c + req.c + packet.c + auth.c + acct.c + dm_coa.c + radius.c +) + +ADD_DEFINITIONS(-DDICTIONARY="${CMAKE_INSTALL_PREFIX}/share/accel-ppp/radius/dictionary") + +ADD_LIBRARY(radius SHARED ${sources}) + +INSTALL(TARGETS radius + LIBRARY DESTINATION lib/accel-ppp +) +FILE(GLOB dict "${CMAKE_CURRENT_SOURCE_DIR}/dict/*") +INSTALL(FILES ${dict} DESTINATION share/accel-ppp/radius) diff --git a/accel-pppd/radius/acct.c b/accel-pppd/radius/acct.c new file mode 100644 index 00000000..ddb3e086 --- /dev/null +++ b/accel-pppd/radius/acct.c @@ -0,0 +1,328 @@ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <netinet/in.h> +#include "linux_ppp.h" + +#include <openssl/md5.h> + +#include "log.h" +#include "radius_p.h" + +#include "memdebug.h" + +#define STAT_UPDATE_INTERVAL (10 * 60 * 1000) + +static int req_set_RA(struct rad_req_t *req, const char *secret) +{ + MD5_CTX ctx; + + if (rad_packet_build(req->pack, req->RA)) + return -1; + + MD5_Init(&ctx); + MD5_Update(&ctx, req->pack->buf, req->pack->len); + MD5_Update(&ctx, secret, strlen(secret)); + MD5_Final(req->pack->buf + 4, &ctx); + + return 0; +} + +static void req_set_stat(struct rad_req_t *req, struct ppp_t *ppp) +{ + struct ifpppstatsreq ifreq; + time_t stop_time; + + if (ppp->stop_time) + stop_time = ppp->stop_time; + else + time(&stop_time); + + memset(&ifreq, 0, sizeof(ifreq)); + ifreq.stats_ptr = (void *)&ifreq.stats; + strcpy(ifreq.ifr__name, ppp->ifname); + + if (ioctl(sock_fd, SIOCGPPPSTATS, &ifreq)) { + log_ppp_error("radius: failed to get ppp statistics: %s\n", strerror(errno)); + return; + } + + if (ifreq.stats.p.ppp_ibytes < req->rpd->acct_input_octets) + req->rpd->acct_input_gigawords++; + req->rpd->acct_input_octets = ifreq.stats.p.ppp_ibytes; + + if (ifreq.stats.p.ppp_obytes < req->rpd->acct_output_octets) + req->rpd->acct_output_gigawords++; + req->rpd->acct_output_octets = ifreq.stats.p.ppp_obytes; + + rad_packet_change_int(req->pack, NULL, "Acct-Input-Octets", ifreq.stats.p.ppp_ibytes); + rad_packet_change_int(req->pack, NULL, "Acct-Output-Octets", ifreq.stats.p.ppp_obytes); + rad_packet_change_int(req->pack, NULL, "Acct-Input-Packets", ifreq.stats.p.ppp_ipackets); + rad_packet_change_int(req->pack, NULL, "Acct-Output-Packets", ifreq.stats.p.ppp_opackets); + rad_packet_change_int(req->pack, NULL, "Acct-Input-Gigawords", req->rpd->acct_input_gigawords); + rad_packet_change_int(req->pack, NULL, "Acct-Output-Gigawords", req->rpd->acct_output_gigawords); + rad_packet_change_int(req->pack, NULL, "Acct-Session-Time", stop_time - ppp->start_time); +} + +static int rad_acct_read(struct triton_md_handler_t *h) +{ + struct rad_req_t *req = container_of(h, typeof(*req), hnd); + struct rad_packet_t *pack; + int r; + + if (req->reply) { + rad_packet_free(req->reply); + req->reply = NULL; + } + + while (1) { + r = rad_packet_recv(h->fd, &pack, NULL); + + if (pack) { + if (req->reply) + rad_packet_free(req->reply); + req->reply = pack; + if (conf_interim_verbose) { + log_ppp_info2("recv "); + rad_packet_print(req->reply, log_ppp_info2); + } + } + + if (r) + break; + } + + if (!req->reply) + return 0; + + if (req->reply->code != CODE_ACCOUNTING_RESPONSE || req->reply->id != req->pack->id) { + rad_packet_free(req->reply); + req->reply = NULL; + } else { + if (req->timeout.tpd) + triton_timer_del(&req->timeout); + } + + return 0; +} + +static void rad_acct_timeout(struct triton_timer_t *t) +{ + struct rad_req_t *req = container_of(t, typeof(*req), timeout); + time_t ts, dt; + + __sync_add_and_fetch(&stat_interim_lost, 1); + + time(&ts); + + dt = ts - req->rpd->acct_timestamp; + + if (dt > conf_acct_timeout) { + log_ppp_warn("radius:acct: no response, terminating session...\n"); + ppp_terminate(req->rpd->ppp, TERM_NAS_ERROR, 0); + return; + } + if (dt > conf_acct_timeout / 2) { + req->timeout.period += 1000; + triton_timer_mod(&req->timeout, 0); + } else if (dt > conf_acct_timeout / 3) { + if (req->timeout.period != conf_timeout * 2000) { + req->timeout.period = conf_timeout * 2000; + triton_timer_mod(&req->timeout, 0); + } + } + + req->pack->id++; + + rad_packet_change_int(req->pack, NULL, "Acct-Delay-Time", dt); + req_set_RA(req, conf_acct_secret); + rad_req_send(req, conf_interim_verbose); + __sync_add_and_fetch(&stat_interim_sent, 1); +} + +static void rad_acct_interim_update(struct triton_timer_t *t) +{ + struct radius_pd_t *rpd = container_of(t, typeof(*rpd), acct_interim_timer); + + if (rpd->acct_req->timeout.tpd) + return; + + req_set_stat(rpd->acct_req, rpd->ppp); + if (!rpd->acct_interim_interval) + return; + + time(&rpd->acct_timestamp); + rpd->acct_req->pack->id++; + + rad_packet_change_val(rpd->acct_req->pack, NULL, "Acct-Status-Type", "Interim-Update"); + rad_packet_change_int(rpd->acct_req->pack, NULL, "Acct-Delay-Time", 0); + req_set_RA(rpd->acct_req, conf_acct_secret); + rad_req_send(rpd->acct_req, conf_interim_verbose); + __sync_add_and_fetch(&stat_interim_sent, 1); + if (conf_acct_timeout) { + rpd->acct_req->timeout.period = conf_timeout * 1000; + triton_timer_add(rpd->ppp->ctrl->ctx, &rpd->acct_req->timeout, 0); + } +} + +int rad_acct_start(struct radius_pd_t *rpd) +{ + int i; + time_t ts; + + rpd->acct_req = rad_req_alloc(rpd, CODE_ACCOUNTING_REQUEST, rpd->ppp->username); + if (!rpd->acct_req) { + log_emerg("radius: out of memory\n"); + return -1; + } + + if (rad_req_acct_fill(rpd->acct_req)) { + log_ppp_error("radius:acct: failed to fill accounting attributes\n"); + goto out_err; + } + + //if (rad_req_add_val(rpd->acct_req, "Acct-Status-Type", "Start", 4)) + // goto out_err; + //if (rad_req_add_str(rpd->acct_req, "Acct-Session-Id", rpd->ppp->sessionid, PPP_SESSIONID_LEN, 1)) + // goto out_err; + + if (rpd->acct_req->reply) { + rad_packet_free(rpd->acct_req->reply); + rpd->acct_req->reply = NULL; + } + + time(&rpd->acct_timestamp); + + for (i = 0; i < conf_max_try; i++) { + time(&ts); + rad_packet_change_int(rpd->acct_req->pack, NULL, "Acct-Delay-Time", ts - rpd->acct_timestamp); + if (req_set_RA(rpd->acct_req, conf_acct_secret)) + goto out_err; + if (rad_req_send(rpd->acct_req, conf_verbose)) + goto out_err; + __sync_add_and_fetch(&stat_acct_sent, 1); + rad_req_wait(rpd->acct_req, conf_timeout); + if (!rpd->acct_req->reply) { + rpd->acct_req->pack->id++; + __sync_add_and_fetch(&stat_acct_lost, 1); + continue; + } + if (rpd->acct_req->reply->id != rpd->acct_req->pack->id || rpd->acct_req->reply->code != CODE_ACCOUNTING_RESPONSE) { + rad_packet_free(rpd->acct_req->reply); + rpd->acct_req->reply = NULL; + rpd->acct_req->pack->id++; + __sync_add_and_fetch(&stat_acct_lost, 1); + } else + break; + } + + if (!rpd->acct_req->reply) { + log_ppp_warn("radius:acct_start: no response\n"); + goto out_err; + } + + rpd->acct_req->hnd.read = rad_acct_read; + + triton_md_register_handler(rpd->ppp->ctrl->ctx, &rpd->acct_req->hnd); + if (triton_md_enable_handler(&rpd->acct_req->hnd, MD_MODE_READ)) + goto out_err; + + rpd->acct_req->timeout.expire = rad_acct_timeout; + rpd->acct_req->timeout.period = conf_timeout * 1000; + + rpd->acct_interim_timer.expire = rad_acct_interim_update; + rpd->acct_interim_timer.period = rpd->acct_interim_interval ? rpd->acct_interim_interval * 1000 : STAT_UPDATE_INTERVAL; + if (rpd->acct_interim_interval && triton_timer_add(rpd->ppp->ctrl->ctx, &rpd->acct_interim_timer, 0)) { + triton_md_unregister_handler(&rpd->acct_req->hnd); + triton_timer_del(&rpd->acct_req->timeout); + goto out_err; + } + return 0; + +out_err: + rad_req_free(rpd->acct_req); + rpd->acct_req = NULL; + return -1; +} + +void rad_acct_stop(struct radius_pd_t *rpd) +{ + int i; + time_t ts; + + if (rpd->acct_interim_timer.tpd) + triton_timer_del(&rpd->acct_interim_timer); + + if (rpd->acct_req) { + triton_md_unregister_handler(&rpd->acct_req->hnd); + if (rpd->acct_req->timeout.tpd) + triton_timer_del(&rpd->acct_req->timeout); + + switch (rpd->ppp->terminate_cause) { + case TERM_USER_REQUEST: + rad_packet_add_val(rpd->acct_req->pack, NULL, "Acct-Terminate-Cause", "User-Request"); + break; + case TERM_SESSION_TIMEOUT: + rad_packet_add_val(rpd->acct_req->pack, NULL, "Acct-Terminate-Cause", "Session-Timeout"); + break; + case TERM_ADMIN_RESET: + rad_packet_add_val(rpd->acct_req->pack, NULL, "Acct-Terminate-Cause", "Admin-Reset"); + break; + case TERM_USER_ERROR: + case TERM_AUTH_ERROR: + rad_packet_add_val(rpd->acct_req->pack, NULL, "Acct-Terminate-Cause", "User-Error"); + break; + case TERM_NAS_ERROR: + rad_packet_add_val(rpd->acct_req->pack, NULL, "Acct-Terminate-Cause", "NAS-Error"); + break; + case TERM_NAS_REQUEST: + rad_packet_add_val(rpd->acct_req->pack, NULL, "Acct-Terminate-Cause", "NAS-Request"); + break; + case TERM_NAS_REBOOT: + rad_packet_add_val(rpd->acct_req->pack, NULL, "Acct-Terminate-Cause", "NAS-Reboot"); + break; + } + rad_packet_change_val(rpd->acct_req->pack, NULL, "Acct-Status-Type", "Stop"); + req_set_stat(rpd->acct_req, rpd->ppp); + req_set_RA(rpd->acct_req, conf_acct_secret); + /// !!! rad_req_add_val(rpd->acct_req, "Acct-Terminate-Cause", ""); + + if (rpd->acct_req->reply) { + rad_packet_free(rpd->acct_req->reply); + rpd->acct_req->reply = NULL; + } + + time(&rpd->acct_timestamp); + + for(i = 0; i < conf_max_try; i++) { + time(&ts); + rad_packet_change_int(rpd->acct_req->pack, NULL, "Acct-Delay-Time", ts - rpd->acct_timestamp); + rpd->acct_req->pack->id++; + if (req_set_RA(rpd->acct_req, conf_acct_secret)) + break; + if (rad_req_send(rpd->acct_req, conf_verbose)) + break; + __sync_add_and_fetch(&stat_acct_sent, 1); + rad_req_wait(rpd->acct_req, conf_timeout); + if (!rpd->acct_req->reply) { + __sync_add_and_fetch(&stat_acct_lost, 1); + continue; + } + if (rpd->acct_req->reply->id != rpd->acct_req->pack->id || rpd->acct_req->reply->code != CODE_ACCOUNTING_RESPONSE) { + rad_packet_free(rpd->acct_req->reply); + rpd->acct_req->reply = NULL; + __sync_add_and_fetch(&stat_acct_lost, 1); + } else + break; + } + if (!rpd->acct_req->reply) + log_ppp_warn("radius:acct_stop: no response\n"); + + rad_req_free(rpd->acct_req); + rpd->acct_req = NULL; + } +} + diff --git a/accel-pppd/radius/attr_defs.h b/accel-pppd/radius/attr_defs.h new file mode 100644 index 00000000..e9b617e4 --- /dev/null +++ b/accel-pppd/radius/attr_defs.h @@ -0,0 +1,287 @@ +#define User_Name 1 +#define User_Password 2 +#define CHAP_Password 3 +#define NAS_IP_Address 4 +#define NAS_Port 5 +#define Service_Type 6 +#define Framed_Protocol 7 +#define Framed_IP_Address 8 +#define Framed_IP_Netmask 9 +#define Framed_Routing 10 +#define Filter_Id 11 +#define Framed_MTU 12 +#define Framed_Compression 13 +#define Login_IP_Host 14 +#define Login_Service 15 +#define Login_TCP_Port 16 +#define Reply_Message 18 +#define Callback_Number 19 +#define Callback_Id 20 +#define Framed_Route 22 +#define Framed_IPX_Network 23 +#define State 24 +#define Class 25 +#define Vendor_Specific 26 +#define Session_Timeout 27 +#define Idle_Timeout 28 +#define Termination_Action 29 +#define Called_Station_Id 30 +#define Calling_Station_Id 31 +#define NAS_Identifier 32 +#define Proxy_State 33 +#define Login_LAT_Service 34 +#define Login_LAT_Node 35 +#define Login_LAT_Group 36 +#define Framed_AppleTalk_Link 37 +#define Framed_AppleTalk_Network 38 +#define Framed_AppleTalk_Zone 39 +#define CHAP_Challenge 60 +#define NAS_Port_Type 61 +#define Port_Limit 62 +#define Login_LAT_Port 63 +#define Service_Type_Login_User 1 +#define Service_Type_Framed_User 2 +#define Service_Type_Callback_Login_User 3 +#define Service_Type_Callback_Framed_User 4 +#define Service_Type_Outbound_User 5 +#define Service_Type_Administrative_User 6 +#define Service_Type_NAS_Prompt_User 7 +#define Service_Type_Authenticate_Only 8 +#define Service_Type_Callback_NAS_Prompt 9 +#define Service_Type_Call_Check 10 +#define Service_Type_Callback_Administrative 11 +#define Framed_Protocol_PPP 1 +#define Framed_Protocol_SLIP 2 +#define Framed_Protocol_ARAP 3 +#define Framed_Protocol_Gandalf_SLML 4 +#define Framed_Protocol_Xylogics_IPX_SLIP 5 +#define Framed_Protocol_X_75_Synchronous 6 +#define Framed_Routing_None 0 +#define Framed_Routing_Broadcast 1 +#define Framed_Routing_Listen 2 +#define Framed_Routing_Broadcast_Listen 3 +#define Framed_Compression_None 0 +#define Framed_Compression_Van_Jacobson_TCP_IP 1 +#define Framed_Compression_IPX_Header_Compression 2 +#define Framed_Compression_Stac_LZS 3 +#define Login_Service_Telnet 0 +#define Login_Service_Rlogin 1 +#define Login_Service_TCP_Clear 2 +#define Login_Service_PortMaster 3 +#define Login_Service_LAT 4 +#define Login_Service_X25_PAD 5 +#define Login_Service_X25_T3POS 6 +#define Login_Service_TCP_Clear_Quiet 8 +#define Login_TCP_Port_Telnet 23 +#define Login_TCP_Port_Rlogin 513 +#define Login_TCP_Port_Rsh 514 +#define Termination_Action_Default 0 +#define Termination_Action_RADIUS_Request 1 +#define NAS_Port_Type_Async 0 +#define NAS_Port_Type_Sync 1 +#define NAS_Port_Type_ISDN 2 +#define NAS_Port_Type_ISDN_V120 3 +#define NAS_Port_Type_ISDN_V110 4 +#define NAS_Port_Type_Virtual 5 +#define NAS_Port_Type_PIAFS 6 +#define NAS_Port_Type_HDLC_Clear_Channel 7 +#define NAS_Port_Type_X_25 8 +#define NAS_Port_Type_X_75 9 +#define NAS_Port_Type_G_3_Fax 10 +#define NAS_Port_Type_SDSL 11 +#define NAS_Port_Type_ADSL_CAP 12 +#define NAS_Port_Type_ADSL_DMT 13 +#define NAS_Port_Type_IDSL 14 +#define NAS_Port_Type_Ethernet 15 +#define NAS_Port_Type_xDSL 16 +#define NAS_Port_Type_Cable 17 +#define NAS_Port_Type_Wireless_Other 18 +#define NAS_Port_Type_Wireless_802_11 19 +#define Acct_Status_Type 40 +#define Acct_Delay_Time 41 +#define Acct_Input_Octets 42 +#define Acct_Output_Octets 43 +#define Acct_Session_Id 44 +#define Acct_Authentic 45 +#define Acct_Session_Time 46 +#define Acct_Input_Packets 47 +#define Acct_Output_Packets 48 +#define Acct_Terminate_Cause 49 +#define Acct_Multi_Session_Id 50 +#define Acct_Link_Count 51 +#define Acct_Status_Type_Start 1 +#define Acct_Status_Type_Stop 2 +#define Acct_Status_Type_Alive 3 +#define Acct_Status_Type_Interim_Update 3 +#define Acct_Status_Type_Accounting_On 7 +#define Acct_Status_Type_Accounting_Off 8 +#define Acct_Status_Type_Failed 15 +#define Acct_Authentic_RADIUS 1 +#define Acct_Authentic_Local 2 +#define Acct_Authentic_Remote 3 +#define Acct_Authentic_Diameter 4 +#define Acct_Terminate_Cause_User_Request 1 +#define Acct_Terminate_Cause_Lost_Carrier 2 +#define Acct_Terminate_Cause_Lost_Service 3 +#define Acct_Terminate_Cause_Idle_Timeout 4 +#define Acct_Terminate_Cause_Session_Timeout 5 +#define Acct_Terminate_Cause_Admin_Reset 6 +#define Acct_Terminate_Cause_Admin_Reboot 7 +#define Acct_Terminate_Cause_Port_Error 8 +#define Acct_Terminate_Cause_NAS_Error 9 +#define Acct_Terminate_Cause_NAS_Request 10 +#define Acct_Terminate_Cause_NAS_Reboot 11 +#define Acct_Terminate_Cause_Port_Unneeded 12 +#define Acct_Terminate_Cause_Port_Preempted 13 +#define Acct_Terminate_Cause_Port_Suspended 14 +#define Acct_Terminate_Cause_Service_Unavailable 15 +#define Acct_Terminate_Cause_Callback 16 +#define Acct_Terminate_Cause_User_Error 17 +#define Acct_Terminate_Cause_Host_Request 18 +#define Acct_Tunnel_Connection 68 +#define Acct_Tunnel_Packets_Lost 86 +#define Acct_Status_Type_Tunnel_Start 9 +#define Acct_Status_Type_Tunnel_Stop 10 +#define Acct_Status_Type_Tunnel_Reject 11 +#define Acct_Status_Type_Tunnel_Link_Start 12 +#define Acct_Status_Type_Tunnel_Link_Stop 13 +#define Acct_Status_Type_Tunnel_Link_Reject 14 +#define Tunnel_Type 64 +#define Tunnel_Medium_Type 65 +#define Tunnel_Client_Endpoint 66 +#define Tunnel_Server_Endpoint 67 +#define Tunnel_Password 69 +#define Tunnel_Private_Group_Id 81 +#define Tunnel_Assignment_Id 82 +#define Tunnel_Preference 83 +#define Tunnel_Client_Auth_Id 90 +#define Tunnel_Server_Auth_Id 91 +#define Tunnel_Type_PPTP 1 +#define Tunnel_Type_L2F 2 +#define Tunnel_Type_L2TP 3 +#define Tunnel_Type_ATMP 4 +#define Tunnel_Type_VTP 5 +#define Tunnel_Type_AH 6 +#define Tunnel_Type_IP 7 +#define Tunnel_Type_MIN_IP 8 +#define Tunnel_Type_ESP 9 +#define Tunnel_Type_GRE 10 +#define Tunnel_Type_DVS 11 +#define Tunnel_Type_IP_in_IP 12 +#define Tunnel_Medium_Type_IP 1 +#define Tunnel_Medium_Type_IPv4 1 +#define Tunnel_Medium_Type_IPv6 2 +#define Tunnel_Medium_Type_NSAP 3 +#define Tunnel_Medium_Type_HDLC 4 +#define Tunnel_Medium_Type_BBN_1822 5 +#define Tunnel_Medium_Type_IEEE_802 6 +#define Tunnel_Medium_Type_E_163 7 +#define Tunnel_Medium_Type_E_164 8 +#define Tunnel_Medium_Type_F_69 9 +#define Tunnel_Medium_Type_X_121 10 +#define Tunnel_Medium_Type_IPX 11 +#define Tunnel_Medium_Type_Appletalk 12 +#define Tunnel_Medium_Type_DecNet_IV 13 +#define Tunnel_Medium_Type_Banyan_Vines 14 +#define Tunnel_Medium_Type_E_164_NSAP 15 +#define Acct_Input_Gigawords 52 +#define Acct_Output_Gigawords 53 +#define Event_Timestamp 55 +#define ARAP_Password 70 +#define ARAP_Features 71 +#define ARAP_Zone_Access 72 +#define ARAP_Security 73 +#define ARAP_Security_Data 74 +#define Password_Retry 75 +#define Prompt 76 +#define Connect_Info 77 +#define Configuration_Token 78 +#define EAP_Message 79 +#define Message_Authenticator 80 +#define ARAP_Challenge_Response 84 +#define Acct_Interim_Interval 85 +#define NAS_Port_Id 87 +#define Framed_Pool 88 +#define ARAP_Zone_Access_Default_Zone 1 +#define ARAP_Zone_Access_Zone_Filter_Inclusive 2 +#define ARAP_Zone_Access_Zone_Filter_Exclusive 4 +#define Prompt_No_Echo 0 +#define Prompt_Echo 1 +#define Error_Cause 101 +#define Service_Type_Authorize_Only 17 +#define Error_Cause_Residual_Context_Removed 201 +#define Error_Cause_Invalid_EAP_Packet 202 +#define Error_Cause_Unsupported_Attribute 401 +#define Error_Cause_Missing_Attribute 402 +#define Error_Cause_NAS_Identification_Mismatch 403 +#define Error_Cause_Invalid_Request 404 +#define Error_Cause_Unsupported_Service 405 +#define Error_Cause_Unsupported_Extension 406 +#define Error_Cause_Administratively_Prohibited 501 +#define Error_Cause_Proxy_Request_Not_Routable 502 +#define Error_Cause_Session_Context_Not_Found 503 +#define Error_Cause_Session_Context_Not_Removable 504 +#define Error_Cause_Proxy_Processing_Error 505 +#define Error_Cause_Resources_Unavailable 506 +#define Error_Cause_Request_Initiated 507 +#define Acct_Terminate_Cause_Supplicant_Restart 19 +#define Acct_Terminate_Cause_Reauthentication_Failure 20 +#define Acct_Terminate_Cause_Port_Reinit 21 +#define Acct_Terminate_Cause_Port_Disabled 22 +#define NAS_Port_Type_Token_Ring 20 +#define NAS_Port_Type_FDDI 21 +#define Tunnel_Type_VLAN 13 +#define EAP_Key_Name 102 +#define Chargeable_User_Identity 89 +#define Error_Cause_Invalid_Attribute_Value 407 +#define Error_Cause_Multiple_Session_Selection_Unsupported 508 +#define Vendor_Microsoft 311 +#define MS_CHAP_Response 1 +#define MS_CHAP_Error 2 +#define MS_CHAP_CPW_1 3 +#define MS_CHAP_CPW_2 4 +#define MS_CHAP_LM_Enc_PW 5 +#define MS_CHAP_NT_Enc_PW 6 +#define MS_MPPE_Encryption_Policy 7 +#define MS_MPPE_Encryption_Type 8 +#define MS_MPPE_Encryption_Types 8 +#define MS_RAS_Vendor 9 +#define MS_CHAP_Domain 10 +#define MS_CHAP_Challenge 11 +#define MS_CHAP_MPPE_Keys 12 +#define MS_BAP_Usage 13 +#define MS_Link_Utilization_Threshold 14 +#define MS_Link_Drop_Time_Limit 15 +#define MS_MPPE_Send_Key 16 +#define MS_MPPE_Recv_Key 17 +#define MS_RAS_Version 18 +#define MS_Old_ARAP_Password 19 +#define MS_New_ARAP_Password 20 +#define MS_ARAP_PW_Change_Reason 21 +#define MS_Filter 22 +#define MS_Acct_Auth_Type 23 +#define MS_Acct_EAP_Type 24 +#define MS_CHAP2_Response 25 +#define MS_CHAP2_Success 26 +#define MS_CHAP2_CPW 27 +#define MS_Primary_DNS_Server 28 +#define MS_Secondary_DNS_Server 29 +#define MS_Primary_NBNS_Server 30 +#define MS_Secondary_NBNS_Server 31 +#define MS_BAP_Usage_Not_Allowed 0 +#define MS_BAP_Usage_Allowed 1 +#define MS_BAP_Usage_Required 2 +#define MS_ARAP_PW_Change_Reason_Just_Change_Password 1 +#define MS_ARAP_PW_Change_Reason_Expired_Password 2 +#define MS_ARAP_PW_Change_Reason_Admin_Requires_Password_Change 3 +#define MS_ARAP_PW_Change_Reason_Password_Too_Short 4 +#define MS_Acct_Auth_Type_PAP 1 +#define MS_Acct_Auth_Type_CHAP 2 +#define MS_Acct_Auth_Type_MS_CHAP_1 3 +#define MS_Acct_Auth_Type_MS_CHAP_2 4 +#define MS_Acct_Auth_Type_EAP 5 +#define MS_Acct_EAP_Type_MD5 4 +#define MS_Acct_EAP_Type_OTP 5 +#define MS_Acct_EAP_Type_Generic_Token_Card 6 +#define MS_Acct_EAP_Type_TLS 13 +#define Traffic_Shape_in 231 diff --git a/accel-pppd/radius/auth.c b/accel-pppd/radius/auth.c new file mode 100644 index 00000000..692830ed --- /dev/null +++ b/accel-pppd/radius/auth.c @@ -0,0 +1,497 @@ +#include <stdlib.h> +#include <string.h> +#include <openssl/md5.h> +#include <openssl/sha.h> + +#include "triton.h" +#include "events.h" +#include "log.h" +#include "pwdb.h" + +#include "radius_p.h" +#include "attr_defs.h" + +#include "memdebug.h" + +static int decrypt_chap_mppe_keys(struct rad_req_t *req, struct rad_attr_t *attr, const uint8_t *challenge, uint8_t *key) +{ + MD5_CTX md5_ctx; + SHA_CTX sha1_ctx; + uint8_t md5[MD5_DIGEST_LENGTH]; + uint8_t sha1[SHA_DIGEST_LENGTH]; + uint8_t plain[32]; + int i; + + if (attr->len != 32) { + log_ppp_warn("radius: %s: incorrect attribute length (%i)\n", attr->attr->name, attr->len); + return -1; + } + + memcpy(plain, attr->val.octets, 32); + + MD5_Init(&md5_ctx); + MD5_Update(&md5_ctx, conf_auth_secret, strlen(conf_auth_secret)); + MD5_Update(&md5_ctx, req->pack->buf + 4, 16); + MD5_Final(md5, &md5_ctx); + + for (i = 0; i < 16; i++) + plain[i] ^= md5[i]; + + MD5_Init(&md5_ctx); + MD5_Update(&md5_ctx, conf_auth_secret, strlen(conf_auth_secret)); + MD5_Update(&md5_ctx, attr->val.octets, 16); + MD5_Final(md5, &md5_ctx); + + for (i = 0; i < 16; i++) + plain[i + 16] ^= md5[i]; + + SHA1_Init(&sha1_ctx); + SHA1_Update(&sha1_ctx, plain + 8, 16); + SHA1_Update(&sha1_ctx, plain + 8, 16); + SHA1_Update(&sha1_ctx, challenge, 8); + SHA1_Final(sha1, &sha1_ctx); + + memcpy(key, sha1, 16); + + return 0; +} + +static int decrypt_mppe_key(struct rad_req_t *req, struct rad_attr_t *attr, uint8_t *key) +{ + MD5_CTX md5_ctx; + uint8_t md5[16]; + uint8_t plain[32]; + int i; + + if (attr->len != 34) { + log_ppp_warn("radius: %s: incorrect attribute length (%i)\n", attr->attr->name, attr->len); + return -1; + } + + if ((attr->val.octets[0] & 0x80) == 0) { + log_ppp_warn("radius: %s: incorrect salt value (%x)\n", attr->attr->name, attr->len); + return -1; + } + + MD5_Init(&md5_ctx); + MD5_Update(&md5_ctx, conf_auth_secret, strlen(conf_auth_secret)); + MD5_Update(&md5_ctx, req->pack->buf + 4, 16); + MD5_Update(&md5_ctx, attr->val.octets, 2); + MD5_Final(md5, &md5_ctx); + + memcpy(plain, attr->val.octets + 2, 32); + + for (i = 0; i < 16; i++) + plain[i] ^= md5[i]; + + if (plain[0] != 16) { + log_ppp_warn("radius: %s: incorrect key length (%i)\n", attr->attr->name, plain[0]); + return -1; + } + + MD5_Init(&md5_ctx); + MD5_Update(&md5_ctx, conf_auth_secret, strlen(conf_auth_secret)); + MD5_Update(&md5_ctx, attr->val.octets + 2, 16); + MD5_Final(md5, &md5_ctx); + + plain[16] ^= md5[0]; + + memcpy(key, plain + 1, 16); + + return 0; +} + + +static uint8_t* encrypt_password(const char *passwd, const char *secret, const uint8_t *RA, int *epasswd_len) +{ + uint8_t *epasswd; + int i, j, chunk_cnt; + uint8_t b[16], c[16]; + MD5_CTX ctx; + + if (strlen(passwd)) + chunk_cnt = (strlen(passwd) - 1) / 16 + 1; + else { + *epasswd_len = 0; + return (uint8_t *)1; + } + + epasswd = _malloc(chunk_cnt * 16); + if (!epasswd) { + log_emerg("radius: out of memory\n"); + return NULL; + } + + memset(epasswd, 0, chunk_cnt * 16); + memcpy(epasswd, passwd, strlen(passwd)); + memcpy(c, RA, 16); + + for (i = 0; i < chunk_cnt; i++) { + MD5_Init(&ctx); + MD5_Update(&ctx, secret, strlen(secret)); + MD5_Update(&ctx, c, 16); + MD5_Final(b, &ctx); + + for(j = 0; j < 16; j++) + epasswd[i * 16 + j] ^= b[j]; + + memcpy(c, epasswd + i * 16, 16); + } + + *epasswd_len = chunk_cnt * 16; + return epasswd; +} + +static int rad_auth_send(struct rad_req_t *req) +{ + int i; + + for(i = 0; i < conf_max_try; i++) { + __sync_add_and_fetch(&stat_auth_sent, 1); + if (rad_req_send(req, conf_verbose)) + goto out; + + rad_req_wait(req, conf_timeout); + + if (req->reply) { + if (req->reply->id != req->pack->id) { + __sync_add_and_fetch(&stat_auth_lost, 1); + rad_packet_free(req->reply); + req->reply = NULL; + } else + break; + } else + __sync_add_and_fetch(&stat_auth_lost, 1); + } + + if (!req->reply) + log_ppp_warn("radius:auth: no response\n"); + else if (req->reply->code == CODE_ACCESS_ACCEPT) { + if (rad_proc_attrs(req)) + return PWDB_DENIED; + return PWDB_SUCCESS; + } + +out: + return PWDB_DENIED; +} + +int rad_auth_pap(struct radius_pd_t *rpd, const char *username, va_list args) +{ + struct rad_req_t *req; + int r = PWDB_DENIED; + //int id = va_arg(args, int); + const char *passwd = va_arg(args, const char *); + uint8_t *epasswd; + int epasswd_len; + + req = rad_req_alloc(rpd, CODE_ACCESS_REQUEST, username); + if (!req) + return PWDB_DENIED; + + epasswd = encrypt_password(passwd, conf_auth_secret, req->RA, &epasswd_len); + if (!epasswd) + goto out; + + if (rad_packet_add_octets(req->pack, NULL, "User-Password", epasswd, epasswd_len)) { + if (epasswd_len) + _free(epasswd); + goto out; + } + + if (epasswd_len) + _free(epasswd); + + if (conf_sid_in_auth) + if (rad_packet_add_str(req->pack, NULL, "Acct-Session-Id", rpd->ppp->sessionid)) + return -1; + + r = rad_auth_send(req); + if (r == PWDB_SUCCESS) { + struct ev_radius_t ev = { + .ppp = rpd->ppp, + .request = req->pack, + .reply = req->reply, + }; + triton_event_fire(EV_RADIUS_ACCESS_ACCEPT, &ev); + } + +out: + rad_req_free(req); + + return r; +} + +int rad_auth_chap_md5(struct radius_pd_t *rpd, const char *username, va_list args) +{ + int r = PWDB_DENIED; + uint8_t chap_password[17]; + + int id = va_arg(args, int); + uint8_t *challenge = va_arg(args, uint8_t *); + int challenge_len = va_arg(args, int); + uint8_t *response = va_arg(args, uint8_t *); + + chap_password[0] = id; + memcpy(chap_password + 1, response, 16); + + if (!rpd->auth_req) { + rpd->auth_req = rad_req_alloc(rpd, CODE_ACCESS_REQUEST, username); + if (!rpd->auth_req) + return PWDB_DENIED; + + if (challenge_len == 16) + memcpy(rpd->auth_req->RA, challenge, 16); + else { + if (rad_packet_add_octets(rpd->auth_req->pack, NULL, "CHAP-Challenge", challenge, challenge_len)) + goto out; + } + + if (rad_packet_add_octets(rpd->auth_req->pack, NULL, "CHAP-Password", chap_password, 17)) + goto out; + } else { + if (challenge_len == 16) + memcpy(rpd->auth_req->RA, challenge, 16); + else { + if (rad_packet_change_octets(rpd->auth_req->pack, NULL, "CHAP-Challenge", challenge, challenge_len)) + goto out; + } + + if (rad_packet_change_octets(rpd->auth_req->pack, NULL, "CHAP-Password", chap_password, 17)) + goto out; + + if (rpd->attr_state) { + if (rad_packet_find_attr(rpd->auth_req->pack, NULL, "State")) { + if (rad_packet_change_octets(rpd->auth_req->pack, NULL, "State", rpd->attr_state, rpd->attr_state_len)) + goto out; + } else { + if (rad_packet_add_octets(rpd->auth_req->pack, NULL, "State", rpd->attr_state, rpd->attr_state_len)) + goto out; + } + } + + if (rad_packet_build(rpd->auth_req->pack, rpd->auth_req->RA)) + return -1; + } + + if (conf_sid_in_auth) + if (rad_packet_add_str(rpd->auth_req->pack, NULL, "Acct-Session-Id", rpd->ppp->sessionid)) + goto out; + + r = rad_auth_send(rpd->auth_req); + if (r == PWDB_SUCCESS) { + struct ev_radius_t ev = { + .ppp = rpd->ppp, + .request = rpd->auth_req->pack, + .reply = rpd->auth_req->reply, + }; + triton_event_fire(EV_RADIUS_ACCESS_ACCEPT, &ev); + rpd->auth_req->pack->id++; + } + + return r; +out: + rad_req_free(rpd->auth_req); + rpd->auth_req = NULL; + + return r; +} + +static void setup_mppe(struct rad_req_t *req, const uint8_t *challenge) +{ + struct rad_attr_t *attr; + uint8_t mppe_recv_key[16]; + uint8_t mppe_send_key[16]; + struct ev_mppe_keys_t ev_mppe = { + .ppp = req->rpd->ppp, + }; + + list_for_each_entry(attr, &req->reply->attrs, entry) { + if (attr->vendor && attr->vendor->id == Vendor_Microsoft) { + switch (attr->attr->id) { + case MS_CHAP_MPPE_Keys: + if (decrypt_chap_mppe_keys(req, attr, challenge, mppe_recv_key)) + continue; + ev_mppe.recv_key = mppe_recv_key; + ev_mppe.send_key = mppe_recv_key; + break; + case MS_MPPE_Recv_Key: + if (decrypt_mppe_key(req, attr, mppe_recv_key)) + continue; + ev_mppe.recv_key = mppe_recv_key; + break; + case MS_MPPE_Send_Key: + if (decrypt_mppe_key(req, attr, mppe_send_key)) + continue; + ev_mppe.send_key = mppe_send_key; + break; + case MS_MPPE_Encryption_Policy: + ev_mppe.policy = attr->val.integer; + break; + case MS_MPPE_Encryption_Type: + ev_mppe.type = attr->val.integer; + break; + } + } + } + + if (ev_mppe.recv_key && ev_mppe.send_key) + triton_event_fire(EV_MPPE_KEYS, &ev_mppe); +} + +int rad_auth_mschap_v1(struct radius_pd_t *rpd, const char *username, va_list args) +{ + int r = PWDB_DENIED; + uint8_t response[50]; + + int id = va_arg(args, int); + const uint8_t *challenge = va_arg(args, const uint8_t *); + int challenge_len = va_arg(args, int); + const uint8_t *lm_response = va_arg(args, const uint8_t *); + const uint8_t *nt_response = va_arg(args, const uint8_t *); + int flags = va_arg(args, int); + + response[0] = id; + response[1] = flags; + memcpy(response + 2, lm_response, 24); + memcpy(response + 2 + 24, nt_response, 24); + + if (!rpd->auth_req) { + rpd->auth_req = rad_req_alloc(rpd, CODE_ACCESS_REQUEST, username); + if (!rpd->auth_req) + return PWDB_DENIED; + + if (rad_packet_add_octets(rpd->auth_req->pack, "Microsoft", "MS-CHAP-Challenge", challenge, challenge_len)) + goto out; + + if (rad_packet_add_octets(rpd->auth_req->pack, "Microsoft", "MS-CHAP-Response", response, sizeof(response))) + goto out; + } else { + if (rad_packet_change_octets(rpd->auth_req->pack, "Microsoft", "MS-CHAP-Challenge", challenge, challenge_len)) + goto out; + + if (rad_packet_change_octets(rpd->auth_req->pack, "Microsoft", "MS-CHAP-Response", response, sizeof(response))) + goto out; + + if (rpd->attr_state) { + if (rad_packet_find_attr(rpd->auth_req->pack, NULL, "State")) { + if (rad_packet_change_octets(rpd->auth_req->pack, NULL, "State", rpd->attr_state, rpd->attr_state_len)) + goto out; + } else { + if (rad_packet_add_octets(rpd->auth_req->pack, NULL, "State", rpd->attr_state, rpd->attr_state_len)) + goto out; + } + } + + if (rad_packet_build(rpd->auth_req->pack, rpd->auth_req->RA)) + return -1; + } + + if (conf_sid_in_auth) + if (rad_packet_add_str(rpd->auth_req->pack, NULL, "Acct-Session-Id", rpd->ppp->sessionid)) + goto out; + + r = rad_auth_send(rpd->auth_req); + if (r == PWDB_SUCCESS) { + struct ev_radius_t ev = { + .ppp = rpd->ppp, + .request = rpd->auth_req->pack, + .reply = rpd->auth_req->reply, + }; + triton_event_fire(EV_RADIUS_ACCESS_ACCEPT, &ev); + setup_mppe(rpd->auth_req, challenge); + rpd->auth_req->pack->id++; + } + + return r; +out: + rad_req_free(rpd->auth_req); + rpd->auth_req = NULL; + + return r; +} + +int rad_auth_mschap_v2(struct radius_pd_t *rpd, const char *username, va_list args) +{ + int r = PWDB_DENIED; + struct rad_attr_t *ra; + uint8_t mschap_response[50]; + + int id = va_arg(args, int); + const uint8_t *challenge = va_arg(args, const uint8_t *); + const uint8_t *peer_challenge = va_arg(args, const uint8_t *); + const uint8_t *reserved = va_arg(args, const uint8_t *); + const uint8_t *response = va_arg(args, const uint8_t *); + int flags = va_arg(args, int); + uint8_t *authenticator = va_arg(args, uint8_t *); + + mschap_response[0] = id; + mschap_response[1] = flags; + memcpy(mschap_response + 2, peer_challenge, 16); + memcpy(mschap_response + 2 + 16, reserved, 8); + memcpy(mschap_response + 2 + 16 + 8, response, 24); + + if (!rpd->auth_req) { + rpd->auth_req = rad_req_alloc(rpd, CODE_ACCESS_REQUEST, username); + if (!rpd->auth_req) + return PWDB_DENIED; + + if (rad_packet_add_octets(rpd->auth_req->pack, "Microsoft", "MS-CHAP-Challenge", challenge, 16)) + goto out; + + if (rad_packet_add_octets(rpd->auth_req->pack, "Microsoft", "MS-CHAP2-Response", mschap_response, sizeof(mschap_response))) + goto out; + } else { + if (rad_packet_change_octets(rpd->auth_req->pack, "Microsoft", "MS-CHAP-Challenge", challenge, 16)) + goto out; + + if (rad_packet_change_octets(rpd->auth_req->pack, "Microsoft", "MS-CHAP2-Response", mschap_response, sizeof(mschap_response))) + goto out; + + if (rpd->attr_state) { + if (rad_packet_find_attr(rpd->auth_req->pack, NULL, "State")) { + if (rad_packet_change_octets(rpd->auth_req->pack, NULL, "State", rpd->attr_state, rpd->attr_state_len)) + goto out; + } else { + if (rad_packet_add_octets(rpd->auth_req->pack, NULL, "State", rpd->attr_state, rpd->attr_state_len)) + goto out; + } + } + + if (rad_packet_build(rpd->auth_req->pack, rpd->auth_req->RA)) + return -1; + } + + if (conf_sid_in_auth) + if (rad_packet_add_str(rpd->auth_req->pack, NULL, "Acct-Session-Id", rpd->ppp->sessionid)) + goto out; + + r = rad_auth_send(rpd->auth_req); + if (r == PWDB_SUCCESS) { + ra = rad_packet_find_attr(rpd->auth_req->reply, "Microsoft", "MS-CHAP2-Success"); + if (!ra) { + log_error("radius:auth:mschap-v2: 'MS-CHAP-Success' not found in radius response\n"); + r = PWDB_DENIED; + } else + memcpy(authenticator, ra->val.octets + 3, 40); + } + if (r == PWDB_SUCCESS) { + struct ev_radius_t ev = { + .ppp = rpd->ppp, + .request = rpd->auth_req->pack, + .reply = rpd->auth_req->reply, + }; + triton_event_fire(EV_RADIUS_ACCESS_ACCEPT, &ev); + setup_mppe(rpd->auth_req, NULL); + rpd->auth_req->pack->id++; + } + + return r; +out: + rad_req_free(rpd->auth_req); + rpd->auth_req = NULL; + + return r; +} + + diff --git a/accel-pppd/radius/dict.c b/accel-pppd/radius/dict.c new file mode 100644 index 00000000..9704569e --- /dev/null +++ b/accel-pppd/radius/dict.c @@ -0,0 +1,356 @@ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <limits.h> + +#include "list.h" +#include "radius_p.h" +#include "log.h" + +#include "memdebug.h" + +static struct rad_dict_t *dict; + +static char *skip_word(char *ptr) +{ + for(; *ptr; ptr++) + if (*ptr == ' ' || *ptr == '\t' || *ptr == '\n') + break; + return ptr; +} +static char *skip_space(char *ptr) +{ + for(; *ptr; ptr++) + if (*ptr != ' ' && *ptr != '\t') + break; + return ptr; +} +static int split(char *buf, char **ptr) +{ + int i; + + for (i = 0; i < 3; i++) { + buf = skip_word(buf); + if (!*buf) + return i; + + *buf = 0; + + buf = skip_space(buf + 1); + if (!*buf) + return i; + + ptr[i] = buf; + } + + buf = skip_word(buf); + //if (*buf == '\n') + *buf = 0; + //else if (*buf) + // return -1; + + return i; +} + +struct rad_dict_attr_t *find_attr(struct list_head *items, const char *name) +{ + struct rad_dict_attr_t *attr; + + list_for_each_entry(attr, items, entry) + if (!strcmp(attr->name, name)) + return attr; + + return NULL; +} + +#define BUF_SIZE 1024 + +static char *path, *fname1, *buf; +static int dict_load(const char *fname) +{ + FILE *f; + char *ptr[3], *endptr; + int r, n = 0; + struct rad_dict_attr_t *attr; + struct rad_dict_value_t *val; + struct rad_dict_vendor_t *vendor; + struct list_head *items; + + f = fopen(fname, "r"); + if (!f) { + log_emerg("radius: open dictioanary '%s': %s\n", fname, strerror(errno)); + return -1; + } + + items = &dict->items; + + while (fgets(buf, BUF_SIZE, f)) { + n++; + if (buf[0] == '#' || buf[0] == '\n' || buf[0] == 0) + continue; + r = split(buf, ptr); + if (r == 1) { + if (!strcmp(buf, "BEGIN-VENDOR")) { + vendor = rad_dict_find_vendor_name(ptr[0]); + if (!vendor) { + log_emerg("radius:%s:%i: vendor not found\n", fname, n); + goto out_err; + } + items = &vendor->items; + } else if (!strcmp(buf, "END-VENDOR")) + items = &dict->items; + else if (!strcmp(buf, "$INCLUDE")) { + for (r = strlen(path) - 1; r; r--) + if (path[r] == '/') { + path[r + 1] = 0; + break; + } + strcpy(fname1, path); + strcat(fname1, ptr[0]); + if (dict_load(fname1)) + goto out_err; + } else + goto out_err_syntax; + } else if (r == 2) { + if (!strcmp(buf, "VENDOR")) { + vendor = malloc(sizeof(*vendor)); + if (!vendor) { + log_emerg("radius: out of memory\n"); + goto out_err; + } + vendor->id = strtol(ptr[1], &endptr, 10); + if (*endptr != 0) + goto out_err_syntax; + vendor->name = strdup(ptr[0]); + if (!vendor->name) { + log_emerg("radius: out of memory\n"); + goto out_err; + } + INIT_LIST_HEAD(&vendor->items); + list_add_tail(&vendor->entry, &dict->vendors); + } else + goto out_err_syntax; + } else if (r == 3) { + if (!strcmp(buf, "ATTRIBUTE")) { + attr = malloc(sizeof(*attr)); + if (!attr) { + log_emerg("radius: out of memory\n"); + goto out_err; + } + memset(attr, 0, sizeof(*attr)); + INIT_LIST_HEAD(&attr->values); + list_add_tail(&attr->entry, items); + attr->name = strdup(ptr[0]); + attr->id = strtol(ptr[1], &endptr, 10); + if (*endptr != 0) + goto out_err_syntax; + if (!strcmp(ptr[2], "integer")) + attr->type = ATTR_TYPE_INTEGER; + else if (!strcmp(ptr[2], "string")) + attr->type = ATTR_TYPE_STRING; + else if (!strcmp(ptr[2], "date")) + attr->type = ATTR_TYPE_DATE; + else if (!strcmp(ptr[2], "ipaddr")) + attr->type = ATTR_TYPE_IPADDR; + else if (!strcmp(ptr[2], "octets")) + attr->type = ATTR_TYPE_OCTETS; + else { + log_emerg("radius:%s:%i: unknown attribute type\n", fname, n); + goto out_err; + } + } else if (!strcmp(buf, "VALUE")) { + attr = find_attr(items, ptr[0]); + if (!attr) { + log_emerg("radius:%s:%i: unknown attribute\n", fname, n); + goto out_err; + } + val = malloc(sizeof(*val)); + if (!val) { + log_emerg("radius: out of memory\n"); + goto out_err; + } + memset(val, 0, sizeof(*val)); + list_add_tail(&val->entry, &attr->values); + val->name = strdup(ptr[1]); + switch (attr->type) { + case ATTR_TYPE_INTEGER: + val->val.integer = strtol(ptr[2], &endptr, 10); + if (*endptr != 0) + goto out_err_syntax; + break; + case ATTR_TYPE_STRING: + val->val.string = strdup(ptr[2]); + break; + case ATTR_TYPE_DATE: + log_warn("radius:%s:%i: VALUE of type 'date' is not implemented yet\n", fname, n); + break; + case ATTR_TYPE_IPADDR: + log_warn("radius:%s:%i: VALUE of type 'ipaddr' is not implemented yet\n", fname, n); + break; + } + } else + goto out_err_syntax; + } else + goto out_err_syntax; + } + + fclose(f); + + return 0; + +out_err_syntax: + log_emerg("radius:%s:%i: syntaxis error\n", fname, n); +out_err: + fclose(f); + return -1; +} + +int rad_dict_load(const char *fname) +{ + int r = -1; + + dict = malloc(sizeof(*dict)); + if (!dict) { + log_emerg("radius: out of memory\n"); + return -1; + } + INIT_LIST_HEAD(&dict->items); + INIT_LIST_HEAD(&dict->vendors); + + path = _malloc(PATH_MAX); + if (!path) { + log_emerg("radius: out of memory\n"); + goto out_free_dict; + } + + fname1 = _malloc(PATH_MAX); + if (!fname1) { + log_emerg("radius: out of memory\n"); + goto out_free_path; + } + + buf = _malloc(BUF_SIZE); + if (!buf) { + log_emerg("radius: out of memory\n"); + goto out_free_fname1; + } + + strcpy(path, fname); + + r = dict_load(fname); + +out_free_fname1: + _free(fname1); +out_free_path: + _free(path); +out_free_dict: + if (r) + rad_dict_free(dict); + return r; +} + +void rad_dict_free(struct rad_dict_t *dict) +{ + struct rad_dict_attr_t *attr; + struct rad_dict_value_t *val; + + while (!list_empty(&dict->items)) { + attr = list_entry(dict->items.next, typeof(*attr), entry); + while (!list_empty(&attr->values)) { + val = list_entry(attr->values.next, typeof(*val), entry); + list_del(&val->entry); + _free((char*)val->name); + if (attr->type == ATTR_TYPE_STRING) + _free((char*)val->val.string); + _free(val); + } + list_del(&attr->entry); + _free((char*)attr->name); + _free(attr); + } + free(dict); +} + +static struct rad_dict_attr_t *dict_find_attr(struct list_head *items, const char *name) +{ + struct rad_dict_attr_t *attr; + + list_for_each_entry(attr, items, entry) + if (!strcmp(attr->name, name)) + return attr; + + return NULL; +} + +__export struct rad_dict_attr_t *rad_dict_find_attr(const char *name) +{ + return dict_find_attr(&dict->items, name); +} + +__export struct rad_dict_attr_t *rad_dict_find_attr_id(struct rad_dict_vendor_t *vendor, int id) +{ + struct rad_dict_attr_t *attr; + struct list_head *items = vendor ? &vendor->items : &dict->items; + + list_for_each_entry(attr, items, entry) + if (attr->id == id) + return attr; + + return NULL; +} + +__export struct rad_dict_value_t *rad_dict_find_val_name(struct rad_dict_attr_t *attr, const char *name) +{ + struct rad_dict_value_t *val; + + list_for_each_entry(val, &attr->values, entry) + if (!strcmp(val->name, name)) + return val; + + return NULL; +} + +__export struct rad_dict_value_t *rad_dict_find_val(struct rad_dict_attr_t *attr, rad_value_t v) +{ + struct rad_dict_value_t *val; + + if (attr->type != ATTR_TYPE_INTEGER) + return NULL; + + list_for_each_entry(val, &attr->values, entry) + if (val->val.integer == v.integer) + return val; + + return NULL; +} + +__export struct rad_dict_vendor_t *rad_dict_find_vendor_name(const char *name) +{ + struct rad_dict_vendor_t *vendor; + + list_for_each_entry(vendor, &dict->vendors, entry) { + if (!strcmp(vendor->name, name)) + return vendor; + } + + return NULL; +} + +__export struct rad_dict_vendor_t *rad_dict_find_vendor_id(int id) +{ + struct rad_dict_vendor_t *vendor; + + list_for_each_entry(vendor, &dict->vendors, entry) { + if (vendor->id == id) + return vendor; + } + + return NULL; +} + +__export struct rad_dict_attr_t *rad_dict_find_vendor_attr(struct rad_dict_vendor_t *vendor, const char *name) +{ + return dict_find_attr(&vendor->items, name); +} + diff --git a/accel-pppd/radius/dict/dictionary b/accel-pppd/radius/dict/dictionary new file mode 100644 index 00000000..27973105 --- /dev/null +++ b/accel-pppd/radius/dict/dictionary @@ -0,0 +1,79 @@ +# -*- text -*- +# +# Version $Id: dictionary,v 1.155 2008/04/20 14:47:55 aland Exp $ +# +# DO NOT EDIT THE FILES IN THIS DIRECTORY +# +# The files in this directory are maintained and updated by +# the FreeRADIUS project. Newer releases of software may update +# or change these files. +# +# Use the main dictionary file (usually /etc/raddb/dictionary) +# for local system attributes and $INCLUDEs. +# +# +# +# This file contains dictionary translations for parsing +# requests and generating responses. All transactions are +# composed of Attribute/Value Pairs. The value of each attribute +# is specified as one of 4 data types. Valid data types are: +# +# text - printable, generally UTF-8 encoded (subset of 'string') +# string - 0-253 octets +# ipaddr - 4 octets in network byte order +# integer - 32 bit value in big endian order (high byte first) +# date - 32 bit value in big endian order - seconds since +# 00:00:00 GMT, Jan. 1, 1970 +# ifid - 8 octets in network byte order +# ipv6addr - 16 octets in network byte order +# ipv6prefix - 18 octets in network byte order +# ether - 6 octets of hh:hh:hh:hh:hh:hh +# where 'h' is hex digits, upper or lowercase. +# +# FreeRADIUS includes extended data types which are not defined +# in the RFC's. These data types are: +# +# abinary - Ascend's binary filter format. +# octets - raw octets, printed and input as hex strings. +# e.g.: 0x123456789abcdef +# +# +# Enumerated values are stored in the user file with dictionary +# VALUE translations for easy administration. +# +# Example: +# +# ATTRIBUTE VALUE +# --------------- ----- +# Framed-Protocol = PPP +# 7 = 1 (integer encoding) +# + +# +# Include compatibility dictionary for older users file. Move +# this directive to the end of this file if you want to see the +# old names in the logfiles, INSTEAD OF the new names. +# +# +# Include the RFC dictionaries next. +# +# For a complete list of the standard attributes and values, +# see: +# http://www.iana.org/assignments/radius-types +# +$INCLUDE dictionary.rfc2865 +$INCLUDE dictionary.rfc2866 +$INCLUDE dictionary.rfc2867 +$INCLUDE dictionary.rfc2868 +$INCLUDE dictionary.rfc2869 +$INCLUDE dictionary.rfc3576 +$INCLUDE dictionary.rfc3580 +$INCLUDE dictionary.rfc4072 +$INCLUDE dictionary.rfc4372 +$INCLUDE dictionary.rfc4679 +$INCLUDE dictionary.rfc5176 + +$INCLUDE dictionary.microsoft +$INCLUDE dictionary.cisco + +ATTRIBUTE Traffic-Shape-in 231 integer diff --git a/accel-pppd/radius/dict/dictionary.cisco b/accel-pppd/radius/dict/dictionary.cisco new file mode 100644 index 00000000..6d1efbe8 --- /dev/null +++ b/accel-pppd/radius/dict/dictionary.cisco @@ -0,0 +1,156 @@ +# -*- text -*- +# +# dictionary.cisco +# +# Accounting VSAs originally by +# "Marcelo M. Sosa Lugones" <marcelo@sosa.com.ar> +# +# Version: $Id: dictionary.cisco,v 1.16 2006/06/05 16:55:21 pnixon Exp $ +# +# For documentation on Cisco RADIUS attributes, see: +# +# http://www.cisco.com/univercd/cc/td/doc/product/access/acs_serv/vapp_dev/vsaig3.htm +# +# For general documentation on Cisco RADIUS configuration, see: +# +# http://www.cisco.com/en/US/partner/tech/tk583/tk547/tsd_technology_support_sub-protocol_home.html +# + +VENDOR Cisco 9 + +# +# Standard attribute +# +BEGIN-VENDOR Cisco + +ATTRIBUTE Cisco-AVPair 1 string +ATTRIBUTE Cisco-NAS-Port 2 string + +# +# T.37 Store-and-Forward attributes. +# +ATTRIBUTE Cisco-Fax-Account-Id-Origin 3 string +ATTRIBUTE Cisco-Fax-Msg-Id 4 string +ATTRIBUTE Cisco-Fax-Pages 5 string +ATTRIBUTE Cisco-Fax-Coverpage-Flag 6 string +ATTRIBUTE Cisco-Fax-Modem-Time 7 string +ATTRIBUTE Cisco-Fax-Connect-Speed 8 string +ATTRIBUTE Cisco-Fax-Recipient-Count 9 string +ATTRIBUTE Cisco-Fax-Process-Abort-Flag 10 string +ATTRIBUTE Cisco-Fax-Dsn-Address 11 string +ATTRIBUTE Cisco-Fax-Dsn-Flag 12 string +ATTRIBUTE Cisco-Fax-Mdn-Address 13 string +ATTRIBUTE Cisco-Fax-Mdn-Flag 14 string +ATTRIBUTE Cisco-Fax-Auth-Status 15 string +ATTRIBUTE Cisco-Email-Server-Address 16 string +ATTRIBUTE Cisco-Email-Server-Ack-Flag 17 string +ATTRIBUTE Cisco-Gateway-Id 18 string +ATTRIBUTE Cisco-Call-Type 19 string +ATTRIBUTE Cisco-Port-Used 20 string +ATTRIBUTE Cisco-Abort-Cause 21 string + +# +# Voice over IP attributes. +# +ATTRIBUTE h323-remote-address 23 string +ATTRIBUTE h323-conf-id 24 string +ATTRIBUTE h323-setup-time 25 string +ATTRIBUTE h323-call-origin 26 string +ATTRIBUTE h323-call-type 27 string +ATTRIBUTE h323-connect-time 28 string +ATTRIBUTE h323-disconnect-time 29 string +ATTRIBUTE h323-disconnect-cause 30 string +ATTRIBUTE h323-voice-quality 31 string +ATTRIBUTE h323-gw-id 33 string +ATTRIBUTE h323-incoming-conf-id 35 string + +ATTRIBUTE h323-credit-amount 101 string +ATTRIBUTE h323-credit-time 102 string +ATTRIBUTE h323-return-code 103 string +ATTRIBUTE h323-prompt-id 104 string +ATTRIBUTE h323-time-and-day 105 string +ATTRIBUTE h323-redirect-number 106 string +ATTRIBUTE h323-preferred-lang 107 string +ATTRIBUTE h323-redirect-ip-address 108 string +ATTRIBUTE h323-billing-model 109 string +ATTRIBUTE h323-currency 110 string +ATTRIBUTE subscriber 111 string +ATTRIBUTE gw-rxd-cdn 112 string +ATTRIBUTE gw-final-xlated-cdn 113 string +ATTRIBUTE remote-media-address 114 string +ATTRIBUTE release-source 115 string +ATTRIBUTE gw-rxd-cgn 116 string +ATTRIBUTE gw-final-xlated-cgn 117 string + +# SIP Attributes +ATTRIBUTE call-id 141 string +ATTRIBUTE session-protocol 142 string +ATTRIBUTE method 143 string +ATTRIBUTE prev-hop-via 144 string +ATTRIBUTE prev-hop-ip 145 string +ATTRIBUTE incoming-req-uri 146 string +ATTRIBUTE outgoing-req-uri 147 string +ATTRIBUTE next-hop-ip 148 string +ATTRIBUTE next-hop-dn 149 string +ATTRIBUTE sip-hdr 150 string + +# +# Extra attributes sent by the Cisco, if you configure +# "radius-server vsa accounting" (requires IOS11.2+). +# +ATTRIBUTE Cisco-Multilink-ID 187 integer +ATTRIBUTE Cisco-Num-In-Multilink 188 integer +ATTRIBUTE Cisco-Pre-Input-Octets 190 integer +ATTRIBUTE Cisco-Pre-Output-Octets 191 integer +ATTRIBUTE Cisco-Pre-Input-Packets 192 integer +ATTRIBUTE Cisco-Pre-Output-Packets 193 integer +ATTRIBUTE Cisco-Maximum-Time 194 integer +ATTRIBUTE Cisco-Disconnect-Cause 195 integer +ATTRIBUTE Cisco-Data-Rate 197 integer +ATTRIBUTE Cisco-PreSession-Time 198 integer +ATTRIBUTE Cisco-PW-Lifetime 208 integer +ATTRIBUTE Cisco-IP-Direct 209 integer +ATTRIBUTE Cisco-PPP-VJ-Slot-Comp 210 integer +ATTRIBUTE Cisco-PPP-Async-Map 212 integer +ATTRIBUTE Cisco-IP-Pool-Definition 217 string +ATTRIBUTE Cisco-Assign-IP-Pool 218 integer +ATTRIBUTE Cisco-Route-IP 228 integer +ATTRIBUTE Cisco-Link-Compression 233 integer +ATTRIBUTE Cisco-Target-Util 234 integer +ATTRIBUTE Cisco-Maximum-Channels 235 integer +ATTRIBUTE Cisco-Data-Filter 242 integer +ATTRIBUTE Cisco-Call-Filter 243 integer +ATTRIBUTE Cisco-Idle-Limit 244 integer +ATTRIBUTE Cisco-Account-Info 250 string +ATTRIBUTE Cisco-Service-Info 251 string +ATTRIBUTE Cisco-Command-Code 252 string +ATTRIBUTE Cisco-Control-Info 253 string +ATTRIBUTE Cisco-Xmit-Rate 255 integer + +VALUE Cisco-Disconnect-Cause Unknown 2 +VALUE Cisco-Disconnect-Cause CLID-Authentication-Failure 4 +VALUE Cisco-Disconnect-Cause No-Carrier 10 +VALUE Cisco-Disconnect-Cause Lost-Carrier 11 +VALUE Cisco-Disconnect-Cause No-Detected-Result-Codes 12 +VALUE Cisco-Disconnect-Cause User-Ends-Session 20 +VALUE Cisco-Disconnect-Cause Idle-Timeout 21 +VALUE Cisco-Disconnect-Cause Exit-Telnet-Session 22 +VALUE Cisco-Disconnect-Cause No-Remote-IP-Addr 23 +VALUE Cisco-Disconnect-Cause Exit-Raw-TCP 24 +VALUE Cisco-Disconnect-Cause Password-Fail 25 +VALUE Cisco-Disconnect-Cause Raw-TCP-Disabled 26 +VALUE Cisco-Disconnect-Cause Control-C-Detected 27 +VALUE Cisco-Disconnect-Cause EXEC-Program-Destroyed 28 +VALUE Cisco-Disconnect-Cause Timeout-PPP-LCP 40 +VALUE Cisco-Disconnect-Cause Failed-PPP-LCP-Negotiation 41 +VALUE Cisco-Disconnect-Cause Failed-PPP-PAP-Auth-Fail 42 +VALUE Cisco-Disconnect-Cause Failed-PPP-CHAP-Auth 43 +VALUE Cisco-Disconnect-Cause Failed-PPP-Remote-Auth 44 +VALUE Cisco-Disconnect-Cause PPP-Remote-Terminate 45 +VALUE Cisco-Disconnect-Cause PPP-Closed-Event 46 +VALUE Cisco-Disconnect-Cause Session-Timeout 100 +VALUE Cisco-Disconnect-Cause Session-Failed-Security 101 +VALUE Cisco-Disconnect-Cause Session-End-Callback 102 +VALUE Cisco-Disconnect-Cause Invalid-Protocol 120 + +END-VENDOR Cisco diff --git a/accel-pppd/radius/dict/dictionary.microsoft b/accel-pppd/radius/dict/dictionary.microsoft new file mode 100644 index 00000000..9ca6b8e9 --- /dev/null +++ b/accel-pppd/radius/dict/dictionary.microsoft @@ -0,0 +1,83 @@ +# -*- text -*- +# +# Microsoft's VSA's, from RFC 2548 +# +# $Id: dictionary.microsoft,v 1.8 2005/08/08 22:23:37 aland Exp $ +# + +VENDOR Microsoft 311 + +BEGIN-VENDOR Microsoft +ATTRIBUTE MS-CHAP-Response 1 octets +ATTRIBUTE MS-CHAP-Error 2 string +ATTRIBUTE MS-CHAP-CPW-1 3 octets +ATTRIBUTE MS-CHAP-CPW-2 4 octets +ATTRIBUTE MS-CHAP-LM-Enc-PW 5 octets +ATTRIBUTE MS-CHAP-NT-Enc-PW 6 octets +ATTRIBUTE MS-MPPE-Encryption-Policy 7 integer +# This is referred to as both singular and plural in the RFC. +# Plural seems to make more sense. +ATTRIBUTE MS-MPPE-Encryption-Type 8 integer +ATTRIBUTE MS-MPPE-Encryption-Types 8 integer +ATTRIBUTE MS-RAS-Vendor 9 integer # content is Vendor-ID +ATTRIBUTE MS-CHAP-Domain 10 string +ATTRIBUTE MS-CHAP-Challenge 11 octets +ATTRIBUTE MS-CHAP-MPPE-Keys 12 octets encrypt=1 +ATTRIBUTE MS-BAP-Usage 13 integer +ATTRIBUTE MS-Link-Utilization-Threshold 14 integer # values are 1-100 +ATTRIBUTE MS-Link-Drop-Time-Limit 15 integer +ATTRIBUTE MS-MPPE-Send-Key 16 octets encrypt=2 +ATTRIBUTE MS-MPPE-Recv-Key 17 octets encrypt=2 +ATTRIBUTE MS-RAS-Version 18 string +ATTRIBUTE MS-Old-ARAP-Password 19 octets +ATTRIBUTE MS-New-ARAP-Password 20 octets +ATTRIBUTE MS-ARAP-PW-Change-Reason 21 integer + +ATTRIBUTE MS-Filter 22 octets +ATTRIBUTE MS-Acct-Auth-Type 23 integer +ATTRIBUTE MS-Acct-EAP-Type 24 integer + +ATTRIBUTE MS-CHAP2-Response 25 octets +ATTRIBUTE MS-CHAP2-Success 26 octets +ATTRIBUTE MS-CHAP2-CPW 27 octets + +ATTRIBUTE MS-Primary-DNS-Server 28 ipaddr +ATTRIBUTE MS-Secondary-DNS-Server 29 ipaddr +ATTRIBUTE MS-Primary-NBNS-Server 30 ipaddr +ATTRIBUTE MS-Secondary-NBNS-Server 31 ipaddr + +#ATTRIBUTE MS-ARAP-Challenge 33 octets + +# +# Integer Translations +# + +# MS-BAP-Usage Values + +VALUE MS-BAP-Usage Not-Allowed 0 +VALUE MS-BAP-Usage Allowed 1 +VALUE MS-BAP-Usage Required 2 + +# MS-ARAP-Password-Change-Reason Values + +VALUE MS-ARAP-PW-Change-Reason Just-Change-Password 1 +VALUE MS-ARAP-PW-Change-Reason Expired-Password 2 +VALUE MS-ARAP-PW-Change-Reason Admin-Requires-Password-Change 3 +VALUE MS-ARAP-PW-Change-Reason Password-Too-Short 4 + +# MS-Acct-Auth-Type Values + +VALUE MS-Acct-Auth-Type PAP 1 +VALUE MS-Acct-Auth-Type CHAP 2 +VALUE MS-Acct-Auth-Type MS-CHAP-1 3 +VALUE MS-Acct-Auth-Type MS-CHAP-2 4 +VALUE MS-Acct-Auth-Type EAP 5 + +# MS-Acct-EAP-Type Values + +VALUE MS-Acct-EAP-Type MD5 4 +VALUE MS-Acct-EAP-Type OTP 5 +VALUE MS-Acct-EAP-Type Generic-Token-Card 6 +VALUE MS-Acct-EAP-Type TLS 13 + +END-VENDOR Microsoft diff --git a/accel-pppd/radius/dict/dictionary.rfc2865 b/accel-pppd/radius/dict/dictionary.rfc2865 new file mode 100644 index 00000000..7e5bf583 --- /dev/null +++ b/accel-pppd/radius/dict/dictionary.rfc2865 @@ -0,0 +1,137 @@ +# -*- text -*- +# +# Attributes and values defined in RFC 2865. +# http://www.ietf.org/rfc/rfc2865.txt +# +# $Id: dictionary.rfc2865,v 1.3 2005/08/10 20:59:40 aland Exp $ +# +ATTRIBUTE User-Name 1 string +ATTRIBUTE User-Password 2 octets +ATTRIBUTE CHAP-Password 3 octets +ATTRIBUTE NAS-IP-Address 4 ipaddr +ATTRIBUTE NAS-Port 5 integer +ATTRIBUTE Service-Type 6 integer +ATTRIBUTE Framed-Protocol 7 integer +ATTRIBUTE Framed-IP-Address 8 ipaddr +ATTRIBUTE Framed-IP-Netmask 9 ipaddr +ATTRIBUTE Framed-Routing 10 integer +ATTRIBUTE Filter-Id 11 string +ATTRIBUTE Framed-MTU 12 integer +ATTRIBUTE Framed-Compression 13 integer +ATTRIBUTE Login-IP-Host 14 ipaddr +ATTRIBUTE Login-Service 15 integer +ATTRIBUTE Login-TCP-Port 16 integer +# Attribute 17 is undefined +ATTRIBUTE Reply-Message 18 string +ATTRIBUTE Callback-Number 19 string +ATTRIBUTE Callback-Id 20 string +# Attribute 21 is undefined +ATTRIBUTE Framed-Route 22 string +ATTRIBUTE Framed-IPX-Network 23 ipaddr +ATTRIBUTE State 24 octets +ATTRIBUTE Class 25 octets +ATTRIBUTE Vendor-Specific 26 octets +ATTRIBUTE Session-Timeout 27 integer +ATTRIBUTE Idle-Timeout 28 integer +ATTRIBUTE Termination-Action 29 integer +ATTRIBUTE Called-Station-Id 30 string +ATTRIBUTE Calling-Station-Id 31 string +ATTRIBUTE NAS-Identifier 32 string +ATTRIBUTE Proxy-State 33 octets +ATTRIBUTE Login-LAT-Service 34 string +ATTRIBUTE Login-LAT-Node 35 string +ATTRIBUTE Login-LAT-Group 36 octets +ATTRIBUTE Framed-AppleTalk-Link 37 integer +ATTRIBUTE Framed-AppleTalk-Network 38 integer +ATTRIBUTE Framed-AppleTalk-Zone 39 string + +ATTRIBUTE CHAP-Challenge 60 octets +ATTRIBUTE NAS-Port-Type 61 integer +ATTRIBUTE Port-Limit 62 integer +ATTRIBUTE Login-LAT-Port 63 integer + +# +# Integer Translations +# + +# Service types + +VALUE Service-Type Login-User 1 +VALUE Service-Type Framed-User 2 +VALUE Service-Type Callback-Login-User 3 +VALUE Service-Type Callback-Framed-User 4 +VALUE Service-Type Outbound-User 5 +VALUE Service-Type Administrative-User 6 +VALUE Service-Type NAS-Prompt-User 7 +VALUE Service-Type Authenticate-Only 8 +VALUE Service-Type Callback-NAS-Prompt 9 +VALUE Service-Type Call-Check 10 +VALUE Service-Type Callback-Administrative 11 + +# Framed Protocols + +VALUE Framed-Protocol PPP 1 +VALUE Framed-Protocol SLIP 2 +VALUE Framed-Protocol ARAP 3 +VALUE Framed-Protocol Gandalf-SLML 4 +VALUE Framed-Protocol Xylogics-IPX-SLIP 5 +VALUE Framed-Protocol X.75-Synchronous 6 + +# Framed Routing Values + +VALUE Framed-Routing None 0 +VALUE Framed-Routing Broadcast 1 +VALUE Framed-Routing Listen 2 +VALUE Framed-Routing Broadcast-Listen 3 + +# Framed Compression Types + +VALUE Framed-Compression None 0 +VALUE Framed-Compression Van-Jacobson-TCP-IP 1 +VALUE Framed-Compression IPX-Header-Compression 2 +VALUE Framed-Compression Stac-LZS 3 + +# Login Services + +VALUE Login-Service Telnet 0 +VALUE Login-Service Rlogin 1 +VALUE Login-Service TCP-Clear 2 +VALUE Login-Service PortMaster 3 +VALUE Login-Service LAT 4 +VALUE Login-Service X25-PAD 5 +VALUE Login-Service X25-T3POS 6 +VALUE Login-Service TCP-Clear-Quiet 8 + +# Login-TCP-Port (see /etc/services for more examples) + +VALUE Login-TCP-Port Telnet 23 +VALUE Login-TCP-Port Rlogin 513 +VALUE Login-TCP-Port Rsh 514 + +# Termination Options + +VALUE Termination-Action Default 0 +VALUE Termination-Action RADIUS-Request 1 + +# NAS Port Types + +VALUE NAS-Port-Type Async 0 +VALUE NAS-Port-Type Sync 1 +VALUE NAS-Port-Type ISDN 2 +VALUE NAS-Port-Type ISDN-V120 3 +VALUE NAS-Port-Type ISDN-V110 4 +VALUE NAS-Port-Type Virtual 5 +VALUE NAS-Port-Type PIAFS 6 +VALUE NAS-Port-Type HDLC-Clear-Channel 7 +VALUE NAS-Port-Type X.25 8 +VALUE NAS-Port-Type X.75 9 +VALUE NAS-Port-Type G.3-Fax 10 +VALUE NAS-Port-Type SDSL 11 +VALUE NAS-Port-Type ADSL-CAP 12 +VALUE NAS-Port-Type ADSL-DMT 13 +VALUE NAS-Port-Type IDSL 14 +VALUE NAS-Port-Type Ethernet 15 +VALUE NAS-Port-Type xDSL 16 +VALUE NAS-Port-Type Cable 17 +VALUE NAS-Port-Type Wireless-Other 18 +VALUE NAS-Port-Type Wireless-802.11 19 diff --git a/accel-pppd/radius/dict/dictionary.rfc2866 b/accel-pppd/radius/dict/dictionary.rfc2866 new file mode 100644 index 00000000..15472bd5 --- /dev/null +++ b/accel-pppd/radius/dict/dictionary.rfc2866 @@ -0,0 +1,57 @@ +# -*- text -*- +# +# Attributes and values defined in RFC 2866. +# http://www.ietf.org/rfc/rfc2866.txt +# +# $Id: dictionary.rfc2866,v 1.2 2005/08/08 22:23:38 aland Exp $ +# +ATTRIBUTE Acct-Status-Type 40 integer +ATTRIBUTE Acct-Delay-Time 41 integer +ATTRIBUTE Acct-Input-Octets 42 integer +ATTRIBUTE Acct-Output-Octets 43 integer +ATTRIBUTE Acct-Session-Id 44 string +ATTRIBUTE Acct-Authentic 45 integer +ATTRIBUTE Acct-Session-Time 46 integer +ATTRIBUTE Acct-Input-Packets 47 integer +ATTRIBUTE Acct-Output-Packets 48 integer +ATTRIBUTE Acct-Terminate-Cause 49 integer +ATTRIBUTE Acct-Multi-Session-Id 50 string +ATTRIBUTE Acct-Link-Count 51 integer + +# Accounting Status Types + +VALUE Acct-Status-Type Start 1 +VALUE Acct-Status-Type Stop 2 +VALUE Acct-Status-Type Alive 3 # dup +VALUE Acct-Status-Type Interim-Update 3 +VALUE Acct-Status-Type Accounting-On 7 +VALUE Acct-Status-Type Accounting-Off 8 +VALUE Acct-Status-Type Failed 15 + +# Authentication Types + +VALUE Acct-Authentic RADIUS 1 +VALUE Acct-Authentic Local 2 +VALUE Acct-Authentic Remote 3 +VALUE Acct-Authentic Diameter 4 + +# Acct Terminate Causes + +VALUE Acct-Terminate-Cause User-Request 1 +VALUE Acct-Terminate-Cause Lost-Carrier 2 +VALUE Acct-Terminate-Cause Lost-Service 3 +VALUE Acct-Terminate-Cause Idle-Timeout 4 +VALUE Acct-Terminate-Cause Session-Timeout 5 +VALUE Acct-Terminate-Cause Admin-Reset 6 +VALUE Acct-Terminate-Cause Admin-Reboot 7 +VALUE Acct-Terminate-Cause Port-Error 8 +VALUE Acct-Terminate-Cause NAS-Error 9 +VALUE Acct-Terminate-Cause NAS-Request 10 +VALUE Acct-Terminate-Cause NAS-Reboot 11 +VALUE Acct-Terminate-Cause Port-Unneeded 12 +VALUE Acct-Terminate-Cause Port-Preempted 13 +VALUE Acct-Terminate-Cause Port-Suspended 14 +VALUE Acct-Terminate-Cause Service-Unavailable 15 +VALUE Acct-Terminate-Cause Callback 16 +VALUE Acct-Terminate-Cause User-Error 17 +VALUE Acct-Terminate-Cause Host-Request 18 diff --git a/accel-pppd/radius/dict/dictionary.rfc2867 b/accel-pppd/radius/dict/dictionary.rfc2867 new file mode 100644 index 00000000..b018aba4 --- /dev/null +++ b/accel-pppd/radius/dict/dictionary.rfc2867 @@ -0,0 +1,16 @@ +# -*- text -*- +# +# Attributes and values defined in RFC 2867. +# http://www.ietf.org/rfc/rfc2867.txt +# +# $Id: dictionary.rfc2867,v 1.2 2005/08/08 22:23:38 aland Exp $ +# +ATTRIBUTE Acct-Tunnel-Connection 68 string +ATTRIBUTE Acct-Tunnel-Packets-Lost 86 integer + +VALUE Acct-Status-Type Tunnel-Start 9 +VALUE Acct-Status-Type Tunnel-Stop 10 +VALUE Acct-Status-Type Tunnel-Reject 11 +VALUE Acct-Status-Type Tunnel-Link-Start 12 +VALUE Acct-Status-Type Tunnel-Link-Stop 13 +VALUE Acct-Status-Type Tunnel-Link-Reject 14 diff --git a/accel-pppd/radius/dict/dictionary.rfc2868 b/accel-pppd/radius/dict/dictionary.rfc2868 new file mode 100644 index 00000000..f6a4047a --- /dev/null +++ b/accel-pppd/radius/dict/dictionary.rfc2868 @@ -0,0 +1,54 @@ +# -*- text -*- +# +# Attributes and values defined in RFC 2868. +# http://www.ietf.org/rfc/rfc2868.txt +# +# $Id: dictionary.rfc2868,v 1.4 2007/02/13 13:28:17 aland Exp $ +# +ATTRIBUTE Tunnel-Type 64 integer has_tag +ATTRIBUTE Tunnel-Medium-Type 65 integer has_tag +ATTRIBUTE Tunnel-Client-Endpoint 66 string has_tag +ATTRIBUTE Tunnel-Server-Endpoint 67 string has_tag + +ATTRIBUTE Tunnel-Password 69 string has_tag,encrypt=2 + +ATTRIBUTE Tunnel-Private-Group-Id 81 string has_tag +ATTRIBUTE Tunnel-Assignment-Id 82 string has_tag +ATTRIBUTE Tunnel-Preference 83 integer has_tag + +ATTRIBUTE Tunnel-Client-Auth-Id 90 string has_tag +ATTRIBUTE Tunnel-Server-Auth-Id 91 string has_tag + +# Tunnel Type + +VALUE Tunnel-Type PPTP 1 +VALUE Tunnel-Type L2F 2 +VALUE Tunnel-Type L2TP 3 +VALUE Tunnel-Type ATMP 4 +VALUE Tunnel-Type VTP 5 +VALUE Tunnel-Type AH 6 +VALUE Tunnel-Type IP 7 +VALUE Tunnel-Type MIN-IP 8 +VALUE Tunnel-Type ESP 9 +VALUE Tunnel-Type GRE 10 +VALUE Tunnel-Type DVS 11 +VALUE Tunnel-Type IP-in-IP 12 + +# Tunnel Medium Type + +VALUE Tunnel-Medium-Type IP 1 +VALUE Tunnel-Medium-Type IPv4 1 +VALUE Tunnel-Medium-Type IPv6 2 +VALUE Tunnel-Medium-Type NSAP 3 +VALUE Tunnel-Medium-Type HDLC 4 +VALUE Tunnel-Medium-Type BBN-1822 5 +VALUE Tunnel-Medium-Type IEEE-802 6 +VALUE Tunnel-Medium-Type E.163 7 +VALUE Tunnel-Medium-Type E.164 8 +VALUE Tunnel-Medium-Type F.69 9 +VALUE Tunnel-Medium-Type X.121 10 +VALUE Tunnel-Medium-Type IPX 11 +VALUE Tunnel-Medium-Type Appletalk 12 +VALUE Tunnel-Medium-Type DecNet-IV 13 +VALUE Tunnel-Medium-Type Banyan-Vines 14 +VALUE Tunnel-Medium-Type E.164-NSAP 15 diff --git a/accel-pppd/radius/dict/dictionary.rfc2869 b/accel-pppd/radius/dict/dictionary.rfc2869 new file mode 100644 index 00000000..1a2631ec --- /dev/null +++ b/accel-pppd/radius/dict/dictionary.rfc2869 @@ -0,0 +1,39 @@ +# -*- text -*- +# +# Attributes and values defined in RFC 2869. +# http://www.ietf.org/rfc/rfc2869.txt +# +# $Id: dictionary.rfc2869,v 1.2 2005/08/08 22:23:39 aland Exp $ +# +ATTRIBUTE Acct-Input-Gigawords 52 integer +ATTRIBUTE Acct-Output-Gigawords 53 integer + +ATTRIBUTE Event-Timestamp 55 date + +ATTRIBUTE ARAP-Password 70 octets # 16 octets of data +ATTRIBUTE ARAP-Features 71 octets # 14 octets of data +ATTRIBUTE ARAP-Zone-Access 72 integer +ATTRIBUTE ARAP-Security 73 integer +ATTRIBUTE ARAP-Security-Data 74 string +ATTRIBUTE Password-Retry 75 integer +ATTRIBUTE Prompt 76 integer +ATTRIBUTE Connect-Info 77 string +ATTRIBUTE Configuration-Token 78 string +ATTRIBUTE EAP-Message 79 octets +ATTRIBUTE Message-Authenticator 80 octets + +ATTRIBUTE ARAP-Challenge-Response 84 octets # 8 octets of data +ATTRIBUTE Acct-Interim-Interval 85 integer +# 86: RFC 2867 +ATTRIBUTE NAS-Port-Id 87 string +ATTRIBUTE Framed-Pool 88 string + +# ARAP Zone Access + +VALUE ARAP-Zone-Access Default-Zone 1 +VALUE ARAP-Zone-Access Zone-Filter-Inclusive 2 +VALUE ARAP-Zone-Access Zone-Filter-Exclusive 4 + +# Prompt +VALUE Prompt No-Echo 0 +VALUE Prompt Echo 1 diff --git a/accel-pppd/radius/dict/dictionary.rfc3576 b/accel-pppd/radius/dict/dictionary.rfc3576 new file mode 100644 index 00000000..35aeb326 --- /dev/null +++ b/accel-pppd/radius/dict/dictionary.rfc3576 @@ -0,0 +1,30 @@ +# -*- text -*- +# +# Attributes and values defined in RFC 3576. +# http://www.ietf.org/rfc/rfc3576.txt +# +# $Id: dictionary.rfc3576,v 1.2 2005/08/08 22:23:39 aland Exp $ +# +ATTRIBUTE Error-Cause 101 integer + +# Service Types + +VALUE Service-Type Authorize-Only 17 + +# Error causes + +VALUE Error-Cause Residual-Context-Removed 201 +VALUE Error-Cause Invalid-EAP-Packet 202 +VALUE Error-Cause Unsupported-Attribute 401 +VALUE Error-Cause Missing-Attribute 402 +VALUE Error-Cause NAS-Identification-Mismatch 403 +VALUE Error-Cause Invalid-Request 404 +VALUE Error-Cause Unsupported-Service 405 +VALUE Error-Cause Unsupported-Extension 406 +VALUE Error-Cause Administratively-Prohibited 501 +VALUE Error-Cause Proxy-Request-Not-Routable 502 +VALUE Error-Cause Session-Context-Not-Found 503 +VALUE Error-Cause Session-Context-Not-Removable 504 +VALUE Error-Cause Proxy-Processing-Error 505 +VALUE Error-Cause Resources-Unavailable 506 +VALUE Error-Cause Request-Initiated 507 diff --git a/accel-pppd/radius/dict/dictionary.rfc3580 b/accel-pppd/radius/dict/dictionary.rfc3580 new file mode 100644 index 00000000..1bd4ca3e --- /dev/null +++ b/accel-pppd/radius/dict/dictionary.rfc3580 @@ -0,0 +1,16 @@ +# -*- text -*- +# +# Attributes and values defined in RFC 3580. +# http://www.ietf.org/rfc/rfc3580.txt +# +# $Id: dictionary.rfc3580,v 1.2 2005/08/08 22:23:39 aland Exp $ +# +VALUE Acct-Terminate-Cause Supplicant-Restart 19 +VALUE Acct-Terminate-Cause Reauthentication-Failure 20 +VALUE Acct-Terminate-Cause Port-Reinit 21 +VALUE Acct-Terminate-Cause Port-Disabled 22 + +VALUE NAS-Port-Type Token-Ring 20 +VALUE NAS-Port-Type FDDI 21 + +VALUE Tunnel-Type VLAN 13 diff --git a/accel-pppd/radius/dict/dictionary.rfc4072 b/accel-pppd/radius/dict/dictionary.rfc4072 new file mode 100644 index 00000000..2280d075 --- /dev/null +++ b/accel-pppd/radius/dict/dictionary.rfc4072 @@ -0,0 +1,9 @@ +# -*- text -*- +# +# Attributes and values defined in RFC 4072 +# http://www.ietf.org/rfc/4072.txt +# +# $Id: dictionary.rfc4072,v 1.1 2007/11/14 09:00:25 aland Exp $ +# + +ATTRIBUTE EAP-Key-Name 102 string diff --git a/accel-pppd/radius/dict/dictionary.rfc4372 b/accel-pppd/radius/dict/dictionary.rfc4372 new file mode 100644 index 00000000..b8af44ac --- /dev/null +++ b/accel-pppd/radius/dict/dictionary.rfc4372 @@ -0,0 +1,8 @@ +# -*- text -*- +# +# Attributes and values defined in RFC 4372. +# http://www.ietf.org/rfc/4372.txt +# +# $Id: dictionary.rfc4372,v 1.1 2007/04/07 14:47:34 aland Exp $ +# +ATTRIBUTE Chargeable-User-Identity 89 string diff --git a/accel-pppd/radius/dict/dictionary.rfc4675 b/accel-pppd/radius/dict/dictionary.rfc4675 new file mode 100644 index 00000000..8d1187f4 --- /dev/null +++ b/accel-pppd/radius/dict/dictionary.rfc4675 @@ -0,0 +1,28 @@ +# -*- text -*- +# +# Attributes and values defined in RFC 4675. +# http://www.ietf.org/rfc/4675.txt +# +# $Id: dictionary.rfc4675,v 1.1 2007/04/07 14:54:14 aland Exp $ +# + +# +# High byte = '1' (0x31) means the frames are tagged. +# High byte = '2' (0x32) means the frames are untagged. +# +# Next 12 bits MUST be zero. +# +# Lower 12 bits is the IEEE-802.1Q VLAN VID. +# +ATTRIBUTE Egress-VLANID 56 integer +ATTRIBUTE Ingress-Filters 57 integer + +# +# First byte == '1' (0x31) means that the frames are tagged. +# First byte == '2' (0x32) means that the frames are untagged. +# +ATTRIBUTE Egress-VLAN-Name 58 string +ATTRIBUTE User-Priority-Table 59 octets # 8 + +VALUE Ingress-Filters Enabled 1 +VALUE Ingress-Filters Disabled 2 diff --git a/accel-pppd/radius/dict/dictionary.rfc4679 b/accel-pppd/radius/dict/dictionary.rfc4679 new file mode 100644 index 00000000..39892a54 --- /dev/null +++ b/accel-pppd/radius/dict/dictionary.rfc4679 @@ -0,0 +1,62 @@ +# -*- text -*- +# +# Attributes and values defined in RFC 4679. +# http://www.ietf.org/rfc/4679.txt +# +# $Id: dictionary.rfc4679,v 1.1 2007/04/08 14:18:01 aland Exp $ +# + +VENDOR ADSL-Forum 3561 + +BEGIN-VENDOR ADSL-Forum + +# +# The first two attributes are prefixed with "ADSL-" because of +# conflicting names in dictionary.redback. +# +ATTRIBUTE ADSL-Agent-Circuit-Id 1 string +ATTRIBUTE ADSL-Agent-Remote-Id 2 string +ATTRIBUTE Actual-Data-Rate-Upstream 129 integer +ATTRIBUTE Actual-Data-Rate-Downstream 130 integer +ATTRIBUTE Minimum-Data-Rate-Upstream 131 integer +ATTRIBUTE Minimum-Data-Rate-Downstream 132 integer +ATTRIBUTE Attainable-Data-Rate-Upstream 133 integer +ATTRIBUTE Attainable-Data-Rate-Downstream 134 integer +ATTRIBUTE Maximum-Data-Rate-Upstream 135 integer +ATTRIBUTE Maximum-Data-Rate-Downstream 136 integer +ATTRIBUTE Minimum-Data-Rate-Upstream-Low-Power 137 integer +ATTRIBUTE Minimum-Data-Rate-Downstream-Low-Power 138 integer +ATTRIBUTE Maximum-Interleaving-Delay-Upstream 139 integer +ATTRIBUTE Actual-Interleaving-Delay-Upstream 140 integer +ATTRIBUTE Maximum-Interleaving-Delay-Downstream 141 integer +ATTRIBUTE Actual-Interleaving-Delay-Downstream 142 integer + +# +# This next attribute has a weird encoding. +# +# Octet[0] - 0x01 AAL5 +# Octet[0] - 0x02 Ethernet + +# Octet[1] - 0x00 Not Available +# Octet[1] - 0x01 Untagged Ethernet +# Octet[1] - 0x02 Single-Tagged Ethernet + +# Octet[2] - 0x00 Not available +# Octet[2] - 0x01 PPPoA LLC +# Octet[2] - 0x02 PPPoA Null +# Octet[2] - 0x03 IPoA LLC +# Octet[2] - 0x04 IPoA NULL +# Octet[2] - 0x05 Ethernet over AAL5 LLC with FCS +# Octet[2] - 0x06 Ethernet over AAL5 LLC without FCS +# Octet[2] - 0x07 Ethernet over AAL5 Null with FCS +# Octet[2] - 0x08 Ethernet over AAL5 Null without FCS +# +ATTRIBUTE Access-Loop-Encapsulation 144 octets # 3 + +# +# If this attribute exists, it means that IFW has been performed +# for the subscribers session. +# +ATTRIBUTE IWF-Session 252 octets # 0 + +END-VENDOR ADSL-Forum diff --git a/accel-pppd/radius/dict/dictionary.rfc4818 b/accel-pppd/radius/dict/dictionary.rfc4818 new file mode 100644 index 00000000..4ea59452 --- /dev/null +++ b/accel-pppd/radius/dict/dictionary.rfc4818 @@ -0,0 +1,11 @@ +# -*- text -*- +############################################################################## +# +# Attributes and values defined in RFC 4818. +# http://www.ietf.org/rfc/rfc4818.txt +# +# $Id: dictionary.rfc4818,v 1.1 2007/05/16 10:06:36 aland Exp $ +# +############################################################################## + +ATTRIBUTE Delegated-IPv6-Prefix 123 ipv6prefix diff --git a/accel-pppd/radius/dict/dictionary.rfc4849 b/accel-pppd/radius/dict/dictionary.rfc4849 new file mode 100644 index 00000000..1738eea0 --- /dev/null +++ b/accel-pppd/radius/dict/dictionary.rfc4849 @@ -0,0 +1,8 @@ +# -*- text -*- +# +# Attributes and values defined in RFC 4849. +# http://www.ietf.org/rfc/rfc4849.txt +# +# $Id: dictionary.rfc4849,v 1.2 2007/06/15 13:08:03 aland Exp $ +# +ATTRIBUTE NAS-Filter-Rule 92 string diff --git a/accel-pppd/radius/dict/dictionary.rfc5176 b/accel-pppd/radius/dict/dictionary.rfc5176 new file mode 100644 index 00000000..93089612 --- /dev/null +++ b/accel-pppd/radius/dict/dictionary.rfc5176 @@ -0,0 +1,9 @@ +# -*- text -*- +# +# Attributes and values defined in RFC 5176. +# http://www.ietf.org/rfc/rfc5176.txt +# +# $Id: dictionary.rfc5176,v 1.1 2008/03/08 16:17:44 aland Exp $ +# +VALUE Error-Cause Invalid-Attribute-Value 407 +VALUE Error-Cause Multiple-Session-Selection-Unsupported 508 diff --git a/accel-pppd/radius/dict2c.py b/accel-pppd/radius/dict2c.py new file mode 100644 index 00000000..ff0961e7 --- /dev/null +++ b/accel-pppd/radius/dict2c.py @@ -0,0 +1,20 @@ +import sys,re + +hdr = file(sys.argv[2],'w') + +def process(fname, hdr): + for line in file(fname): + if line[:-1].strip() == '': + continue + if line[0] == '#': + continue + f = re.compile('[$.a-zA-Z0-9\-]+').findall(line) + if f[0] == 'ATTRIBUTE' or f[0] == 'VENDOR': + hdr.write('#define {0} {1}\n'.format(f[1].replace('-','_').replace('.','_'), f[2])) + elif f[0] == 'VALUE': + hdr.write('#define {0}_{1} {2}\n'.format(f[1].replace('-','_').replace('.','_'), f[2].replace('-','_'),f[3])) + elif f[0] == '$INCLUDE': + process(f[1], hdr) + +if __name__ == '__main__': + process(sys.argv[1], hdr) diff --git a/accel-pppd/radius/dm_coa.c b/accel-pppd/radius/dm_coa.c new file mode 100644 index 00000000..366bb417 --- /dev/null +++ b/accel-pppd/radius/dm_coa.c @@ -0,0 +1,295 @@ +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> +#include <errno.h> +#include <string.h> +#include <fcntl.h> +#include <time.h> +#include <arpa/inet.h> +#include <netinet/in.h> +#include <sys/socket.h> +#include <openssl/md5.h> + +#include "triton.h" +#include "events.h" +#include "log.h" + +#include "radius_p.h" + +#include "memdebug.h" + +#define PD_COA_PORT 3799 + +struct dm_coa_serv_t +{ + struct triton_context_t ctx; + struct triton_md_handler_t hnd; +}; + +static struct dm_coa_serv_t serv; + +static int dm_coa_check_RA(struct rad_packet_t *pack, const char *secret) +{ + uint8_t RA[16]; + MD5_CTX ctx; + + memset(RA, 0, 16); + + MD5_Init(&ctx); + MD5_Update(&ctx, pack->buf, 4); + MD5_Update(&ctx, RA, 16); + MD5_Update(&ctx, pack->buf + 20, pack->len - 20); + MD5_Update(&ctx, secret, strlen(secret)); + MD5_Final(RA, &ctx); + + return memcmp(RA, pack->buf + 4, 16); +} + +static void dm_coa_set_RA(struct rad_packet_t *pack, const char *secret) +{ + MD5_CTX ctx; + + MD5_Init(&ctx); + MD5_Update(&ctx, pack->buf, pack->len); + MD5_Update(&ctx, secret, strlen(secret)); + MD5_Final(pack->buf + 4, &ctx); +} + +static int dm_coa_send_ack(int fd, struct rad_packet_t *req, struct sockaddr_in *addr) +{ + struct rad_packet_t *reply; + uint8_t RA[16]; + + memcpy(RA, req->buf + 4, sizeof(RA)); + + reply = rad_packet_alloc(req->code == CODE_COA_REQUEST ? CODE_COA_ACK : CODE_DISCONNECT_ACK); + if (!reply) + return -1; + + reply->id = req->id; + + if (rad_packet_build(reply, RA)) { + rad_packet_free(reply); + return -1; + } + + dm_coa_set_RA(reply, conf_dm_coa_secret); + + if (conf_verbose) { + log_ppp_info2("send "); + rad_packet_print(reply, log_ppp_info2); + } + + rad_packet_send(reply, fd, addr); + + rad_packet_free(reply); + + return 0; +} + +static int dm_coa_send_nak(int fd, struct rad_packet_t *req, struct sockaddr_in *addr, int err_code) +{ + struct rad_packet_t *reply; + uint8_t RA[16]; + + memcpy(RA, req->buf + 4, sizeof(RA)); + + reply = rad_packet_alloc(req->code == CODE_COA_REQUEST ? CODE_COA_NAK : CODE_DISCONNECT_NAK); + if (!reply) + return -1; + + reply->id = req->id; + + if (err_code) + rad_packet_add_int(reply, NULL, "Error-Cause", err_code); + + if (rad_packet_build(reply, RA)) { + rad_packet_free(reply); + return -1; + } + + dm_coa_set_RA(reply, conf_dm_coa_secret); + + if (conf_verbose) { + log_ppp_info2("send "); + rad_packet_print(reply, log_ppp_info2); + } + + rad_packet_send(reply, fd, addr); + + rad_packet_free(reply); + + return 0; +} + + +static void disconnect_request(struct radius_pd_t *rpd) +{ + if (conf_verbose) { + log_ppp_info2("recv "); + rad_packet_print(rpd->dm_coa_req, log_ppp_info2); + } + + dm_coa_send_ack(serv.hnd.fd, rpd->dm_coa_req, &rpd->dm_coa_addr); + + rad_packet_free(rpd->dm_coa_req); + + pthread_mutex_lock(&rpd->lock); + rpd->dm_coa_req = NULL; + pthread_mutex_unlock(&rpd->lock); + + ppp_terminate(rpd->ppp, TERM_ADMIN_RESET, 0); +} + +static void coa_request(struct radius_pd_t *rpd) +{ + struct ev_radius_t ev = { + .ppp = rpd->ppp, + .request = rpd->dm_coa_req, + }; + + if (conf_verbose) { + log_ppp_info2("recv "); + rad_packet_print(rpd->dm_coa_req, log_ppp_info2); + } + + triton_event_fire(EV_RADIUS_COA, &ev); + + if (ev.res) + dm_coa_send_nak(serv.hnd.fd, rpd->dm_coa_req, &rpd->dm_coa_addr, 0); + else + dm_coa_send_ack(serv.hnd.fd, rpd->dm_coa_req, &rpd->dm_coa_addr); + + rad_packet_free(rpd->dm_coa_req); + + pthread_mutex_lock(&rpd->lock); + rpd->dm_coa_req = NULL; + pthread_mutex_unlock(&rpd->lock); +} + +void dm_coa_cancel(struct radius_pd_t *rpd) +{ + triton_cancel_call(rpd->ppp->ctrl->ctx, (triton_event_func)disconnect_request); + triton_cancel_call(rpd->ppp->ctrl->ctx, (triton_event_func)coa_request); + rad_packet_free(rpd->dm_coa_req); +} + +static int dm_coa_read(struct triton_md_handler_t *h) +{ + struct rad_packet_t *pack; + struct radius_pd_t *rpd; + int err_code; + struct sockaddr_in addr; + + while (1) { + if (rad_packet_recv(h->fd, &pack, &addr)) + return 0; + + if (!pack) + continue; + + if (pack->code != CODE_DISCONNECT_REQUEST && pack->code != CODE_COA_REQUEST) { + log_warn("radius:dm_coa: unexpected code (%i) received\n", pack->code); + goto out_err_no_reply; + } + + if (dm_coa_check_RA(pack, conf_dm_coa_secret)) { + log_warn("radius:dm_coa: RA validation failed\n"); + goto out_err_no_reply; + } + + if (conf_verbose) { + log_debug("recv "); + rad_packet_print(pack, log_debug); + } + + if (rad_check_nas_pack(pack)) { + log_warn("radius:dm_coa: NAS identification failed\n"); + err_code = 403; + goto out_err; + } + + rpd = rad_find_session_pack(pack); + if (!rpd) { + log_warn("radius:dm_coa: session not found\n"); + err_code = 503; + goto out_err; + } + + if (rpd->dm_coa_req) { + pthread_mutex_unlock(&rpd->lock); + goto out_err_no_reply; + } + + rpd->dm_coa_req = pack; + memcpy(&rpd->dm_coa_addr, &addr, sizeof(addr)); + + if (pack->code == CODE_DISCONNECT_REQUEST) + triton_context_call(rpd->ppp->ctrl->ctx, (triton_event_func)disconnect_request, rpd); + else + triton_context_call(rpd->ppp->ctrl->ctx, (triton_event_func)coa_request, rpd); + + pthread_mutex_unlock(&rpd->lock); + + continue; + + out_err: + dm_coa_send_nak(h->fd, pack, &addr, err_code); + + out_err_no_reply: + rad_packet_free(pack); + } +} + +static void dm_coa_close(struct triton_context_t *ctx) +{ + struct dm_coa_serv_t *serv = container_of(ctx, typeof(*serv), ctx); + triton_md_unregister_handler(&serv->hnd); + close(serv->hnd.fd); + triton_context_unregister(ctx); +} + +static struct dm_coa_serv_t serv = { + .ctx.close = dm_coa_close, + .ctx.before_switch = log_switch, + .hnd.read = dm_coa_read, +}; + +static void __init init(void) +{ + struct sockaddr_in addr; + + if (!conf_dm_coa_secret) { + log_emerg("radius: no dm_coa_secret specified, DM/CoA disabled...\n"); + return; + } + + serv.hnd.fd = socket (PF_INET, SOCK_DGRAM, 0); + if (serv.hnd.fd < 0) { + log_emerg("radius:dm_coa: socket: %s\n", strerror(errno)); + return; + } + addr.sin_family = AF_INET; + addr.sin_port = htons (conf_dm_coa_port); + if (conf_dm_coa_server) + addr.sin_addr.s_addr = conf_dm_coa_server; + else + addr.sin_addr.s_addr = htonl (INADDR_ANY); + if (bind (serv.hnd.fd, (struct sockaddr *) &addr, sizeof (addr)) < 0) { + log_emerg("radius:dm_coa: bind: %s\n", strerror(errno)); + close(serv.hnd.fd); + return; + } + + if (fcntl(serv.hnd.fd, F_SETFL, O_NONBLOCK)) { + log_emerg("radius:dm_coa: failed to set nonblocking mode: %s\n", strerror(errno)); + close(serv.hnd.fd); + return; + } + + triton_context_register(&serv.ctx, NULL); + triton_md_register_handler(&serv.ctx, &serv.hnd); + triton_md_enable_handler(&serv.hnd, MD_MODE_READ); + triton_context_wakeup(&serv.ctx); +} diff --git a/accel-pppd/radius/packet.c b/accel-pppd/radius/packet.c new file mode 100644 index 00000000..4e24dedc --- /dev/null +++ b/accel-pppd/radius/packet.c @@ -0,0 +1,644 @@ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> + +#include "log.h" +#include "mempool.h" + +#include "radius_p.h" + +#include "memdebug.h" + +static mempool_t packet_pool; +static mempool_t attr_pool; + +struct rad_packet_t *rad_packet_alloc(int code) +{ + struct rad_packet_t *pack; + + pack = mempool_alloc(packet_pool); + if (!pack) { + log_emerg("radius:packet: out of memory\n"); + return NULL; + } + + memset(pack, 0, sizeof(*pack)); + pack->code = code; + pack->len = 20; + pack->id = 1; + INIT_LIST_HEAD(&pack->attrs); + + return pack; +} + +void print_buf(uint8_t *buf,int size) +{ + int i; + for(i=0;i<size;i++) + printf("%x ",buf[i]); + printf("\n"); +} + +int rad_packet_build(struct rad_packet_t *pack, uint8_t *RA) +{ + struct rad_attr_t *attr; + uint8_t *ptr; + + if (pack->buf) + ptr = _realloc(pack->buf, pack->len); + else + ptr = _malloc(pack->len); + + if (!ptr) { + log_emerg("radius:packet: out of memory\n"); + return -1; + } + + pack->buf = ptr; + *ptr = pack->code; ptr++; + *ptr = pack->id; ptr++; + *(uint16_t*)ptr = htons(pack->len); ptr+= 2; + memcpy(ptr, RA, 16); ptr+=16; + + list_for_each_entry(attr, &pack->attrs, entry) { + if (attr->vendor) { + *ptr = 26; ptr++; + *ptr = attr->len + 2 + 6; ptr++; + *(uint32_t *)ptr = htonl(attr->vendor->id); ptr+=4; + } + *ptr = attr->attr->id; ptr++; + *ptr = attr->len + 2; ptr++; + switch(attr->attr->type) { + case ATTR_TYPE_INTEGER: + *(uint32_t*)ptr = htonl(attr->val.integer); + break; + case ATTR_TYPE_OCTETS: + case ATTR_TYPE_STRING: + memcpy(ptr, attr->val.string, attr->len); + break; + case ATTR_TYPE_IPADDR: + *(in_addr_t*)ptr = attr->val.ipaddr; + break; + case ATTR_TYPE_DATE: + *(uint32_t*)ptr = htonl(attr->val.date); + break; + default: + log_emerg("radius:packet:BUG: unknown attribute type\n"); + abort(); + } + ptr += attr->len; + } + + //print_buf(pack->buf, pack->len); + return 0; +} + +int rad_packet_recv(int fd, struct rad_packet_t **p, struct sockaddr_in *addr) +{ + struct rad_packet_t *pack; + struct rad_attr_t *attr; + struct rad_dict_attr_t *da; + struct rad_dict_vendor_t *vendor; + uint8_t *ptr; + int n, id, len, vendor_id; + socklen_t addr_len = sizeof(*addr); + + *p = NULL; + + pack = rad_packet_alloc(0); + if (!pack) + return 0; + + pack->buf = _malloc(REQ_LENGTH_MAX); + if (!pack->buf) { + log_emerg("radius:packet: out of memory\n"); + goto out_err; + } + + while (1) { + if (addr) + n = recvfrom(fd, pack->buf, REQ_LENGTH_MAX, 0, addr, &addr_len); + else + n = read(fd, pack->buf, REQ_LENGTH_MAX); + if (n < 0) { + if (errno == EAGAIN) { + rad_packet_free(pack); + return -1; + } + if (errno != ECONNREFUSED) + log_ppp_error("radius:packet:read: %s\n", strerror(errno)); + goto out_err; + } + break; + } + + if (n < 20) { + log_ppp_warn("radius:packet: short packed received (%i)\n", n); + goto out_err; + } + + ptr = (uint8_t *)pack->buf; + + pack->code = *ptr; ptr++; + pack->id = *ptr; ptr++; + pack->len = ntohs(*(uint16_t*)ptr); ptr += 2; + + if (pack->len > n) { + log_ppp_warn("radius:packet: short packet received %i, expected %i\n", pack->len, n); + goto out_err; + } + + ptr += 16; + n -= 20; + + while (n>0) { + id = *ptr; ptr++; + len = *ptr - 2; ptr++; + if (len < 0) { + log_ppp_warn("radius:packet short attribute len received\n"); + goto out_err; + } + if (2 + len > n) { + log_ppp_warn("radius:packet: too long attribute received (%i, %i)\n", id, len); + goto out_err; + } + if (id == 26) { + vendor_id = ntohl(*(uint32_t *)ptr); + vendor = rad_dict_find_vendor_id(vendor_id); + if (vendor) { + ptr += 4; + id = *ptr; ptr++; + len = *ptr - 2; ptr++; + n -= 2 + 4; + } else + log_ppp_warn("radius:packet: vendor %i not found\n", id); + } else + vendor = NULL; + da = rad_dict_find_attr_id(vendor, id); + if (da) { + attr = mempool_alloc(attr_pool); + if (!attr) { + log_emerg("radius:packet: out of memory\n"); + goto out_err; + } + memset(attr, 0, sizeof(*attr)); + attr->vendor = vendor; + attr->attr = da; + attr->len = len; + switch (da->type) { + case ATTR_TYPE_STRING: + attr->val.string = _malloc(len+1); + if (!attr->val.string) { + log_emerg("radius:packet: out of memory\n"); + _free(attr); + goto out_err; + } + memcpy(attr->val.string, ptr, len); + attr->val.string[len] = 0; + break; + case ATTR_TYPE_OCTETS: + attr->val.octets = _malloc(len); + if (!attr->val.octets) { + log_emerg("radius:packet: out of memory\n"); + _free(attr); + goto out_err; + } + memcpy(attr->val.octets, ptr, len); + break; + case ATTR_TYPE_DATE: + case ATTR_TYPE_INTEGER: + attr->val.integer = ntohl(*(uint32_t*)ptr); + break; + case ATTR_TYPE_IPADDR: + attr->val.integer = *(uint32_t*)ptr; + break; + } + list_add_tail(&attr->entry, &pack->attrs); + } else + log_ppp_warn("radius:packet: unknown attribute received (%i,%i)\n", vendor ? vendor->id : 0, id); + ptr += len; + n -= 2 + len; + } + + *p = pack; + + return 0; + +out_err: + rad_packet_free(pack); + return 0; +} + +void rad_packet_free(struct rad_packet_t *pack) +{ + struct rad_attr_t *attr; + + if (pack->buf) + _free(pack->buf); + + while(!list_empty(&pack->attrs)) { + attr = list_entry(pack->attrs.next, typeof(*attr), entry); + list_del(&attr->entry); + if (attr->attr->type == ATTR_TYPE_STRING || attr->attr->type == ATTR_TYPE_OCTETS) + _free(attr->val.string); + mempool_free(attr); + } + + mempool_free(pack); +} + +void rad_packet_print(struct rad_packet_t *pack, void (*print)(const char *fmt, ...)) +{ + struct rad_attr_t *attr; + struct rad_dict_value_t *val; + + print("[RADIUS "); + switch(pack->code) { + case CODE_ACCESS_REQUEST: + print("Access-Request"); + break; + case CODE_ACCESS_CHALLENGE: + print("Access-Challenge"); + break; + case CODE_ACCESS_ACCEPT: + print("Access-Accept"); + break; + case CODE_ACCESS_REJECT: + print("Access-Reject"); + break; + case CODE_ACCOUNTING_REQUEST: + print("Accounting-Request"); + break; + case CODE_ACCOUNTING_RESPONSE: + print("Accounting-Response"); + break; + case CODE_DISCONNECT_REQUEST: + print("Disconnect-Request"); + break; + case CODE_DISCONNECT_ACK: + print("Disconnect-ACK"); + break; + case CODE_DISCONNECT_NAK: + print("Disconnect-NAK"); + break; + case CODE_COA_REQUEST: + print("CoA-Request"); + break; + case CODE_COA_ACK: + print("CoA-ACK"); + break; + case CODE_COA_NAK: + print("CoA-NAK"); + break; + default: + print("Unknown (%i)", pack->code); + } + print(" id=%x", pack->id); + + list_for_each_entry(attr, &pack->attrs, entry) { + if (attr->vendor) + print("<%s %s ", attr->vendor->name, attr->attr->name); + else + print(" <%s ", attr->attr->name); + switch (attr->attr->type) { + case ATTR_TYPE_INTEGER: + val = rad_dict_find_val(attr->attr, attr->val); + if (val) + print("%s", val->name); + else + print("%u", attr->val.integer); + break; + case ATTR_TYPE_STRING: + print("\"%s\"", attr->val.string); + break; + case ATTR_TYPE_IPADDR: + print("%i.%i.%i.%i", attr->val.ipaddr & 0xff, (attr->val.ipaddr >> 8) & 0xff, (attr->val.ipaddr >> 16) & 0xff, (attr->val.ipaddr >> 24) & 0xff); + break; + } + print(">"); + } + print("]\n"); +} + +int __export rad_packet_add_int(struct rad_packet_t *pack, const char *vendor_name, const char *name, int val) +{ + struct rad_attr_t *ra; + struct rad_dict_attr_t *attr; + struct rad_dict_vendor_t *vendor; + + if (pack->len + (vendor_name ? 8 : 2) + 4 >= REQ_LENGTH_MAX) + return -1; + + if (vendor_name) { + vendor = rad_dict_find_vendor_name(vendor_name); + if (!vendor) + return -1; + attr = rad_dict_find_vendor_attr(vendor, name); + } else { + vendor = NULL; + attr = rad_dict_find_attr(name); + } + + if (!attr) + return -1; + + ra = mempool_alloc(attr_pool); + if (!ra) + return -1; + + memset(ra, 0, sizeof(*ra)); + ra->vendor = vendor; + ra->attr = attr; + ra->len = 4; + ra->val.integer = val; + list_add_tail(&ra->entry, &pack->attrs); + pack->len += (vendor_name ? 8 : 2) + 4; + + return 0; +} + +int __export rad_packet_change_int(struct rad_packet_t *pack, const char *vendor_name, const char *name, int val) +{ + struct rad_attr_t *ra; + + ra = rad_packet_find_attr(pack, vendor_name, name); + if (!ra) + return -1; + + ra->val.integer = val; + + return 0; +} + +int __export rad_packet_add_octets(struct rad_packet_t *pack, const char *vendor_name, const char *name, const uint8_t *val, int len) +{ + struct rad_attr_t *ra; + struct rad_dict_attr_t *attr; + struct rad_dict_vendor_t *vendor; + + if (pack->len + (vendor_name ? 8 : 2) + len >= REQ_LENGTH_MAX) + return -1; + + if (vendor_name) { + vendor = rad_dict_find_vendor_name(vendor_name); + if (!vendor) + return -1; + attr = rad_dict_find_vendor_attr(vendor, name); + } else { + vendor = NULL; + attr = rad_dict_find_attr(name); + } + + if (!attr) + return -1; + + ra = mempool_alloc(attr_pool); + if (!ra) { + log_emerg("radius: out of memory\n"); + return -1; + } + + memset(ra, 0, sizeof(*ra)); + ra->vendor = vendor; + ra->attr = attr; + ra->len = len; + ra->val.octets = _malloc(len); + if (!ra->val.octets) { + log_emerg("radius: out of memory\n"); + _free(ra); + return -1; + } + memcpy(ra->val.octets, val, len); + list_add_tail(&ra->entry, &pack->attrs); + pack->len += (vendor_name ? 8 : 2) + len; + + return 0; +} + +int __export rad_packet_change_octets(struct rad_packet_t *pack, const char *vendor_name, const char *name, const uint8_t *val, int len) +{ + struct rad_attr_t *ra; + + ra = rad_packet_find_attr(pack, vendor_name, name); + if (!ra) + return -1; + + if (ra->len != len) { + if (pack->len - ra->len + len >= REQ_LENGTH_MAX) + return -1; + + ra->val.octets = _realloc(ra->val.octets, len); + if (!ra->val.octets) { + log_emerg("radius: out of memory\n"); + return -1; + } + + pack->len += len - ra->len; + ra->len = len; + } + + memcpy(ra->val.octets, val, len); + + return 0; +} + + +int __export rad_packet_add_str(struct rad_packet_t *pack, const char *vendor_name, const char *name, const char *val) +{ + struct rad_attr_t *ra; + struct rad_dict_attr_t *attr; + struct rad_dict_vendor_t *vendor; + int len = strlen(val); + + if (pack->len + (vendor_name ? 8 : 2) + len >= REQ_LENGTH_MAX) + return -1; + + if (vendor_name) { + vendor = rad_dict_find_vendor_name(vendor_name); + if (!vendor) + return -1; + attr = rad_dict_find_vendor_attr(vendor, name); + } else { + vendor = NULL; + attr = rad_dict_find_attr(name); + } + + if (!attr) + return -1; + + ra = mempool_alloc(attr_pool); + if (!ra) { + log_emerg("radius: out of memory\n"); + return -1; + } + + memset(ra, 0, sizeof(*ra)); + ra->vendor = vendor; + ra->attr = attr; + ra->len = len; + ra->val.string = _malloc(len + 1); + if (!ra->val.string) { + log_emerg("radius: out of memory\n"); + _free(ra); + return -1; + } + memcpy(ra->val.string, val, len); + ra->val.string[len] = 0; + list_add_tail(&ra->entry, &pack->attrs); + pack->len += (vendor_name ? 8 : 2) + len; + + return 0; +} + +int __export rad_packet_change_str(struct rad_packet_t *pack, const char *vendor_name, const char *name, const char *val, int len) +{ + struct rad_attr_t *ra; + + ra = rad_packet_find_attr(pack, vendor_name, name); + if (!ra) + return -1; + + if (ra->len != len) { + if (pack->len - ra->len + len >= REQ_LENGTH_MAX) + return -1; + + ra->val.string = _realloc(ra->val.string, len + 1); + if (!ra->val.string) { + log_emerg("radius: out of memory\n"); + return -1; + } + + pack->len += len - ra->len; + ra->len = len; + } + + memcpy(ra->val.string, val, len); + ra->val.string[len] = 0; + + return 0; +} + +int __export rad_packet_add_val(struct rad_packet_t *pack, const char *vendor_name, const char *name, const char *val) +{ + struct rad_attr_t *ra; + struct rad_dict_attr_t *attr; + struct rad_dict_value_t *v; + struct rad_dict_vendor_t *vendor; + + if (pack->len + (vendor_name ? 8 : 2) + 4 >= REQ_LENGTH_MAX) + return -1; + + if (vendor_name) { + vendor = rad_dict_find_vendor_name(vendor_name); + if (!vendor) + return -1; + attr = rad_dict_find_vendor_attr(vendor, name); + } else { + vendor = NULL; + attr = rad_dict_find_attr(name); + } + + if (!attr) + return -1; + + v = rad_dict_find_val_name(attr, val); + if (!v) + return -1; + + ra = mempool_alloc(attr_pool); + if (!ra) + return -1; + + memset(ra, 0, sizeof(*ra)); + ra->vendor = vendor; + ra->attr = attr; + ra->len = 4; + ra->val = v->val; + list_add_tail(&ra->entry, &pack->attrs); + pack->len += (vendor_name ? 8 : 2) + 4; + + return 0; +} + +int __export rad_packet_change_val(struct rad_packet_t *pack, const char *vendor_name, const char *name, const char *val) +{ + struct rad_attr_t *ra; + struct rad_dict_value_t *v; + + ra = rad_packet_find_attr(pack, vendor_name, name); + if (!ra) + return -1; + + v = rad_dict_find_val_name(ra->attr, val); + if (!v) + return -1; + + ra->val = v->val; + + return 0; +} + +int __export rad_packet_add_ipaddr(struct rad_packet_t *pack, const char *vendor_name, const char *name, in_addr_t ipaddr) +{ + return rad_packet_add_int(pack, vendor_name, name, ipaddr); +} + + +struct rad_attr_t __export *rad_packet_find_attr(struct rad_packet_t *pack, const char *vendor_name, const char *name) +{ + struct rad_attr_t *ra; + struct rad_dict_vendor_t *vendor; + + if (vendor_name) { + vendor = rad_dict_find_vendor_name(vendor_name); + if (!vendor) + return NULL; + } else + vendor = NULL; + + list_for_each_entry(ra, &pack->attrs, entry) { + if (vendor && vendor != ra->vendor) + continue; + + if (strcmp(ra->attr->name, name)) + continue; + + return ra; + } + + return NULL; +} + +int rad_packet_send(struct rad_packet_t *pack, int fd, struct sockaddr_in *addr) +{ + int n; + + while (1) { + if (addr) + n = sendto(fd, pack->buf, pack->len, 0, addr, sizeof(*addr)); + else + n = write(fd, pack->buf, pack->len); + if (n < 0) { + if (errno == EINTR) + continue; + log_ppp_error("radius:write: %s\n", strerror(errno)); + return -1; + } else if (n != pack->len) { + log_ppp_error("radius:write: short write %i, excpected %i\n", n, pack->len); + return -1; + } + break; + } + + return 0; +} + +static void __init init(void) +{ + attr_pool = mempool_create(sizeof(struct rad_attr_t)); + packet_pool = mempool_create(sizeof(struct rad_packet_t)); +} diff --git a/accel-pppd/radius/radius.c b/accel-pppd/radius/radius.c new file mode 100644 index 00000000..8976a330 --- /dev/null +++ b/accel-pppd/radius/radius.c @@ -0,0 +1,529 @@ +#include <stdlib.h> +#include <stdarg.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <arpa/inet.h> + +#include "mempool.h" +#include "events.h" +#include "log.h" +#include "ppp.h" +#include "pwdb.h" +#include "ipdb.h" +#include "ppp_auth.h" +#include "cli.h" + +#include "radius_p.h" +#include "attr_defs.h" + +#include "memdebug.h" + +#define CHAP_MD5 5 +#define MSCHAP_V1 0x80 +#define MSCHAP_V2 0x81 + +int conf_max_try = 3; +int conf_timeout = 3; +int conf_acct_timeout = 600; +char *conf_nas_identifier; +in_addr_t conf_nas_ip_address; +in_addr_t conf_gw_ip_address; +in_addr_t conf_bind; +int conf_verbose; +int conf_interim_verbose; + +in_addr_t conf_auth_server; +int conf_auth_server_port = 1812; +char *conf_auth_secret; + +in_addr_t conf_acct_server; +int conf_acct_server_port = 1813; +char *conf_acct_secret; + +in_addr_t conf_dm_coa_server; +int conf_dm_coa_port = 3799; +char *conf_dm_coa_secret; + +int conf_sid_in_auth; +int conf_require_nas_ident; +int conf_acct_interim_interval; + +unsigned long stat_auth_sent; +unsigned long stat_auth_lost; +unsigned long stat_acct_sent; +unsigned long stat_acct_lost; +unsigned long stat_interim_sent; +unsigned long stat_interim_lost; + +static LIST_HEAD(sessions); +static pthread_rwlock_t sessions_lock = PTHREAD_RWLOCK_INITIALIZER; + +static void *pd_key; +static struct ipdb_t ipdb; + +static mempool_t rpd_pool; + +int rad_proc_attrs(struct rad_req_t *req) +{ + struct rad_attr_t *attr; + int res = 0; + + req->rpd->acct_interim_interval = conf_acct_interim_interval; + + list_for_each_entry(attr, &req->reply->attrs, entry) { + if (attr->vendor) + continue; + switch(attr->attr->id) { + case Framed_IP_Address: + if (!conf_gw_ip_address) + log_ppp_warn("radius: gw-ip-address not specified, cann't assign IP address...\n"); + else { + req->rpd->ipaddr.owner = &ipdb; + req->rpd->ipaddr.peer_addr = attr->val.ipaddr; + req->rpd->ipaddr.addr = conf_gw_ip_address; + } + break; + case Acct_Interim_Interval: + req->rpd->acct_interim_interval = attr->val.integer; + break; + case Session_Timeout: + req->rpd->session_timeout.period = attr->val.integer * 1000; + break; + case Class: + if (!req->rpd->attr_class) + req->rpd->attr_class = _malloc(attr->len); + else if (req->rpd->attr_class_len != attr->len) + req->rpd->attr_class = _realloc(req->rpd->attr_class, attr->len); + memcpy(req->rpd->attr_class, attr->val.octets, attr->len); + req->rpd->attr_class_len = attr->len; + break; + case State: + if (!req->rpd->attr_state) + req->rpd->attr_state = _malloc(attr->len); + else if (req->rpd->attr_state_len != attr->len) + req->rpd->attr_state = _realloc(req->rpd->attr_state, attr->len); + memcpy(req->rpd->attr_state, attr->val.octets, attr->len); + req->rpd->attr_state_len = attr->len; + break; + case Termination_Action: + req->rpd->termination_action = attr->val.integer; + break; + } + } + + return res; +} + +static int check(struct pwdb_t *pwdb, struct ppp_t *ppp, const char *username, int type, va_list _args) +{ + int r = PWDB_NO_IMPL; + va_list args; + int chap_type; + struct radius_pd_t *rpd = find_pd(ppp); + + va_copy(args, _args); + + switch(type) { + case PPP_PAP: + r = rad_auth_pap(rpd, username, args); + break; + case PPP_CHAP: + chap_type = va_arg(args, int); + switch(chap_type) { + case CHAP_MD5: + r = rad_auth_chap_md5(rpd, username, args); + break; + case MSCHAP_V1: + r = rad_auth_mschap_v1(rpd, username, args); + break; + case MSCHAP_V2: + r = rad_auth_mschap_v2(rpd, username, args); + break; + } + break; + } + + va_end(args); + + return r; +} + +static struct ipdb_item_t *get_ip(struct ppp_t *ppp) +{ + struct radius_pd_t *rpd = find_pd(ppp); + + if (rpd->ipaddr.peer_addr) + return &rpd->ipaddr; + return NULL; +} + +static void session_timeout(struct triton_timer_t *t) +{ + struct radius_pd_t *rpd = container_of(t, typeof(*rpd), session_timeout); + log_ppp_msg("radius: session timed out\n"); + + if (rpd->ppp->stop_time) + return; + + if (rpd->termination_action == Termination_Action_RADIUS_Request) { + if (ppp_auth_restart(rpd->ppp)) + ppp_terminate(rpd->ppp, TERM_SESSION_TIMEOUT, 0); + } else + ppp_terminate(rpd->ppp, TERM_SESSION_TIMEOUT, 0); +} + +static void ppp_starting(struct ppp_t *ppp) +{ + struct radius_pd_t *rpd = mempool_alloc(rpd_pool); + + memset(rpd, 0, sizeof(*rpd)); + rpd->pd.key = &pd_key; + rpd->ppp = ppp; + pthread_mutex_init(&rpd->lock, NULL); + INIT_LIST_HEAD(&rpd->plugin_list); + list_add_tail(&rpd->pd.entry, &ppp->pd_list); + + pthread_rwlock_wrlock(&sessions_lock); + list_add_tail(&rpd->entry, &sessions); + pthread_rwlock_unlock(&sessions_lock); +} + +static void ppp_acct_start(struct ppp_t *ppp) +{ + struct radius_pd_t *rpd = find_pd(ppp); + + if (rad_acct_start(rpd)) { + ppp_terminate(rpd->ppp, TERM_NAS_ERROR, 0); + return; + } + + if (rpd->session_timeout.period) { + rpd->session_timeout.expire = session_timeout; + triton_timer_add(ppp->ctrl->ctx, &rpd->session_timeout, 0); + } +} +static void ppp_finishing(struct ppp_t *ppp) +{ + struct radius_pd_t *rpd = find_pd(ppp); + + rad_acct_stop(rpd); +} +static void ppp_finished(struct ppp_t *ppp) +{ + struct radius_pd_t *rpd = find_pd(ppp); + + pthread_rwlock_wrlock(&sessions_lock); + pthread_mutex_lock(&rpd->lock); + list_del(&rpd->entry); + pthread_mutex_unlock(&rpd->lock); + pthread_rwlock_unlock(&sessions_lock); + + if (rpd->auth_req) + rad_req_free(rpd->auth_req); + + if (rpd->acct_req) + rad_req_free(rpd->acct_req); + + if (rpd->dm_coa_req) + dm_coa_cancel(rpd); + + if (rpd->session_timeout.tpd) + triton_timer_del(&rpd->session_timeout); + + if (rpd->attr_class) + _free(rpd->attr_class); + + if (rpd->attr_state) + _free(rpd->attr_state); + + list_del(&rpd->pd.entry); + + mempool_free(rpd); +} + +struct radius_pd_t *find_pd(struct ppp_t *ppp) +{ + struct ppp_pd_t *pd; + struct radius_pd_t *rpd; + + list_for_each_entry(pd, &ppp->pd_list, entry) { + if (pd->key == &pd_key) { + rpd = container_of(pd, typeof(*rpd), pd); + return rpd; + } + } + log_emerg("radius:BUG: rpd not found\n"); + abort(); +} + + +struct radius_pd_t *rad_find_session(const char *sessionid, const char *username, int port_id, in_addr_t ipaddr, const char *csid) +{ + struct radius_pd_t *rpd; + + pthread_rwlock_rdlock(&sessions_lock); + list_for_each_entry(rpd, &sessions, entry) { + if (!rpd->ppp->username) + continue; + if (sessionid && strcmp(sessionid, rpd->ppp->sessionid)) + continue; + if (username && strcmp(username, rpd->ppp->username)) + continue; + if (port_id >= 0 && port_id != rpd->ppp->unit_idx) + continue; + if (ipaddr && ipaddr != rpd->ppp->peer_ipaddr) + continue; + if (csid && rpd->ppp->ctrl->calling_station_id && strcmp(csid, rpd->ppp->ctrl->calling_station_id)) + continue; + pthread_mutex_lock(&rpd->lock); + pthread_rwlock_unlock(&sessions_lock); + return rpd; + } + pthread_rwlock_unlock(&sessions_lock); + return NULL; +} + +struct radius_pd_t *rad_find_session_pack(struct rad_packet_t *pack) +{ + struct rad_attr_t *attr; + const char *sessionid = NULL; + const char *username = NULL; + const char *csid = NULL; + int port_id = -1; + in_addr_t ipaddr = 0; + + list_for_each_entry(attr, &pack->attrs, entry) { + switch(attr->attr->id) { + case Acct_Session_Id: + sessionid = attr->val.string; + break; + case User_Name: + username = attr->val.string; + break; + case NAS_Port: + port_id = attr->val.integer; + break; + case Framed_IP_Address: + ipaddr = attr->val.ipaddr; + break; + case Calling_Station_Id: + csid = attr->val.string; + break; + } + } + + if (!sessionid && !username && port_id == -1 && ipaddr == 0 && !csid) + return NULL; + + if (username && !sessionid && port_id == -1 && ipaddr == 0) + return NULL; + + return rad_find_session(sessionid, username, port_id, ipaddr, csid); +} + +int rad_check_nas_pack(struct rad_packet_t *pack) +{ + struct rad_attr_t *attr; + const char *ident = NULL; + in_addr_t ipaddr = 0; + + list_for_each_entry(attr, &pack->attrs, entry) { + if (!strcmp(attr->attr->name, "NAS-Identifier")) + ident = attr->val.string; + else if (!strcmp(attr->attr->name, "NAS-IP-Address")) + ipaddr = attr->val.ipaddr; + } + + if (conf_require_nas_ident && !ident && !ipaddr) + return -1; + + if (conf_nas_identifier && ident && strcmp(conf_nas_identifier, ident)) + return -1; + + if (conf_nas_ip_address && ipaddr && conf_nas_ip_address != ipaddr) + return -1; + + return 0; +} + +static int show_stat_exec(const char *cmd, char * const *fields, int fields_cnt, void *client) +{ + cli_send(client, "radius:\r\n"); + cli_sendv(client, " auth sent: %lu\r\n", stat_auth_sent); + cli_sendv(client, " auth lost: %lu\r\n", stat_auth_lost); + cli_sendv(client, " acct sent: %lu\r\n", stat_acct_sent); + cli_sendv(client, " acct lost: %lu\r\n", stat_acct_lost); + cli_sendv(client, " interim sent: %lu\r\n", stat_interim_sent); + cli_sendv(client, " interim lost: %lu\r\n", stat_interim_lost); + return CLI_CMD_OK; +} + +void __export rad_register_plugin(struct ppp_t *ppp, struct rad_plugin_t *plugin) +{ + struct radius_pd_t *rpd = find_pd(ppp); + + if (!rpd) + return; + + list_add_tail(&plugin->entry, &rpd->plugin_list); +} + +static struct ipdb_t ipdb = { + .get = get_ip, +}; + +static struct pwdb_t pwdb = { + .check = check, +}; + +static int parse_server(const char *opt, in_addr_t *addr, int *port, char **secret) +{ + char *str = _strdup(opt); + char *p1, *p2; + + p1 = strstr(str, ":"); + p2 = strstr(str, ","); + + if (p1) + *p1 = 0; + if (p2) + *p2 = 0; + else + return -1; + + *addr = inet_addr(str); + + if (p1) { + *port = atoi(p1 + 1); + if (*port <=0 ) + return -1; + } + + p1 = _strdup(p2 + 1); + p2 = *secret; + *secret = p1; + if (p2) + _free(p2); + + _free(str); + + return 0; +} + +static int load_config(void) +{ + char *opt; + + opt = conf_get_opt("radius", "max-try"); + if (opt && atoi(opt) > 0) + conf_max_try = atoi(opt); + + opt = conf_get_opt("radius", "timeout"); + if (opt && atoi(opt) > 0) + conf_timeout = atoi(opt); + + opt = conf_get_opt("radius", "acct-timeout"); + if (opt && atoi(opt) > 0) + conf_acct_timeout = atoi(opt); + + opt = conf_get_opt("radius", "verbose"); + if (opt && atoi(opt) > 0) + conf_verbose = 1; + + opt = conf_get_opt("radius", "interim-verbose"); + if (opt && atoi(opt) > 0) + conf_interim_verbose = 1; + + opt = conf_get_opt("radius", "nas-ip-address"); + if (opt) + conf_nas_ip_address = inet_addr(opt); + + if (conf_nas_identifier) + _free(conf_nas_identifier); + opt = conf_get_opt("radius", "nas-identifier"); + if (opt) + conf_nas_identifier = _strdup(opt); + else + conf_nas_identifier = NULL; + + opt = conf_get_opt("radius", "gw-ip-address"); + if (opt) + conf_gw_ip_address = inet_addr(opt); + + opt = conf_get_opt("radius", "bind"); + if (opt) + conf_bind = inet_addr(opt); + else if (conf_nas_ip_address) + conf_bind = conf_nas_ip_address; + + opt = conf_get_opt("radius", "auth-server"); + if (!opt) + opt = conf_get_opt("radius", "auth_server"); + if (!opt) { + log_emerg("radius: auth-server not specified\n"); + return -1; + } else if (parse_server(opt, &conf_auth_server, &conf_auth_server_port, &conf_auth_secret)) { + log_emerg("radius: failed to parse auth_server\n"); + return -1; + } + + opt = conf_get_opt("radius", "acct-server"); + if (!opt) + opt = conf_get_opt("radius", "acct_server"); + if (!opt) + log_emerg("radius: acct-server not specified\n"); + if (opt && parse_server(opt, &conf_acct_server, &conf_acct_server_port, &conf_acct_secret)) { + log_emerg("radius: failed to parse acct_server\n"); + return -1; + } + + opt = conf_get_opt("radius", "dae-server"); + if (opt && parse_server(opt, &conf_dm_coa_server, &conf_dm_coa_port, &conf_dm_coa_secret)) { + log_emerg("radius: failed to parse dae-server\n"); + return -1; + } + + opt = conf_get_opt("radius", "sid_in_auth"); + if (opt && atoi(opt) > 0) + conf_sid_in_auth = 1; + + opt = conf_get_opt("radius", "require-nas-identification"); + if (opt && atoi(opt) > 0) + conf_require_nas_ident = 1; + + opt = conf_get_opt("radius", "acct-interim-interval"); + if (opt && atoi(opt) > 0) + conf_acct_interim_interval = atoi(opt); + + return 0; +} + +static void __init radius_init(void) +{ + char *opt; + char *dict = DICTIONARY; + + rpd_pool = mempool_create(sizeof(struct radius_pd_t)); + + if (load_config()) + _exit(EXIT_FAILURE); + + opt = conf_get_opt("radius", "dictionary"); + if (opt) + dict = opt; + + if (rad_dict_load(dict)) + _exit(EXIT_FAILURE); + + pwdb_register(&pwdb); + ipdb_register(&ipdb); + + triton_event_register_handler(EV_PPP_STARTING, (triton_event_func)ppp_starting); + triton_event_register_handler(EV_PPP_ACCT_START, (triton_event_func)ppp_acct_start); + triton_event_register_handler(EV_PPP_FINISHING, (triton_event_func)ppp_finishing); + triton_event_register_handler(EV_PPP_FINISHED, (triton_event_func)ppp_finished); + triton_event_register_handler(EV_CONFIG_RELOAD, (triton_event_func)load_config); + + cli_register_simple_cmd2(show_stat_exec, NULL, 2, "show", "stat"); +} diff --git a/accel-pppd/radius/radius.h b/accel-pppd/radius/radius.h new file mode 100644 index 00000000..ad229d22 --- /dev/null +++ b/accel-pppd/radius/radius.h @@ -0,0 +1,117 @@ +#ifndef __RADIUS_H +#define __RADIUS_H + +#include <stdint.h> + +#define REQ_LENGTH_MAX 4096 + +#define ATTR_TYPE_INTEGER 0 +#define ATTR_TYPE_STRING 1 +#define ATTR_TYPE_OCTETS 2 +#define ATTR_TYPE_DATE 3 +#define ATTR_TYPE_IPADDR 4 + +#define CODE_ACCESS_REQUEST 1 +#define CODE_ACCESS_ACCEPT 2 +#define CODE_ACCESS_REJECT 3 +#define CODE_ACCESS_CHALLENGE 11 + +#define CODE_ACCOUNTING_REQUEST 4 +#define CODE_ACCOUNTING_RESPONSE 5 + +#define CODE_DISCONNECT_REQUEST 40 +#define CODE_DISCONNECT_ACK 41 +#define CODE_DISCONNECT_NAK 42 +#define CODE_COA_REQUEST 43 +#define CODE_COA_ACK 44 +#define CODE_COA_NAK 45 + +typedef union +{ + int integer; + char *string; + uint8_t *octets; + time_t date; + in_addr_t ipaddr; +} rad_value_t; + +struct rad_dict_t +{ + struct list_head items; + struct list_head vendors; +}; + +struct rad_dict_vendor_t +{ + struct list_head entry; + int id; + const char *name; + struct list_head items; +}; + +struct rad_dict_value_t +{ + struct list_head entry; + rad_value_t val; + const char *name; +}; + +struct rad_dict_attr_t +{ + struct list_head entry; + const char *name; + int id; + int type; + struct list_head values; +}; + +struct rad_attr_t +{ + struct list_head entry; + struct rad_dict_attr_t *attr; + struct rad_dict_vendor_t *vendor; + //struct rad_dict_value_t *val; + rad_value_t val; + int len; +}; + +struct rad_packet_t +{ + int code; + uint8_t id; + int len; + struct list_head attrs; + void *buf; +}; + +struct rad_plugin_t +{ + struct list_head entry; + int (*send_access_request)(struct rad_plugin_t *, struct rad_packet_t *pack); + int (*send_accounting_request)(struct rad_plugin_t *, struct rad_packet_t *pack); +}; + +struct ppp_t; + +void rad_register_plugin(struct ppp_t *, struct rad_plugin_t *); + +struct rad_dict_attr_t *rad_dict_find_attr(const char *name); +struct rad_dict_attr_t *rad_dict_find_attr_id(struct rad_dict_vendor_t *vendor, int type); +struct rad_dict_value_t *rad_dict_find_val_name(struct rad_dict_attr_t *, const char *name); +struct rad_dict_value_t *rad_dict_find_val(struct rad_dict_attr_t *, rad_value_t val); +struct rad_dict_vendor_t *rad_dict_find_vendor_name(const char *name); +struct rad_dict_vendor_t *rad_dict_find_vendor_id(int id); +struct rad_dict_attr_t *rad_dict_find_vendor_attr(struct rad_dict_vendor_t *vendor, const char *name); + +struct rad_attr_t *rad_packet_find_attr(struct rad_packet_t *pack, const char *vendor, const char *name); +int rad_packet_add_int(struct rad_packet_t *pack, const char *vendor, const char *name, int val); +int rad_packet_add_val(struct rad_packet_t *pack, const char *vendor, const char *name, const char *val); +int rad_packet_add_str(struct rad_packet_t *pack, const char *vendor, const char *name, const char *val); +int rad_packet_add_octets(struct rad_packet_t *pack, const char *vendor, const char *name, const uint8_t *val, int len); +int rad_packet_change_int(struct rad_packet_t *pack, const char *vendor, const char *name, int val); +int rad_packet_change_val(struct rad_packet_t *pack, const char *vendor, const char *name, const char *val); +int rad_packet_change_octets(struct rad_packet_t *pack, const char *vendor, const char *name, const uint8_t *val, int len); +int rad_packet_add_ipaddr(struct rad_packet_t *pack, const char *vendor, const char *name, in_addr_t ipaddr); + +#endif + diff --git a/accel-pppd/radius/radius_p.h b/accel-pppd/radius/radius_p.h new file mode 100644 index 00000000..71c1a637 --- /dev/null +++ b/accel-pppd/radius/radius_p.h @@ -0,0 +1,122 @@ +#ifndef __RADIUS_P_H +#define __RADIUS_P_H + +#include <netinet/in.h> +#include <pthread.h> + +#include "triton.h" +#include "radius.h" +#include "ppp.h" +#include "ipdb.h" + +struct radius_pd_t +{ + struct list_head entry; + struct ppp_pd_t pd; + struct ppp_t *ppp; + pthread_mutex_t lock; + + struct rad_req_t *auth_req; + struct rad_req_t *acct_req; + struct triton_timer_t acct_interim_timer; + uint32_t acct_input_octets; + uint32_t acct_output_octets; + uint32_t acct_input_gigawords; + uint32_t acct_output_gigawords; + + struct triton_timer_t session_timeout; + + struct rad_packet_t *dm_coa_req; + struct sockaddr_in dm_coa_addr; + + struct ipdb_item_t ipaddr; + int acct_interim_interval; + time_t acct_timestamp; + + uint8_t *attr_class; + int attr_class_len; + uint8_t *attr_state; + int attr_state_len; + int termination_action; + + struct list_head plugin_list; +}; + +struct rad_req_t +{ + struct triton_context_t ctx; + struct triton_md_handler_t hnd; + struct triton_timer_t timeout; + uint8_t RA[16]; + struct rad_packet_t *pack; + struct rad_packet_t *reply; + in_addr_t server_addr; + int server_port; + + struct radius_pd_t *rpd; +}; + +extern int conf_max_try; +extern int conf_timeout; +extern int conf_acct_timeout; +extern int conf_verbose; +extern int conf_interim_verbose; +extern char *conf_nas_identifier; +extern in_addr_t conf_nas_ip_address; +extern in_addr_t conf_bind; +extern in_addr_t conf_gw_ip_address; +extern in_addr_t conf_auth_server; +extern char *conf_auth_secret; +extern int conf_auth_server_port; +extern in_addr_t conf_acct_server; +extern char *conf_acct_secret; +extern int conf_acct_server_port; +extern char *conf_dm_coa_secret; +extern int conf_sid_in_auth; +extern int conf_require_nas_ident; +extern in_addr_t conf_dm_coa_server; +extern int conf_dm_coa_port; +extern int conf_acct_interim_interval; + +extern unsigned long stat_auth_sent; +extern unsigned long stat_auth_lost; +extern unsigned long stat_acct_sent; +extern unsigned long stat_acct_lost; +extern unsigned long stat_interim_sent; +extern unsigned long stat_interim_lost; + +int rad_check_nas_pack(struct rad_packet_t *pack); +struct radius_pd_t *rad_find_session(const char *sessionid, const char *username, int port_id, in_addr_t ipaddr, const char *csid); +struct radius_pd_t *rad_find_session_pack(struct rad_packet_t *pack); + +int rad_dict_load(const char *fname); +void rad_dict_free(struct rad_dict_t *dict); + +struct rad_req_t *rad_req_alloc(struct radius_pd_t *rpd, int code, const char *username); +int rad_req_acct_fill(struct rad_req_t *); +void rad_req_free(struct rad_req_t *); +int rad_req_send(struct rad_req_t *, int verbose); +int rad_req_wait(struct rad_req_t *, int); + +struct radius_pd_t *find_pd(struct ppp_t *ppp); +int rad_proc_attrs(struct rad_req_t *req); + +int rad_auth_pap(struct radius_pd_t *rpd, const char *username, va_list args); +int rad_auth_chap_md5(struct radius_pd_t *rpd, const char *username, va_list args); +int rad_auth_mschap_v1(struct radius_pd_t *rpd, const char *username, va_list args); +int rad_auth_mschap_v2(struct radius_pd_t *rpd, const char *username, va_list args); + +int rad_acct_start(struct radius_pd_t *rpd); +void rad_acct_stop(struct radius_pd_t *rpd); + +struct rad_packet_t *rad_packet_alloc(int code); +int rad_packet_build(struct rad_packet_t *pack, uint8_t *RA); +int rad_packet_recv(int fd, struct rad_packet_t **, struct sockaddr_in *addr); +void rad_packet_free(struct rad_packet_t *); +void rad_packet_print(struct rad_packet_t *pack, void (*print)(const char *fmt, ...)); +int rad_packet_send(struct rad_packet_t *pck, int fd, struct sockaddr_in *addr); + +void dm_coa_cancel(struct radius_pd_t *pd); + +#endif + diff --git a/accel-pppd/radius/req.c b/accel-pppd/radius/req.c new file mode 100644 index 00000000..a384f236 --- /dev/null +++ b/accel-pppd/radius/req.c @@ -0,0 +1,277 @@ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <sched.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include "log.h" +#include "radius_p.h" + +#include "memdebug.h" + +static int urandom_fd; + +static int rad_req_read(struct triton_md_handler_t *h); +static void rad_req_timeout(struct triton_timer_t *t); + +struct rad_req_t *rad_req_alloc(struct radius_pd_t *rpd, int code, const char *username) +{ + struct rad_plugin_t *plugin; + struct rad_req_t *req = _malloc(sizeof(*req)); + + if (!req) + return NULL; + + memset(req, 0, sizeof(*req)); + req->rpd = rpd; + req->hnd.fd = -1; + req->ctx.before_switch = log_switch; + + req->server_addr = conf_auth_server; + req->server_port = conf_auth_server_port; + + while (1) { + if (read(urandom_fd, req->RA, 16) != 16) { + if (errno == EINTR) + continue; + log_ppp_error("radius:req:read urandom: %s\n", strerror(errno)); + goto out_err; + } + break; + } + + req->pack = rad_packet_alloc(code); + if (!req->pack) + goto out_err; + + if (rad_packet_add_str(req->pack, NULL, "User-Name", username)) + goto out_err; + if (conf_nas_identifier) + if (rad_packet_add_str(req->pack, NULL, "NAS-Identifier", conf_nas_identifier)) + goto out_err; + if (conf_nas_ip_address) + if (rad_packet_add_ipaddr(req->pack, NULL, "NAS-IP-Address", conf_nas_ip_address)) + goto out_err; + if (rad_packet_add_int(req->pack, NULL, "NAS-Port", rpd->ppp->unit_idx)) + goto out_err; + if (rad_packet_add_val(req->pack, NULL, "NAS-Port-Type", "Virtual")) + goto out_err; + if (rad_packet_add_val(req->pack, NULL, "Service-Type", "Framed-User")) + goto out_err; + if (rad_packet_add_val(req->pack, NULL, "Framed-Protocol", "PPP")) + goto out_err; + if (rpd->ppp->ctrl->calling_station_id) + if (rad_packet_add_str(req->pack, NULL, "Calling-Station-Id", rpd->ppp->ctrl->calling_station_id)) + goto out_err; + if (rpd->ppp->ctrl->called_station_id) + if (rad_packet_add_str(req->pack, NULL, "Called-Station-Id", rpd->ppp->ctrl->called_station_id)) + goto out_err; + if (rpd->attr_class) + if (rad_packet_add_octets(req->pack, NULL, "Class", rpd->attr_class, rpd->attr_class_len)) + goto out_err; + + list_for_each_entry(plugin, &req->rpd->plugin_list, entry) { + switch (code) { + case CODE_ACCESS_REQUEST: + if (plugin->send_access_request && plugin->send_access_request(plugin, req->pack)) + goto out_err; + break; + case CODE_ACCOUNTING_REQUEST: + if (plugin->send_accounting_request && plugin->send_accounting_request(plugin, req->pack)) + goto out_err; + break; + } + } + + return req; + +out_err: + rad_req_free(req); + return NULL; +} + +int rad_req_acct_fill(struct rad_req_t *req) +{ + req->server_addr = conf_acct_server; + req->server_port = conf_acct_server_port; + + memset(req->RA, 0, sizeof(req->RA)); + + if (rad_packet_add_val(req->pack, NULL, "Acct-Status-Type", "Start")) + return -1; + if (rad_packet_add_val(req->pack, NULL, "Acct-Authentic", "RADIUS")) + return -1; + if (rad_packet_add_str(req->pack, NULL, "Acct-Session-Id", req->rpd->ppp->sessionid)) + return -1; + if (rad_packet_add_int(req->pack, NULL, "Acct-Session-Time", 0)) + return -1; + if (rad_packet_add_int(req->pack, NULL, "Acct-Input-Octets", 0)) + return -1; + if (rad_packet_add_int(req->pack, NULL, "Acct-Output-Octets", 0)) + return -1; + if (rad_packet_add_int(req->pack, NULL, "Acct-Input-Packets", 0)) + return -1; + if (rad_packet_add_int(req->pack, NULL, "Acct-Output-Packets", 0)) + return -1; + if (rad_packet_add_int(req->pack, NULL, "Acct-Input-Gigawords", 0)) + return -1; + if (rad_packet_add_int(req->pack, NULL, "Acct-Output-Gigawords", 0)) + return -1; + if (rad_packet_add_int(req->pack, NULL, "Acct-Delay-Time", 0)) + return -1; + if (rad_packet_add_ipaddr(req->pack, NULL, "Framed-IP-Address", req->rpd->ppp->peer_ipaddr)) + return -1; + + return 0; +} + +void rad_req_free(struct rad_req_t *req) +{ + if (req->hnd.fd >= 0 ) + close(req->hnd.fd); + if (req->pack) + rad_packet_free(req->pack); + if (req->reply) + rad_packet_free(req->reply); + _free(req); +} + +static int make_socket(struct rad_req_t *req) +{ + struct sockaddr_in addr; + + req->hnd.fd = socket(PF_INET, SOCK_DGRAM, 0); + if (req->hnd.fd < 0) { + log_ppp_error("radius:socket: %s\n", strerror(errno)); + return -1; + } + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + + if (conf_bind) { + addr.sin_addr.s_addr = conf_bind; + if (bind(req->hnd.fd, (struct sockaddr *) &addr, sizeof(addr))) { + log_ppp_error("radius:bind: %s\n", strerror(errno)); + goto out_err; + } + } + + addr.sin_addr.s_addr = req->server_addr; + addr.sin_port = htons(req->server_port); + + if (connect(req->hnd.fd, (struct sockaddr *) &addr, sizeof(addr))) { + log_ppp_error("radius:connect: %s\n", strerror(errno)); + goto out_err; + } + + if (fcntl(req->hnd.fd, F_SETFL, O_NONBLOCK)) { + log_ppp_error("radius: failed to set nonblocking mode: %s\n", strerror(errno)); + goto out_err; + } + + return 0; + +out_err: + close(req->hnd.fd); + req->hnd.fd = -1; + return -1; +} + +int rad_req_send(struct rad_req_t *req, int verbose) +{ + if (req->hnd.fd == -1 && make_socket(req)) + return -1; + + if (!req->pack->buf && rad_packet_build(req->pack, req->RA)) + goto out_err; + + if (verbose) { + log_ppp_info1("send "); + rad_packet_print(req->pack, log_ppp_info1); + } + + rad_packet_send(req->pack, req->hnd.fd, NULL); + + return 0; + +out_err: + close(req->hnd.fd); + req->hnd.fd = -1; + return -1; +} + +static void req_wakeup(struct rad_req_t *req) +{ + struct triton_context_t *ctx = req->rpd->ppp->ctrl->ctx; + triton_timer_del(&req->timeout); + triton_md_unregister_handler(&req->hnd); + triton_context_unregister(&req->ctx); + triton_context_wakeup(ctx); +} +static int rad_req_read(struct triton_md_handler_t *h) +{ + struct rad_req_t *req = container_of(h, typeof(*req), hnd); + struct rad_packet_t *pack; + int r; + + while (1) { + r = rad_packet_recv(h->fd, &pack, NULL); + + if (pack) { + if (req->reply) + rad_packet_free(req->reply); + req->reply = pack; + } + + if (r) + break; + } + + req_wakeup(req); + + return 1; +} +static void rad_req_timeout(struct triton_timer_t *t) +{ + struct rad_req_t *req = container_of(t, typeof(*req), timeout); + + req_wakeup(req); +} + +int rad_req_wait(struct rad_req_t *req, int timeout) +{ + req->hnd.read = rad_req_read; + req->timeout.expire = rad_req_timeout; + + triton_context_register(&req->ctx, req->rpd->ppp); + triton_md_register_handler(&req->ctx, &req->hnd); + triton_md_enable_handler(&req->hnd, MD_MODE_READ); + + req->timeout.period = timeout * 1000; + triton_timer_add(&req->ctx, &req->timeout, 0); + + triton_context_wakeup(&req->ctx); + + triton_context_schedule(); + + if (conf_verbose && req->reply) { + log_ppp_info1("recv "); + rad_packet_print(req->reply, log_ppp_info1); + } + return 0; +} + +void __init req_init(void) +{ + urandom_fd = open("/dev/urandom", O_RDONLY); + if (!urandom_fd) { + log_emerg("radius:req: open /dev/urandom: %s\n", strerror(errno)); + _exit(EXIT_FAILURE); + } +} |