Adding upstream version 0.5.4.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
6dc19540ee
commit
19551ac12c
23 changed files with 6571 additions and 0 deletions
130
build/define_parser.rs
Normal file
130
build/define_parser.rs
Normal 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
15
build/main.rs
Normal 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
88
build/openssh.rs
Normal 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
70
build/src_writer.rs
Normal 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 }
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue