AuthOptions parsing
This commit is contained in:
parent
c1bab5808b
commit
0adb92e8ff
1 changed files with 75 additions and 15 deletions
90
src/auth.rs
90
src/auth.rs
|
@ -177,7 +177,7 @@ enum AuthOption {
|
||||||
/// Note: we do not unescape the tabulation, and thus we don't parse the data
|
/// Note: we do not unescape the tabulation, and thus we don't parse the data
|
||||||
ForwardViews(Vec<u8>),
|
ForwardViews(Vec<u8>),
|
||||||
/// Remote user has secured transport to auth client (e.g. localhost, SSL, TLS).
|
/// Remote user has secured transport to auth client (e.g. localhost, SSL, TLS).
|
||||||
Secured(String),
|
Secured(Option<String>),
|
||||||
/// The value can be “insecure”, “trusted” or “TLS”.
|
/// The value can be “insecure”, “trusted” or “TLS”.
|
||||||
Transport(String),
|
Transport(String),
|
||||||
/// TLS cipher being used.
|
/// TLS cipher being used.
|
||||||
|
@ -197,6 +197,9 @@ enum AuthOption {
|
||||||
CertUsername,
|
CertUsername,
|
||||||
/// IMAP ID string
|
/// IMAP ID string
|
||||||
ClientId,
|
ClientId,
|
||||||
|
/// An unknown key
|
||||||
|
UnknownPair(String, Vec<u8>),
|
||||||
|
UnknownBool(Vec<u8>),
|
||||||
/// Initial response for authentication mechanism.
|
/// Initial response for authentication mechanism.
|
||||||
/// NOTE: This must be the last parameter. Everything after it is ignored.
|
/// NOTE: This must be the last parameter. Everything after it is ignored.
|
||||||
/// This is to avoid accidental security holes if user-given data is directly put to base64 string without filtering out tabs.
|
/// This is to avoid accidental security holes if user-given data is directly put to base64 string without filtering out tabs.
|
||||||
|
@ -313,7 +316,7 @@ use nom::{
|
||||||
IResult,
|
IResult,
|
||||||
branch::alt,
|
branch::alt,
|
||||||
error::{ErrorKind, Error},
|
error::{ErrorKind, Error},
|
||||||
character::complete::{tab, u64},
|
character::complete::{tab, u64, u16},
|
||||||
bytes::complete::{tag, tag_no_case, take, take_while, take_while1},
|
bytes::complete::{tag, tag_no_case, take, take_while, take_while1},
|
||||||
multi::{many1, separated_list0},
|
multi::{many1, separated_list0},
|
||||||
combinator::{map, opt, recognize, value,},
|
combinator::{map, opt, recognize, value,},
|
||||||
|
@ -363,22 +366,68 @@ fn parameter<'a>(input: &'a [u8]) -> IResult<&'a [u8], &[u8]> {
|
||||||
))))(input)
|
))))(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn service<'a>(input: &'a [u8]) -> IResult<&'a [u8], String> {
|
fn parameter_str(input: &[u8]) -> IResult<&[u8], String> {
|
||||||
let (input, buf) = preceded(
|
let (input, buf) = parameter(input)?;
|
||||||
tag_no_case("service="),
|
|
||||||
parameter
|
|
||||||
)(input)?;
|
|
||||||
|
|
||||||
std::str::from_utf8(buf)
|
std::str::from_utf8(buf)
|
||||||
.map(|v| (input, v.to_string()))
|
.map(|v| (input, v.to_string()))
|
||||||
.map_err(|_| nom::Err::Failure(Error::new(input, ErrorKind::TakeWhile1)))
|
.map_err(|_| nom::Err::Failure(Error::new(input, ErrorKind::TakeWhile1)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_param_name_char(c: u8) -> bool {
|
||||||
|
is_not_tab_or_esc_or_lf(c) && c != 0x3d // =
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parameter_name(input: &[u8]) -> IResult<&[u8], String> {
|
||||||
|
let (input, buf) = take_while1(is_param_name_char)(input)?;
|
||||||
|
|
||||||
|
std::str::from_utf8(buf)
|
||||||
|
.map(|v| (input, v.to_string()))
|
||||||
|
.map_err(|_| nom::Err::Failure(Error::new(input, ErrorKind::TakeWhile1)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn service<'a>(input: &'a [u8]) -> IResult<&'a [u8], String> {
|
||||||
|
preceded(
|
||||||
|
tag_no_case("service="),
|
||||||
|
parameter_str
|
||||||
|
)(input)
|
||||||
|
}
|
||||||
|
|
||||||
fn auth_option<'a>(input: &'a [u8]) -> IResult<&'a [u8], AuthOption> {
|
fn auth_option<'a>(input: &'a [u8]) -> IResult<&'a [u8], AuthOption> {
|
||||||
|
use AuthOption::*;
|
||||||
alt((
|
alt((
|
||||||
value(AuthOption::Debug, tag_no_case(b"debug")),
|
alt((
|
||||||
value(AuthOption::NoPenalty, tag_no_case(b"no-penalty")),
|
value(Debug, tag_no_case(b"debug")),
|
||||||
value(AuthOption::CertUsername, tag_no_case(b"cert_username")),
|
value(NoPenalty, tag_no_case(b"no-penalty")),
|
||||||
|
value(ClientId, tag_no_case(b"client_id")),
|
||||||
|
map(preceded(tag_no_case(b"session="), u64), |id| Session(id)),
|
||||||
|
map(preceded(tag_no_case(b"lip="), parameter_str), |ip| LocalIp(ip)),
|
||||||
|
map(preceded(tag_no_case(b"rip="), parameter_str), |ip| RemoteIp(ip)),
|
||||||
|
map(preceded(tag_no_case(b"lport="), u16), |port| LocalPort(port)),
|
||||||
|
map(preceded(tag_no_case(b"rport="), u16), |port| RemotePort(port)),
|
||||||
|
map(preceded(tag_no_case(b"real_rip="), parameter_str), |ip| RealRemoteIp(ip)),
|
||||||
|
map(preceded(tag_no_case(b"real_lip="), parameter_str), |ip| RealLocalIp(ip)),
|
||||||
|
map(preceded(tag_no_case(b"real_lport="), u16), |port| RealLocalPort(port)),
|
||||||
|
map(preceded(tag_no_case(b"real_rport="), u16), |port| RealRemotePort(port)),
|
||||||
|
)),
|
||||||
|
alt((
|
||||||
|
map(preceded(tag_no_case(b"local_name="), parameter_str), |name| LocalName(name)),
|
||||||
|
map(preceded(tag_no_case(b"forward_views="), parameter), |views| ForwardViews(views.into())),
|
||||||
|
map(preceded(tag_no_case(b"secured="), parameter_str), |info| Secured(Some(info))),
|
||||||
|
value(Secured(None), tag_no_case(b"secured")),
|
||||||
|
value(CertUsername, tag_no_case(b"cert_username")),
|
||||||
|
map(preceded(tag_no_case(b"transport="), parameter_str), |ts| Transport(ts)),
|
||||||
|
map(preceded(tag_no_case(b"tls_cipher="), parameter_str), |cipher| TlsCipher(cipher)),
|
||||||
|
map(preceded(tag_no_case(b"tls_cipher_bits="), parameter_str), |bits| TlsCipherBits(bits)),
|
||||||
|
map(preceded(tag_no_case(b"tls_pfs="), parameter_str), |pfs| TlsPfs(pfs)),
|
||||||
|
map(preceded(tag_no_case(b"tls_protocol="), parameter_str), |proto| TlsProtocol(proto)),
|
||||||
|
map(preceded(tag_no_case(b"valid-client-cert="), parameter_str), |cert| ValidClientCert(cert)),
|
||||||
|
)),
|
||||||
|
alt((
|
||||||
|
map(preceded(tag_no_case(b"resp="), base64), |data| Resp(data)),
|
||||||
|
map(tuple((parameter_name, tag(b"="), parameter)), |(n, _, v)| UnknownPair(n, v.into())),
|
||||||
|
map(parameter, |v| UnknownBool(v.into())),
|
||||||
|
)),
|
||||||
))(input)
|
))(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -409,7 +458,20 @@ fn is_base64_core(c: u8) -> bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_base64_pad(c: u8) -> bool {
|
fn is_base64_pad(c: u8) -> bool {
|
||||||
c == 0x3d
|
c == 0x3d // =
|
||||||
|
}
|
||||||
|
|
||||||
|
fn base64(input: &[u8]) -> IResult<&[u8], Vec<u8>> {
|
||||||
|
let (input, (b64, _)) = tuple((
|
||||||
|
take_while1(is_base64_core),
|
||||||
|
take_while(is_base64_pad),
|
||||||
|
))(input)?;
|
||||||
|
|
||||||
|
let data = base64::engine::general_purpose::STANDARD_NO_PAD
|
||||||
|
.decode(b64)
|
||||||
|
.map_err(|_| nom::Err::Failure(Error::new(input, ErrorKind::TakeWhile1)))?;
|
||||||
|
|
||||||
|
Ok((input, data))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @FIXME Dovecot does not say if base64 content must be padded or not
|
/// @FIXME Dovecot does not say if base64 content must be padded or not
|
||||||
|
@ -419,12 +481,10 @@ fn cont_command<'a>(input: &'a [u8]) -> IResult<&'a [u8], ClientCommand> {
|
||||||
tab,
|
tab,
|
||||||
u64,
|
u64,
|
||||||
tab,
|
tab,
|
||||||
take_while1(is_base64_core),
|
base64
|
||||||
take_while(is_base64_pad),
|
|
||||||
));
|
));
|
||||||
|
|
||||||
let (input, (_, _, id, _, b64, _)) = parser(input)?;
|
let (input, (_, _, id, _, data)) = parser(input)?;
|
||||||
let data = base64::engine::general_purpose::STANDARD_NO_PAD.decode(b64).map_err(|_| nom::Err::Failure(Error::new(input, ErrorKind::TakeWhile1)))?;
|
|
||||||
Ok((input, ClientCommand::Cont { id, data }))
|
Ok((input, ClientCommand::Cont { id, data }))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue