1
0
Fork 0

Adding upstream version 0.5.4.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-05-06 06:52:03 +02:00
parent 6dc19540ee
commit 19551ac12c
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
23 changed files with 6571 additions and 0 deletions

130
build/define_parser.rs Normal file
View file

@ -0,0 +1,130 @@
use std::collections::HashMap;
use std::io::BufRead;
struct Scope {
name: String,
tokens: Vec<String>,
}
pub fn parse_defines(reader: impl BufRead) -> anyhow::Result<HashMap<String, String>> {
let mut defines = HashMap::new();
// iterate over each line in the reader
let mut scope: Option<Scope> = None;
for line in reader.lines() {
let line = line?;
// check if the line is a define
if line.trim().starts_with("#define") {
if let Some(prev_scope) = scope.take() {
// if we have a previous scope, store it
defines.insert(prev_scope.name, prev_scope.tokens.join(" "));
}
// start a new scope
let mut tokens = line.split_whitespace();
let name = tokens
.nth(1)
.ok_or_else(|| anyhow::anyhow!("Expected a name after #define"))?
.to_string();
let mut tokens = tokens.collect::<Vec<_>>();
let mut single_line = true;
// if last token is a \; remove it
if let Some(last) = tokens.last() {
if *last == "\\" {
tokens.pop();
single_line = false;
}
}
// get tokens after the name
let mut parsed_tokens: Vec<String> = vec![];
for token in tokens {
let parsed = parse_token(&defines, token, false)?;
parsed_tokens.extend(parsed);
}
scope = Some(Scope {
name,
tokens: parsed_tokens,
});
// if is single line, push to defines and set scope to None
if single_line {
if let Some(scope) = scope.take() {
defines.insert(scope.name, scope.tokens.join(" "));
}
}
} else {
// if we are in a scope, add the line to the tokens
let Some(inner_scope) = scope.as_mut() else {
continue;
};
let tokens = line.split_whitespace();
let mut tokens: Vec<String> = tokens.map(|s| s.to_string()).collect();
// check if it ends with a \, if so, remove it
let mut last_line = true;
if let Some(last) = tokens.last() {
if last == "\\" {
tokens.pop();
last_line = false;
}
}
// parse tokens
for token in tokens {
let parsed = parse_token(&defines, &token, false)?;
inner_scope.tokens.extend(parsed);
}
// if last line, push to defines and set scope to None
if last_line {
if let Some(scope) = scope.take() {
defines.insert(scope.name, scope.tokens.join(" "));
}
}
}
}
// put last scope
if let Some(scope) = scope {
defines.insert(scope.name, scope.tokens.join(" "));
}
Ok(defines)
}
/// Parse token
fn parse_token(
defines: &HashMap<String, String>,
token: &str,
nested: bool,
) -> anyhow::Result<Vec<String>> {
let token = token.trim().trim_end_matches(',');
// if token is a define, parse it
if let Some(value) = defines.get(token) {
return parse_token(defines, value, true);
}
// otherwise, check if it is a string
if token.starts_with('"') && token.ends_with('"') {
return Ok(vec![
token[1..token.len() - 1].trim_end_matches(',').to_string(),
]);
}
// check if it is a number
if token.parse::<i64>().is_ok() {
return Ok(vec![token.to_string()]);
}
if nested {
return Ok(vec![token.to_string()]);
}
anyhow::bail!("Unknown token: {token}; defines: {defines:#?}",)
}

15
build/main.rs Normal file
View file

@ -0,0 +1,15 @@
mod define_parser;
mod openssh;
mod src_writer;
fn main() -> anyhow::Result<()> {
// If reload SSH ALGO is not set, we don't need to do anything
if std::env::var("RELOAD_SSH_ALGO").is_err() {
return Ok(());
}
let prefs = openssh::get_my_prefs()?;
src_writer::write_source(prefs)?;
Ok(())
}

88
build/openssh.rs Normal file
View file

