use super::security_layer::{SecurityLayer, decode_choice, encode_offer};
use super::{AcceptStep, GssAcceptor, GssError};
#[derive(Debug)]
pub enum ServerStep {
Challenge(Vec<u8>),
Done { principal: String },
}
#[derive(Debug, thiserror::Error)]
pub enum ServerExchangeError {
#[error(transparent)]
Gss(#[from] GssError),
#[error(transparent)]
Layer(#[from] super::security_layer::LayerError),
#[error("unexpected token in state {0}")]
State(&'static str),
}
#[derive(Debug)]
enum State {
AcceptingContext,
OfferingLayer, AwaitingChoice,
Done,
}
pub struct GssapiServerExchange {
acceptor: Box<dyn GssAcceptor>,
state: State,
max_recv_size: u32,
}
impl std::fmt::Debug for GssapiServerExchange {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("GssapiServerExchange")
.field("state", &self.state)
.field("max_recv_size", &self.max_recv_size)
.finish_non_exhaustive()
}
}
impl GssapiServerExchange {
#[must_use]
pub fn new(acceptor: Box<dyn GssAcceptor>, max_recv_size: u32) -> Self {
Self {
acceptor,
state: State::AcceptingContext,
max_recv_size,
}
}
pub fn step(&mut self, client_token: &[u8]) -> Result<ServerStep, ServerExchangeError> {
match self.state {
State::AcceptingContext => match self.acceptor.accept(client_token)? {
AcceptStep::Continue(t) => Ok(ServerStep::Challenge(t)),
AcceptStep::Established(t) => {
if let Some(token) = t {
self.state = State::OfferingLayer;
Ok(ServerStep::Challenge(token))
} else {
self.state = State::AwaitingChoice;
let offer = encode_offer(SecurityLayer::AUTH, self.max_recv_size);
Ok(ServerStep::Challenge(self.acceptor.wrap(&offer, false)?))
}
}
},
State::OfferingLayer => {
self.state = State::AwaitingChoice;
let offer = encode_offer(SecurityLayer::AUTH, self.max_recv_size);
Ok(ServerStep::Challenge(self.acceptor.wrap(&offer, false)?))
}
State::AwaitingChoice => {
let plaintext = self.acceptor.unwrap(client_token)?;
let _choice = decode_choice(&plaintext)?; let principal = self.acceptor.src_principal()?;
self.state = State::Done;
Ok(ServerStep::Done { principal })
}
State::Done => Err(ServerExchangeError::State("Done")),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::gssapi::{AcceptStep, GssAcceptor, GssError};
use assert2::assert;
struct FakeAcceptor {
established: bool,
}
impl GssAcceptor for FakeAcceptor {
fn accept(&mut self, _t: &[u8]) -> Result<AcceptStep, GssError> {
self.established = true;
Ok(AcceptStep::Established(Some(b"AP-REP".to_vec())))
}
fn wrap(&self, p: &[u8], _c: bool) -> Result<Vec<u8>, GssError> {
Ok(p.to_vec())
}
fn unwrap(&self, t: &[u8]) -> Result<Vec<u8>, GssError> {
Ok(t.to_vec())
}
fn src_principal(&self) -> Result<String, GssError> {
Ok("alice@REALM".into())
}
}
#[test]
fn establishes_then_offers_layer_then_completes() {
let mut ex =
GssapiServerExchange::new(Box::new(FakeAcceptor { established: false }), 0x1_0000);
let r1 = ex.step(b"AP-REQ").unwrap();
assert!(matches!(r1, ServerStep::Challenge(_)));
let r2 = ex.step(b"").unwrap();
let offer = match r2 {
ServerStep::Challenge(t) => t,
ServerStep::Done { .. } => panic!("expected offer"),
};
assert!(offer[0] == 0x01);
let mut choice = vec![0x01u8, 0x00, 0x10, 0x00];
choice.extend_from_slice(b"alice");
let r3 = ex.step(&choice).unwrap();
match r3 {
ServerStep::Done { principal } => assert!(principal == "alice@REALM"),
ServerStep::Challenge(_) => panic!("expected Done"),
}
}
#[test]
fn rejects_non_auth_layer_choice() {
let mut ex =
GssapiServerExchange::new(Box::new(FakeAcceptor { established: false }), 0x1_0000);
ex.step(b"AP-REQ").unwrap();
ex.step(b"").unwrap();
let bad = vec![0x04u8, 0x00, 0x10, 0x00]; assert!(ex.step(&bad).is_err());
}
}