use std::sync::atomic::{AtomicU32, Ordering};
use onc_rpc_client::rpc::opaque_auth;
pub(crate) use onc_rpc_client::auth::{AuthFlavor, AuthSys};
const STAMP_START: u32 = 42;
static STAMP_COUNTER: AtomicU32 = AtomicU32::new(STAMP_START);
#[derive(Debug, Clone)]
pub(crate) enum Credential {
None,
Sys(AuthSys),
}
impl Credential {
pub(crate) fn to_opaque_auth(&self) -> opaque_auth<'static> {
match self {
Self::None => opaque_auth::default(),
Self::Sys(auth) => auth.to_opaque_auth(next_stamp()),
}
}
}
pub(crate) fn flavor_name(flavor: u32) -> String {
match flavor {
0 => "AUTH_NONE".to_owned(),
1 => "AUTH_SYS".to_owned(),
2 => "AUTH_SHORT".to_owned(),
3 => "AUTH_DH".to_owned(),
4 => "AUTH_KERB".to_owned(),
5 => "AUTH_RSA".to_owned(),
6 => "RPCSEC_GSS".to_owned(),
7 => "AUTH_TLS".to_owned(),
30_001 => "AUTH_NW".to_owned(),
200_000 => "AUTH_SEC".to_owned(),
200_004 => "AUTH_ESV".to_owned(),
300_000 => "AUTH_NQNFS".to_owned(),
300_001 => "AUTH_GSSAPI".to_owned(),
300_002 => "AUTH_ILU_UGEN".to_owned(),
390_000 => "RPCSEC_GSS(SPNEGO)".to_owned(),
390_003 => "RPCSEC_GSS(krb5)".to_owned(),
390_004 => "RPCSEC_GSS(krb5i)".to_owned(),
390_005 => "RPCSEC_GSS(krb5p)".to_owned(),
_ => format!("flavor({flavor})"),
}
}
pub(crate) fn next_stamp() -> u32 {
loop {
let cur = STAMP_COUNTER.load(Ordering::Relaxed);
let next = if cur == u32::MAX { STAMP_START } else { cur.wrapping_add(1) };
if STAMP_COUNTER.compare_exchange_weak(cur, next, Ordering::Relaxed, Ordering::Relaxed).is_ok() {
return cur;
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn stamps_are_unique_across_consecutive_encodes() {
let a = next_stamp();
let b = next_stamp();
assert_ne!(a, b, "a repeated stamp lets a server serve a cached reply to a different identity");
}
#[test]
fn stamp_never_returns_below_the_floor() {
for _ in 0..64 {
assert!(next_stamp() >= STAMP_START);
}
}
#[test]
fn credential_none_encodes_as_auth_none() {
use onc_rpc_client::rpc::auth_flavor;
assert_eq!(Credential::None.to_opaque_auth().flavor, auth_flavor::AUTH_NULL);
}
#[test]
fn credential_sys_encodes_as_auth_unix() {
use onc_rpc_client::rpc::auth_flavor;
let cred = Credential::Sys(AuthSys::new(1000, 1000, "host"));
assert_eq!(cred.to_opaque_auth().flavor, auth_flavor::AUTH_UNIX);
}
}