From 84f225b4c07ecb258017ad9eab7b4e4be90ba28e Mon Sep 17 00:00:00 2001 From: moznion Date: Thu, 3 Dec 2020 02:21:33 +0900 Subject: [PATCH] Support RFC2869 --- README.md | 2 +- dicts/dictionary.rfc2869 | 42 +++++ radius/src/attributes.rs | 4 + radius/src/avp.rs | 2 +- radius/src/lib.rs | 1 + radius/src/packet.rs | 4 + radius/src/rfc2869.rs | 374 +++++++++++++++++++++++++++++++++++++++ 7 files changed, 427 insertions(+), 2 deletions(-) create mode 100644 dicts/dictionary.rfc2869 create mode 100644 radius/src/rfc2869.rs diff --git a/README.md b/README.md index aa2ef93..2a899e9 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ This supports the following RFC dictionaries at the moment: - [RFC2866](https://tools.ietf.org/html/rfc2866) - [RFC2867](https://tools.ietf.org/html/rfc2867) - [RFC2868](https://tools.ietf.org/html/rfc2868) +- [RFC2869](https://tools.ietf.org/html/rfc2869) - [RFC3576](https://tools.ietf.org/html/rfc3576) - [RFC4072](https://tools.ietf.org/html/rfc4072) - [RFC5090](https://tools.ietf.org/html/rfc5090) @@ -32,7 +33,6 @@ Simple example implementations are here: - retransmission feature on the client - Support the following RFC dictionaries: - - rfc2869 - rfc3162 - rfc3580 - rfc4372 diff --git a/dicts/dictionary.rfc2869 b/dicts/dictionary.rfc2869 new file mode 100644 index 0000000..45dee25 --- /dev/null +++ b/dicts/dictionary.rfc2869 @@ -0,0 +1,42 @@ +# -*- text -*- +# Copyright (C) 2020 The FreeRADIUS Server project and contributors +# This work is licensed under CC-BY version 4.0 https://creativecommons.org/licenses/by/4.0 +# Version $Id$ +# +# Attributes and values defined in RFC 2869. +# http://www.ietf.org/rfc/rfc2869.txt +# +# $Id$ +# +ATTRIBUTE Acct-Input-Gigawords 52 integer +ATTRIBUTE Acct-Output-Gigawords 53 integer + +ATTRIBUTE Event-Timestamp 55 date + +ATTRIBUTE ARAP-Password 70 octets[16] +ATTRIBUTE ARAP-Features 71 octets[14] +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 concat +ATTRIBUTE Message-Authenticator 80 octets + +ATTRIBUTE ARAP-Challenge-Response 84 octets[8] +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/radius/src/attributes.rs b/radius/src/attributes.rs index de5a6aa..6bbf3a2 100644 --- a/radius/src/attributes.rs +++ b/radius/src/attributes.rs @@ -37,6 +37,10 @@ impl Attributes { self.0.push(avp) } + pub(crate) fn extend(&mut self, avps: Vec) { + self.0.extend(avps) + } + pub(crate) fn del(&mut self, typ: AVPType) { self.0 = self .0 diff --git a/radius/src/avp.rs b/radius/src/avp.rs index 004026b..4b849a5 100644 --- a/radius/src/avp.rs +++ b/radius/src/avp.rs @@ -19,7 +19,7 @@ pub enum AVPError { InvalidRequestAuthenticatorLength(), #[error("invalid attribute length: {0}")] InvalidAttributeLengthError(usize), - // TODO: more meaningful error message + // ^ TODO: more meaningful error message #[error("unexpected decoding error: {0}")] UnexpectedDecodingError(String), #[error("invalid salt. the MSB has to be 1, but given value isn't: {0}")] diff --git a/radius/src/lib.rs b/radius/src/lib.rs index c266342..7c0da0a 100644 --- a/radius/src/lib.rs +++ b/radius/src/lib.rs @@ -7,6 +7,7 @@ pub mod rfc2865; pub mod rfc2866; pub mod rfc2867; pub mod rfc2868; +pub mod rfc2869; pub mod rfc3576; pub mod rfc4072; pub mod rfc5090; diff --git a/radius/src/packet.rs b/radius/src/packet.rs index eb51056..f3c73a4 100644 --- a/radius/src/packet.rs +++ b/radius/src/packet.rs @@ -231,6 +231,10 @@ impl Packet { self.attributes.add(avp); } + pub fn extend(&mut self, avps: Vec) { + self.attributes.extend(avps) + } + pub fn delete(&mut self, typ: AVPType) { self.attributes.del(typ); } diff --git a/radius/src/rfc2869.rs b/radius/src/rfc2869.rs new file mode 100644 index 0000000..c0a41db --- /dev/null +++ b/radius/src/rfc2869.rs @@ -0,0 +1,374 @@ +// Code generated by machine generator; DO NOT EDIT. + +use chrono::{DateTime, Utc}; + +use crate::avp::{AVPError, AVPType, AVP}; +use crate::packet::Packet; + +pub type ArapZoneAccess = u32; +pub const ARAP_ZONE_ACCESS_DEFAULT_ZONE: ArapZoneAccess = 1; +pub const ARAP_ZONE_ACCESS_ZONE_FILTER_INCLUSIVE: ArapZoneAccess = 2; +pub const ARAP_ZONE_ACCESS_ZONE_FILTER_EXCLUSIVE: ArapZoneAccess = 4; + +pub type Prompt = u32; +pub const PROMPT_NO_ECHO: Prompt = 0; +pub const PROMPT_ECHO: Prompt = 1; + +pub const ACCT_INPUT_GIGAWORDS_TYPE: AVPType = 52; +pub fn delete_acct_input_gigawords(packet: &mut Packet) { + packet.delete(ACCT_INPUT_GIGAWORDS_TYPE); +} +pub fn add_acct_input_gigawords(packet: &mut Packet, value: u32) { + packet.add(AVP::from_u32(ACCT_INPUT_GIGAWORDS_TYPE, value)); +} +pub fn lookup_acct_input_gigawords(packet: &Packet) -> Option> { + packet + .lookup(ACCT_INPUT_GIGAWORDS_TYPE) + .map(|v| v.encode_u32()) +} +pub fn lookup_all_acct_input_gigawords(packet: &Packet) -> Result, AVPError> { + let mut vec = Vec::new(); + for avp in packet.lookup_all(ACCT_INPUT_GIGAWORDS_TYPE) { + vec.push(avp.encode_u32()?) + } + Ok(vec) +} + +pub const ACCT_OUTPUT_GIGAWORDS_TYPE: AVPType = 53; +pub fn delete_acct_output_gigawords(packet: &mut Packet) { + packet.delete(ACCT_OUTPUT_GIGAWORDS_TYPE); +} +pub fn add_acct_output_gigawords(packet: &mut Packet, value: u32) { + packet.add(AVP::from_u32(ACCT_OUTPUT_GIGAWORDS_TYPE, value)); +} +pub fn lookup_acct_output_gigawords(packet: &Packet) -> Option> { + packet + .lookup(ACCT_OUTPUT_GIGAWORDS_TYPE) + .map(|v| v.encode_u32()) +} +pub fn lookup_all_acct_output_gigawords(packet: &Packet) -> Result, AVPError> { + let mut vec = Vec::new(); + for avp in packet.lookup_all(ACCT_OUTPUT_GIGAWORDS_TYPE) { + vec.push(avp.encode_u32()?) + } + Ok(vec) +} + +pub const EVENT_TIMESTAMP_TYPE: AVPType = 55; +pub fn delete_event_timestamp(packet: &mut Packet) { + packet.delete(EVENT_TIMESTAMP_TYPE); +} +pub fn add_event_timestamp(packet: &mut Packet, value: &DateTime) { + packet.add(AVP::from_date(EVENT_TIMESTAMP_TYPE, value)); +} +pub fn lookup_event_timestamp(packet: &Packet) -> Option, AVPError>> { + packet.lookup(EVENT_TIMESTAMP_TYPE).map(|v| v.encode_date()) +} +pub fn lookup_all_event_timestamp(packet: &Packet) -> Result>, AVPError> { + let mut vec = Vec::new(); + for avp in packet.lookup_all(EVENT_TIMESTAMP_TYPE) { + vec.push(avp.encode_date()?) + } + Ok(vec) +} + +pub const ARAP_PASSWORD_TYPE: AVPType = 70; +pub fn delete_arap_password(packet: &mut Packet) { + packet.delete(ARAP_PASSWORD_TYPE); +} +pub fn add_arap_password(packet: &mut Packet, value: &[u8]) -> Result<(), AVPError> { + if value.len() != 16 { + return Err(AVPError::InvalidAttributeLengthError(16)); + } + packet.add(AVP::from_bytes(ARAP_PASSWORD_TYPE, value)); + Ok(()) +} +pub fn lookup_arap_password(packet: &Packet) -> Option> { + packet.lookup(ARAP_PASSWORD_TYPE).map(|v| v.encode_bytes()) +} +pub fn lookup_all_arap_password(packet: &Packet) -> Vec> { + let mut vec = Vec::new(); + for avp in packet.lookup_all(ARAP_PASSWORD_TYPE) { + vec.push(avp.encode_bytes()) + } + vec +} + +pub const ARAP_FEATURES_TYPE: AVPType = 71; +pub fn delete_arap_features(packet: &mut Packet) { + packet.delete(ARAP_FEATURES_TYPE); +} +pub fn add_arap_features(packet: &mut Packet, value: &[u8]) -> Result<(), AVPError> { + if value.len() != 14 { + return Err(AVPError::InvalidAttributeLengthError(14)); + } + packet.add(AVP::from_bytes(ARAP_FEATURES_TYPE, value)); + Ok(()) +} +pub fn lookup_arap_features(packet: &Packet) -> Option> { + packet.lookup(ARAP_FEATURES_TYPE).map(|v| v.encode_bytes()) +} +pub fn lookup_all_arap_features(packet: &Packet) -> Vec> { + let mut vec = Vec::new(); + for avp in packet.lookup_all(ARAP_FEATURES_TYPE) { + vec.push(avp.encode_bytes()) + } + vec +} + +pub const ARAP_ZONE_ACCESS_TYPE: AVPType = 72; +pub fn delete_arap_zone_access(packet: &mut Packet) { + packet.delete(ARAP_ZONE_ACCESS_TYPE); +} +pub fn add_arap_zone_access(packet: &mut Packet, value: ArapZoneAccess) { + packet.add(AVP::from_u32(ARAP_ZONE_ACCESS_TYPE, value as u32)); +} +pub fn lookup_arap_zone_access(packet: &Packet) -> Option> { + packet + .lookup(ARAP_ZONE_ACCESS_TYPE) + .map(|v| Ok(v.encode_u32()? as ArapZoneAccess)) +} +pub fn lookup_all_arap_zone_access(packet: &Packet) -> Result, AVPError> { + let mut vec = Vec::new(); + for avp in packet.lookup_all(ARAP_ZONE_ACCESS_TYPE) { + vec.push(avp.encode_u32()? as ArapZoneAccess) + } + Ok(vec) +} + +pub const ARAP_SECURITY_TYPE: AVPType = 73; +pub fn delete_arap_security(packet: &mut Packet) { + packet.delete(ARAP_SECURITY_TYPE); +} +pub fn add_arap_security(packet: &mut Packet, value: u32) { + packet.add(AVP::from_u32(ARAP_SECURITY_TYPE, value)); +} +pub fn lookup_arap_security(packet: &Packet) -> Option> { + packet.lookup(ARAP_SECURITY_TYPE).map(|v| v.encode_u32()) +} +pub fn lookup_all_arap_security(packet: &Packet) -> Result, AVPError> { + let mut vec = Vec::new(); + for avp in packet.lookup_all(ARAP_SECURITY_TYPE) { + vec.push(avp.encode_u32()?) + } + Ok(vec) +} + +pub const ARAP_SECURITY_DATA_TYPE: AVPType = 74; +pub fn delete_arap_security_data(packet: &mut Packet) { + packet.delete(ARAP_SECURITY_DATA_TYPE); +} +pub fn add_arap_security_data(packet: &mut Packet, value: &str) { + packet.add(AVP::from_string(ARAP_SECURITY_DATA_TYPE, value)); +} +pub fn lookup_arap_security_data(packet: &Packet) -> Option> { + packet + .lookup(ARAP_SECURITY_DATA_TYPE) + .map(|v| v.encode_string()) +} +pub fn lookup_all_arap_security_data(packet: &Packet) -> Result, AVPError> { + let mut vec = Vec::new(); + for avp in packet.lookup_all(ARAP_SECURITY_DATA_TYPE) { + vec.push(avp.encode_string()?) + } + Ok(vec) +} + +pub const PASSWORD_RETRY_TYPE: AVPType = 75; +pub fn delete_password_retry(packet: &mut Packet) { + packet.delete(PASSWORD_RETRY_TYPE); +} +pub fn add_password_retry(packet: &mut Packet, value: u32) { + packet.add(AVP::from_u32(PASSWORD_RETRY_TYPE, value)); +} +pub fn lookup_password_retry(packet: &Packet) -> Option> { + packet.lookup(PASSWORD_RETRY_TYPE).map(|v| v.encode_u32()) +} +pub fn lookup_all_password_retry(packet: &Packet) -> Result, AVPError> { + let mut vec = Vec::new(); + for avp in packet.lookup_all(PASSWORD_RETRY_TYPE) { + vec.push(avp.encode_u32()?) + } + Ok(vec) +} + +pub const PROMPT_TYPE: AVPType = 76; +pub fn delete_prompt(packet: &mut Packet) { + packet.delete(PROMPT_TYPE); +} +pub fn add_prompt(packet: &mut Packet, value: Prompt) { + packet.add(AVP::from_u32(PROMPT_TYPE, value as u32)); +} +pub fn lookup_prompt(packet: &Packet) -> Option> { + packet + .lookup(PROMPT_TYPE) + .map(|v| Ok(v.encode_u32()? as Prompt)) +} +pub fn lookup_all_prompt(packet: &Packet) -> Result, AVPError> { + let mut vec = Vec::new(); + for avp in packet.lookup_all(PROMPT_TYPE) { + vec.push(avp.encode_u32()? as Prompt) + } + Ok(vec) +} + +pub const CONNECT_INFO_TYPE: AVPType = 77; +pub fn delete_connect_info(packet: &mut Packet) { + packet.delete(CONNECT_INFO_TYPE); +} +pub fn add_connect_info(packet: &mut Packet, value: &str) { + packet.add(AVP::from_string(CONNECT_INFO_TYPE, value)); +} +pub fn lookup_connect_info(packet: &Packet) -> Option> { + packet.lookup(CONNECT_INFO_TYPE).map(|v| v.encode_string()) +} +pub fn lookup_all_connect_info(packet: &Packet) -> Result, AVPError> { + let mut vec = Vec::new(); + for avp in packet.lookup_all(CONNECT_INFO_TYPE) { + vec.push(avp.encode_string()?) + } + Ok(vec) +} + +pub const CONFIGURATION_TOKEN_TYPE: AVPType = 78; +pub fn delete_configuration_token(packet: &mut Packet) { + packet.delete(CONFIGURATION_TOKEN_TYPE); +} +pub fn add_configuration_token(packet: &mut Packet, value: &str) { + packet.add(AVP::from_string(CONFIGURATION_TOKEN_TYPE, value)); +} +pub fn lookup_configuration_token(packet: &Packet) -> Option> { + packet + .lookup(CONFIGURATION_TOKEN_TYPE) + .map(|v| v.encode_string()) +} +pub fn lookup_all_configuration_token(packet: &Packet) -> Result, AVPError> { + let mut vec = Vec::new(); + for avp in packet.lookup_all(CONFIGURATION_TOKEN_TYPE) { + vec.push(avp.encode_string()?) + } + Ok(vec) +} + +pub const EAP_MESSAGE_TYPE: AVPType = 79; +pub fn delete_eap_message(packet: &mut Packet) { + packet.delete(EAP_MESSAGE_TYPE); +} +pub fn add_eap_message(packet: &mut Packet, value: &[u8]) { + packet.extend( + value + .chunks(253) + .map(|chunk| AVP::from_bytes(EAP_MESSAGE_TYPE, chunk)) + .collect(), + ); +} +pub fn lookup_eap_message(packet: &Packet) -> Option> { + let avps = packet.lookup_all(EAP_MESSAGE_TYPE); + match avps.is_empty() { + true => None, + false => Some(avps.into_iter().fold(Vec::new(), |mut acc, v| { + acc.extend(v.encode_bytes()); + acc + })), + } +} + +pub const MESSAGE_AUTHENTICATOR_TYPE: AVPType = 80; +pub fn delete_message_authenticator(packet: &mut Packet) { + packet.delete(MESSAGE_AUTHENTICATOR_TYPE); +} +pub fn add_message_authenticator(packet: &mut Packet, value: &[u8]) { + packet.add(AVP::from_bytes(MESSAGE_AUTHENTICATOR_TYPE, value)); +} +pub fn lookup_message_authenticator(packet: &Packet) -> Option> { + packet + .lookup(MESSAGE_AUTHENTICATOR_TYPE) + .map(|v| v.encode_bytes()) +} +pub fn lookup_all_message_authenticator(packet: &Packet) -> Vec> { + let mut vec = Vec::new(); + for avp in packet.lookup_all(MESSAGE_AUTHENTICATOR_TYPE) { + vec.push(avp.encode_bytes()) + } + vec +} + +pub const ARAP_CHALLENGE_RESPONSE_TYPE: AVPType = 84; +pub fn delete_arap_challenge_response(packet: &mut Packet) { + packet.delete(ARAP_CHALLENGE_RESPONSE_TYPE); +} +pub fn add_arap_challenge_response(packet: &mut Packet, value: &[u8]) -> Result<(), AVPError> { + if value.len() != 8 { + return Err(AVPError::InvalidAttributeLengthError(8)); + } + packet.add(AVP::from_bytes(ARAP_CHALLENGE_RESPONSE_TYPE, value)); + Ok(()) +} +pub fn lookup_arap_challenge_response(packet: &Packet) -> Option> { + packet + .lookup(ARAP_CHALLENGE_RESPONSE_TYPE) + .map(|v| v.encode_bytes()) +} +pub fn lookup_all_arap_challenge_response(packet: &Packet) -> Vec> { + let mut vec = Vec::new(); + for avp in packet.lookup_all(ARAP_CHALLENGE_RESPONSE_TYPE) { + vec.push(avp.encode_bytes()) + } + vec +} + +pub const ACCT_INTERIM_INTERVAL_TYPE: AVPType = 85; +pub fn delete_acct_interim_interval(packet: &mut Packet) { + packet.delete(ACCT_INTERIM_INTERVAL_TYPE); +} +pub fn add_acct_interim_interval(packet: &mut Packet, value: u32) { + packet.add(AVP::from_u32(ACCT_INTERIM_INTERVAL_TYPE, value)); +} +pub fn lookup_acct_interim_interval(packet: &Packet) -> Option> { + packet + .lookup(ACCT_INTERIM_INTERVAL_TYPE) + .map(|v| v.encode_u32()) +} +pub fn lookup_all_acct_interim_interval(packet: &Packet) -> Result, AVPError> { + let mut vec = Vec::new(); + for avp in packet.lookup_all(ACCT_INTERIM_INTERVAL_TYPE) { + vec.push(avp.encode_u32()?) + } + Ok(vec) +} + +pub const NAS_PORT_ID_TYPE: AVPType = 87; +pub fn delete_nas_port_id(packet: &mut Packet) { + packet.delete(NAS_PORT_ID_TYPE); +} +pub fn add_nas_port_id(packet: &mut Packet, value: &str) { + packet.add(AVP::from_string(NAS_PORT_ID_TYPE, value)); +} +pub fn lookup_nas_port_id(packet: &Packet) -> Option> { + packet.lookup(NAS_PORT_ID_TYPE).map(|v| v.encode_string()) +} +pub fn lookup_all_nas_port_id(packet: &Packet) -> Result, AVPError> { + let mut vec = Vec::new(); + for avp in packet.lookup_all(NAS_PORT_ID_TYPE) { + vec.push(avp.encode_string()?) + } + Ok(vec) +} + +pub const FRAMED_POOL_TYPE: AVPType = 88; +pub fn delete_framed_pool(packet: &mut Packet) { + packet.delete(FRAMED_POOL_TYPE); +} +pub fn add_framed_pool(packet: &mut Packet, value: &str) { + packet.add(AVP::from_string(FRAMED_POOL_TYPE, value)); +} +pub fn lookup_framed_pool(packet: &Packet) -> Option> { + packet.lookup(FRAMED_POOL_TYPE).map(|v| v.encode_string()) +} +pub fn lookup_all_framed_pool(packet: &Packet) -> Result, AVPError> { + let mut vec = Vec::new(); + for avp in packet.lookup_all(FRAMED_POOL_TYPE) { + vec.push(avp.encode_string()?) + } + Ok(vec) +}