From 6aa131a7378c1776ba6e854601c0c8fa1ce0031dd16237153f6c303a68c0fd33 Mon Sep 17 00:00:00 2001 From: Sergey Matveev Date: Tue, 23 Dec 2025 14:30:25 +0300 Subject: [PATCH] Configuration checksumming --- CHECKSUM | 14 +++++ dsc | 102 ++++++++++++++++++++++++++++++++++--- t/bin.t | 2 +- t/csum.t | 40 +++++++++++++++ t/export.t | 5 +- t/import-path-validation.t | 8 +-- t/setup.rc | 7 +++ 7 files changed, 164 insertions(+), 14 deletions(-) create mode 100644 CHECKSUM create mode 100755 t/csum.t diff --git a/CHECKSUM b/CHECKSUM new file mode 100644 index 0000000..55a57da --- /dev/null +++ b/CHECKSUM @@ -0,0 +1,14 @@ +There is ability to integrity protect the exported configuration. +Exported txtar contains trailing ".csum" pseudo file with one or +multiple hashes of the digested data. + +DSC_HASHES environment variables specified what hashes must be included +and invoked. For example "blake2b-512:b2sum,skein-512:skein512" value +means, that BLAKE2b-512 is calculated by calling "b2sum" utility, and +Skein-512 by "skein512". + +"dsc csum" command can be used to calculate hashes over the arbitrary +data. "dsc import-check" can be used to verify piped in configuration +integrity. "dsc import-pipe" does not do any checks, so import-check +should be called before. "dsc import FILE" just calls import-check +followed by import-pipe. diff --git a/dsc b/dsc index fadc0fd..50186eb 100755 --- a/dsc +++ b/dsc @@ -31,14 +31,19 @@ if {$argc == 0} { dsc revert opt -- revert opt's configuration dsc commit -- commit (save) configuration dsc export [prefix] >file.txtar -- export (whole by default) configuration - dsc import @$w] w] + fconfigure $fh -translation binary + dict set hsh $name [list $fh $r $w] + } + fconfigure stdin -translation binary + while {![eof stdin]} { + set buf [read stdin $CopyBufLen] + dict for {_ fhs} $hsh { + lassign $fhs fh + puts -nonewline $fh $buf + } + } + foreach name [lsort [dict keys $hsh]] { + lassign [dict get $hsh $name] fh r w + close $fh + close $w + gets $r v + set v [split $v " "] + puts "$name [lindex $v 0]" + } + } + export-raw { set dirs [walk $Saved/$opt directory] if {$opt != ""} { set dirs [list $Saved/$opt {*}$dirs] @@ -361,7 +404,24 @@ switch [lindex $argv 0] { } } } - import { + export { + set exporter [open [list |$argv0 export-raw $opt]] + fconfigure $exporter -translation binary + lassign [pipe] r w + set hasher [open [list |$argv0 csum >@$w] w] + fconfigure $hasher -translation binary + fconfigure stdout -translation binary + while {![eof $exporter]} { + set buf [read $exporter $CopyBufLen] + puts -nonewline stdout $buf + puts -nonewline $hasher $buf + } + close $hasher + close $w + puts "-- .csum --" + puts -nonewline [read $r] + } + import-pipe { fconfigure stdin -translation binary while {[gets stdin line] >= 0} { set fn [txtar-fn $line] @@ -404,6 +464,8 @@ switch [lindex $argv 0] { puts $fh $line } elseif {[string range $fn 0 2] == "-- "} { puts $fh $fn + } elseif {$fn == ".csum"} { + break } else { close $fh set fh [openfh $fn] @@ -412,6 +474,34 @@ switch [lindex $argv 0] { close $fh exec sync } + import-check { + lassign [pipe] r w + set hasher [open [list |$argv0 csum >@$w] w] + fconfigure $hasher -translation binary + fconfigure stdin -translation binary + while {[gets stdin line] >= 0} { + if {$line == "-- .csum --"} { + break + } + puts $hasher $line + } + close $hasher + close $w + set got [read $r] + set exp [read stdin] + if {$got == $exp} { + exit 0 + } + puts stderr "integrity failure" + puts -nonewline stderr $got + puts stderr "\t!=" + puts -nonewline stderr $exp + exit 1 + } + import { + exec $argv0 import-check <$opt + exec $argv0 import-pipe <$opt + } path { if {[file exists $Stash/$opt]} { puts [file normalize $Stash/$opt] diff --git a/t/bin.t b/t/bin.t index 38ace6c..66ca017 100755 --- a/t/bin.t +++ b/t/bin.t @@ -12,7 +12,7 @@ test_expect_success "commit" "dsc commit" test_expect_success "export" "dsc export >out" test_expect_success "has base64" "grep -q :base64 out" test_expect_success "del *" 'dsc del ""' -test_expect_success "import" "dsc import out" test_expect_success "cmp" "test_cmp out prv" diff --git a/t/csum.t b/t/csum.t new file mode 100755 index 0000000..803b639 --- /dev/null +++ b/t/csum.t @@ -0,0 +1,40 @@ +#!/bin/sh + +test_description="$(basename $0)" +. $SHARNESS_TEST_SRCDIR/sharness.sh +. $SHARNESS_TEST_DIRECTORY/setup.rc + +test_expect_success "hostname" "dsc set sys/hostname mein" +test_expect_success "commit" "dsc commit" +test_expect_success "export-raw" "dsc export-raw >out" +test_expect_success "csum" "dsc csum detached" +cp out expected +printf "%s\n" "-- .csum --" >>expected +cat detached >>expected +test_expect_success "export" "dsc export >out" +test_expect_success "cmp" "test_cmp out expected" +test_expect_success "import-check" "dsc import-check out +test_expect_success "without csum -check" "! dsc import-check >expected +echo "whatever hash" >>expected +test_expect_success "wrong csum" "! dsc import-check out" +cat >expected <hsh +printf "%s\n" "-- .csum --" >>expected +echo -n "sha2-512 " >>expected +cat hsh >>expected +test_expect_success "cmp" "test_cmp out expected" + +test_done diff --git a/t/export.t b/t/export.t index f6d582e..922b99d 100755 --- a/t/export.t +++ b/t/export.t @@ -73,7 +73,7 @@ test_expect_success "cmp" "test_cmp out expected" test_expect_success "commit" "dsc commit" test_expect_success "diff" "dsc diff >out" test_expect_success "diff is empty" "! [ -s out ]" -test_expect_success "export" "dsc export >out" +test_expect_success "export" "dsc export-raw >out" cat >expected <out" -cat out test_expect_success "diff is empty" "! [ -s out ]" test_done diff --git a/t/import-path-validation.t b/t/import-path-validation.t index 4f25209..18a613e 100755 --- a/t/import-path-validation.t +++ b/t/import-path-validation.t @@ -9,7 +9,7 @@ cat >in <out 2>&1" +test_expect_success "import abs" "! dsc import-pipe out 2>&1" test_expect_success "import abs msg" \ '[ "$(cat out)" = "absolute paths are forbidden" ]' @@ -18,7 +18,7 @@ cat >in <out 2>&1" +test_expect_success "import rel" "! dsc import-pipe out 2>&1" test_expect_success "import rel msg" \ '[ "$(cat out)" = "relative paths are forbidden" ]' @@ -27,7 +27,7 @@ cat >in <out 2>&1" +test_expect_success "import abs" "! dsc import-pipe out 2>&1" test_expect_success "import abs msg" \ '[ "$(cat out)" = "absolute paths are forbidden" ]' @@ -35,7 +35,7 @@ cat >in <out 2>&1" +test_expect_success "import rel" "! dsc import-pipe out 2>&1" test_expect_success "import rel msg" \ '[ "$(cat out)" = "relative paths are forbidden" ]' diff --git a/t/setup.rc b/t/setup.rc index 0a7f392..56d94e2 100644 --- a/t/setup.rc +++ b/t/setup.rc @@ -3,3 +3,10 @@ export DSC_STASH=stash DSC_SAVED=saved export JIMLIB="$SHARNESS_TEST_DIRECTORY/../jimlib:$JIMLIB" PATH="$SHARNESS_TEST_DIRECTORY/..:$PATH" mkdir saved +export DSC_HASHES="sha2-512:sha512" +if command -v skein512 2>/dev/null >/dev/null ; then + DSC_HASHES="skein-512:skein512,$DSC_HASHES" +fi +if command -v b2sum 2>/dev/null >/dev/null ; then + DSC_HASHES="blake2b-512:b2sum,$DSC_HASHES" +fi -- 2.52.0