@ -0,0 +1,88 @@
use std::path::{Path, PathBuf};
use crate::define_parser::parse_defines;
const OPENSSH_TAG: &str = "V_9_9_P2";
/// Default algorithms for ssh.
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct MyPrefs {
pub ca_signature_algorithms: Vec<String>,
pub ciphers: Vec<String>,
pub host_key_algorithms: Vec<String>,
pub kex_algorithms: Vec<String>,
pub mac: Vec<String>,
pub pubkey_accepted_algorithms: Vec<String>,
}
pub fn get_my_prefs() -> anyhow::Result<MyPrefs> {
let out_dir = std::env::var_os("OUT_DIR")
.map(|s| PathBuf::from(s).join("openssh"))
.ok_or_else(|| anyhow::anyhow!("OUT_DIR not set"))?;
let build_dir = out_dir.join("build");
let inner_dir = build_dir.join("src");
std::fs::remove_dir_all(&build_dir).ok();
std::fs::create_dir_all(&inner_dir).ok();
clone_openssh(&inner_dir)?;
let my_proposal_path = inner_dir.join("myproposal.h");
let reader = std::io::BufReader::new(std::fs::File::open(my_proposal_path)?);
let defines = parse_defines(reader)?;
let ca_signature_algorithms = defines
.get("SSH_ALLOWED_CA_SIGALGS")
.map(|s| s.split_whitespace().map(|s| format!(r#""{s}""#)).collect())
.unwrap_or_default();
let ciphers = defines
.get("KEX_CLIENT_ENCRYPT")
.map(|s| s.split_whitespace().map(|s| format!(r#""{s}""#)).collect())
.unwrap_or_default();
let host_key_algorithms = defines
.get("KEX_DEFAULT_PK_ALG")
.map(|s| s.split_whitespace().map(|s| format!(r#""{s}""#)).collect())
.unwrap_or_default();
let kex_algorithms = defines
.get("KEX_CLIENT")
.map(|s| s.split_whitespace().map(|s| format!(r#""{s}""#)).collect())
.unwrap_or_default();
let mac = defines
.get("KEX_CLIENT_MAC")
.map(|s| s.split_whitespace().map(|s| format!(r#""{s}""#)).collect())
.unwrap_or_default();
let pubkey_accepted_algorithms = defines
.get("KEX_DEFAULT_PK_ALG")
.map(|s| s.split_whitespace().map(|s| format!(r#""{s}""#)).collect())
.unwrap_or_default();
Ok(MyPrefs {
ca_signature_algorithms,
ciphers,
host_key_algorithms,
kex_algorithms,
mac,
pubkey_accepted_algorithms,
})
}
fn clone_openssh(path: &Path) -> anyhow::Result<()> {
let repo_url = "https://github.com/openssh/openssh-portable.git";
let repo = git2::Repository::clone(repo_url, path)?;
let obj = repo.revparse_single(OPENSSH_TAG)?;
let commit = obj.peel_to_commit()?;
repo.checkout_tree(&obj, None)?;
repo.set_head_detached(commit.id())?;
Ok(())
}

70
build/src_writer.rs Normal file
View file

@ -0,0 +1,70 @@
use std::io::Write as _;
use std::path::PathBuf;
use crate::openssh::MyPrefs;
pub fn write_source(prefs: MyPrefs) -> anyhow::Result<()> {
let SrcPaths { src_dir, src_path } = src_path();
// create dir
if !src_dir.exists() {
std::fs::create_dir_all(&src_dir)?;
}
// open file
let mut file = std::fs::File::create(src_path)?;
writeln!(
file,
r#"//! This file is autogenerated at build-time when `RELOAD_SSH_ALGO` is set to environment."#
)?;
writeln!(file)?;
writeln!(file, "use crate::DefaultAlgorithms;")?;
writeln!(file,)?;
writeln!(file, r#"/// Default algorithms for ssh."#)?;
writeln!(file, r#"pub fn defaults() -> DefaultAlgorithms {{"#)?;
writeln!(file, r#" DefaultAlgorithms {{"#)?;
write_vec(
&mut file,
"ca_signature_algorithms",
&prefs.ca_signature_algorithms,
)?;
write_vec(&mut file, "ciphers", &prefs.ciphers)?;
write_vec(&mut file, "host_key_algorithms", &prefs.host_key_algorithms)?;
write_vec(&mut file, "kex_algorithms", &prefs.kex_algorithms)?;
write_vec(&mut file, "mac", &prefs.mac)?;
write_vec(
&mut file,
"pubkey_accepted_algorithms",
&prefs.pubkey_accepted_algorithms,
)?;
writeln!(file, r#" }}"#)?;
writeln!(file, r#"}}"#)?;
Ok(())
}
fn write_vec(file: &mut std::fs::File, name: &str, vec: &[String]) -> anyhow::Result<()> {
writeln!(file, r#" {name}: vec!["#)?;
for item in vec {
writeln!(file, r#" {item}.to_string(),"#,)?;
}
writeln!(file, r#" ],"#)?;
Ok(())
}
struct SrcPaths {
src_dir: PathBuf,
src_path: PathBuf,
}
fn src_path() -> SrcPaths {
let src_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("src")
.join("default_algorithms");
let src_path = src_dir.join("openssh.rs");
SrcPaths { src_dir, src_path }
}