replace and remove old ticket functions
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
@ -7,7 +7,7 @@ use std::marker::PhantomData;
|
||||
use anyhow::{bail, format_err, Error};
|
||||
use base64;
|
||||
|
||||
use openssl::pkey::{PKey, Public, Private, HasPublic};
|
||||
use openssl::pkey::{PKey, Private, HasPublic};
|
||||
use openssl::sign::{Signer, Verifier};
|
||||
use openssl::hash::MessageDigest;
|
||||
use percent_encoding::{AsciiSet, percent_decode_str, percent_encode};
|
||||
@ -266,7 +266,7 @@ pub fn term_aad(userid: &Userid, path: &str, port: u16) -> String {
|
||||
mod test {
|
||||
use openssl::pkey::{PKey, Private};
|
||||
|
||||
use super::{Ticket, TICKET_LIFETIME};
|
||||
use super::Ticket;
|
||||
use crate::api2::types::Userid;
|
||||
use crate::tools::epoch_now_u64;
|
||||
|
||||
@ -288,13 +288,6 @@ mod test {
|
||||
.expect("failed to verify test ticket");
|
||||
|
||||
assert_eq!(*userid, check);
|
||||
|
||||
// Compat check:
|
||||
let (_age, uid) =
|
||||
super::verify_rsa_ticket(key, "PREFIX", &ticket, aad, -300, TICKET_LIFETIME)
|
||||
.expect("failed compatibility verification");
|
||||
let uid = uid.expect("compat did not return a userid");
|
||||
assert_eq!(*userid, uid);
|
||||
} else {
|
||||
parsed
|
||||
.verify(key, "PREFIX", aad)
|
||||
@ -320,151 +313,5 @@ mod test {
|
||||
t.change_time(epoch_now_u64().unwrap() as i64 + 0x1000_0000);
|
||||
false
|
||||
});
|
||||
|
||||
// compat check:
|
||||
let ticket =
|
||||
super::assemble_rsa_ticket(&key, "PREFIX", Some(Userid::root_userid()), Some("stuff"))
|
||||
.expect("failed to assemble compatibility ticket");
|
||||
let parsed_uid: Userid = Ticket::parse(&ticket)
|
||||
.expect("failed to parse compatibility ticket")
|
||||
.verify(&key, Some("stuff"), -300..TICKET_LIFETIME)
|
||||
.expect("failed to verify compatibility ticket");
|
||||
assert_eq!(parsed_uid, *Userid::root_userid());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn assemble_term_ticket(
|
||||
keypair: &PKey<Private>,
|
||||
userid: &Userid,
|
||||
path: &str,
|
||||
port: u16,
|
||||
) -> Result<String, Error> {
|
||||
assemble_rsa_ticket(
|
||||
keypair,
|
||||
TERM_PREFIX,
|
||||
None,
|
||||
Some(&format!("{}{}{}", userid, path, port)),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn verify_term_ticket(
|
||||
keypair: &PKey<Public>,
|
||||
userid: &Userid,
|
||||
path: &str,
|
||||
port: u16,
|
||||
ticket: &str,
|
||||
) -> Result<(i64, Option<Userid>), Error> {
|
||||
verify_rsa_ticket(
|
||||
keypair,
|
||||
TERM_PREFIX,
|
||||
ticket,
|
||||
Some(&format!("{}{}{}", userid, path, port)),
|
||||
-300,
|
||||
TICKET_LIFETIME,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn assemble_rsa_ticket(
|
||||
keypair: &PKey<Private>,
|
||||
prefix: &str,
|
||||
data: Option<&Userid>,
|
||||
secret_data: Option<&str>,
|
||||
) -> Result<String, Error> {
|
||||
|
||||
let epoch = epoch_now_u64()?;
|
||||
|
||||
let timestamp = format!("{:08X}", epoch);
|
||||
|
||||
let mut plain = prefix.to_owned();
|
||||
plain.push(':');
|
||||
|
||||
if let Some(data) = data {
|
||||
use std::fmt::Write;
|
||||
write!(plain, "{}", data)?;
|
||||
plain.push(':');
|
||||
}
|
||||
|
||||
plain.push_str(×tamp);
|
||||
|
||||
let mut full = plain.clone();
|
||||
if let Some(secret) = secret_data {
|
||||
full.push(':');
|
||||
full.push_str(secret);
|
||||
}
|
||||
|
||||
let mut signer = Signer::new(MessageDigest::sha256(), &keypair)?;
|
||||
signer.update(full.as_bytes())?;
|
||||
let sign = signer.sign_to_vec()?;
|
||||
|
||||
let sign_b64 = base64::encode_config(&sign, base64::STANDARD_NO_PAD);
|
||||
|
||||
Ok(format!("{}::{}", plain, sign_b64))
|
||||
}
|
||||
|
||||
pub fn verify_rsa_ticket<P: HasPublic>(
|
||||
keypair: &PKey<P>,
|
||||
prefix: &str,
|
||||
ticket: &str,
|
||||
secret_data: Option<&str>,
|
||||
min_age: i64,
|
||||
max_age: i64,
|
||||
) -> Result<(i64, Option<Userid>), Error> {
|
||||
|
||||
use std::collections::VecDeque;
|
||||
|
||||
let mut parts: VecDeque<&str> = ticket.split(':').collect();
|
||||
|
||||
match parts.pop_front() {
|
||||
Some(text) => if text != prefix { bail!("ticket with invalid prefix"); }
|
||||
None => bail!("ticket without prefix"),
|
||||
}
|
||||
|
||||
let sign_b64 = match parts.pop_back() {
|
||||
Some(v) => v,
|
||||
None => bail!("ticket without signature"),
|
||||
};
|
||||
|
||||
match parts.pop_back() {
|
||||
Some(text) => if text != "" { bail!("ticket with invalid signature separator"); }
|
||||
None => bail!("ticket without signature separator"),
|
||||
}
|
||||
|
||||
let mut data = None;
|
||||
|
||||
let mut full = match parts.len() {
|
||||
2 => {
|
||||
data = Some(parts[0].to_owned());
|
||||
format!("{}:{}:{}", prefix, parts[0], parts[1])
|
||||
}
|
||||
1 => format!("{}:{}", prefix, parts[0]),
|
||||
_ => bail!("ticket with invalid number of components"),
|
||||
};
|
||||
|
||||
if let Some(secret) = secret_data {
|
||||
full.push(':');
|
||||
full.push_str(secret);
|
||||
}
|
||||
|
||||
let sign = base64::decode_config(sign_b64, base64::STANDARD_NO_PAD)?;
|
||||
|
||||
let mut verifier = Verifier::new(MessageDigest::sha256(), &keypair)?;
|
||||
verifier.update(full.as_bytes())?;
|
||||
|
||||
if !verifier.verify(&sign)? {
|
||||
bail!("ticket with invalid signature");
|
||||
}
|
||||
|
||||
let timestamp = i64::from_str_radix(parts.pop_back().unwrap(), 16)?;
|
||||
let now = epoch_now_u64()? as i64;
|
||||
|
||||
let age = now - timestamp;
|
||||
if age < min_age {
|
||||
bail!("invalid ticket - timestamp newer than expected.");
|
||||
}
|
||||
|
||||
if age > max_age {
|
||||
bail!("invalid ticket - timestamp too old.");
|
||||
}
|
||||
|
||||
Ok((age, data.map(|s| s.parse()).transpose()?))
|
||||
}
|
||||
|
Reference in New Issue
Block a user