<pre style='margin:0'>
Joshua Root (jmroot) pushed a commit to branch master
in repository macports-base.
</pre>
<p><a href="https://github.com/macports/macports-base/commit/ce4af0f6fa16002f3fb77c5080a28839c74a5f3d">https://github.com/macports/macports-base/commit/ce4af0f6fa16002f3fb77c5080a28839c74a5f3d</a></p>
<pre style="white-space: pre; background: #F8F8F8"><span style='display:block; white-space:pre;color:#808000;'>commit ce4af0f6fa16002f3fb77c5080a28839c74a5f3d
</span>Author: Joshua Root <jmr@macports.org>
AuthorDate: Fri May 9 01:48:57 2025 +1000
<span style='display:block; white-space:pre;color:#404040;'> Add signify support for archives
</span>---
src/macports1.0/macports.tcl | 6 +-
src/package1.0/portarchivefetch.tcl | 137 ++++++++++++++++++++++++++----------
2 files changed, 105 insertions(+), 38 deletions(-)
<span style='display:block; white-space:pre;color:#808080;'>diff --git a/src/macports1.0/macports.tcl b/src/macports1.0/macports.tcl
</span><span style='display:block; white-space:pre;color:#808080;'>index 6c1b9dc68..28ec8cf56 100644
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>--- a/src/macports1.0/macports.tcl
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/src/macports1.0/macports.tcl
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -2071,6 +2071,10 @@ proc macports::worker_init {workername portpath porturl portbuildpath options va
</span> # tool path cache
$workername alias get_tool_path macports::get_tool_path
<span style='display:block; white-space:pre;background:#e0ffe0;'>+ # Signature verification
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ $workername alias verify_signature_openssl macports::verify_signature_openssl
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ $workername alias verify_signature_signify macports::verify_signature_signify
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span> $workername alias get_compatible_xcode_versions macports::get_compatible_xcode_versions
$workername alias curlwrap macports::curlwrap
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -6656,7 +6660,7 @@ proc macports::get_archive_sites_conf_values {} {
</span> variable os_platform; variable os_major
set archive_sites_conf_values [list]
set all_names [list]
<span style='display:block; white-space:pre;background:#ffe0e0;'>- set defaults_list [list applications_dir /Applications/MacPorts prefix /opt/local type tbz2]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ set defaults_list [list applications_dir /Applications/MacPorts prefix /opt/local type tbz2 sigtype rmd160]
</span> if {$os_platform eq "darwin" && $os_major <= 12} {
lappend defaults_list cxx_stdlib libstdc++ delete_la_files no
} else {
<span style='display:block; white-space:pre;color:#808080;'>diff --git a/src/package1.0/portarchivefetch.tcl b/src/package1.0/portarchivefetch.tcl
</span><span style='display:block; white-space:pre;color:#808080;'>index 5da0ad702..2119ec35d 100644
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>--- a/src/package1.0/portarchivefetch.tcl
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/src/package1.0/portarchivefetch.tcl
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -72,7 +72,9 @@ proc portarchivefetch::filter_sites {} {
</span> portfetch::mirror_sites::archive_frameworks_dir \
portfetch::mirror_sites::archive_applications_dir \
portfetch::mirror_sites::archive_cxx_stdlib \
<span style='display:block; white-space:pre;background:#ffe0e0;'>- portfetch::mirror_sites::archive_delete_la_files
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ portfetch::mirror_sites::archive_delete_la_files \
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ portfetch::mirror_sites::archive_sigtype \
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ portfetch::mirror_sites::archive_pubkey
</span>
# get defaults from ports tree resources
set mirrorfile [get_full_archive_sites_path]
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -164,6 +166,69 @@ proc portarchivefetch::checkfiles {urls} {
</span> }
<span style='display:block; white-space:pre;background:#e0ffe0;'>+# Return all signature types that may be used with the configured sites
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+proc portarchivefetch::get_all_sigtypes {} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ global archive_sites portfetch::mirror_sites::archive_sigtype
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ set sigtypes [dict create]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ foreach site $archive_sites {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ # If the entry is a URL rather than a mirror site name then
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ # this will actually extract the URL scheme, but that's OK
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ # since it won't exist in the array and will be skipped.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ set site [lindex [split $site :] 0]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if {[info exists archive_sigtype($site)]} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ dict set sigtypes $archive_sigtype($site) 1
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if {[dict size $sigtypes] > 0} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ return [dict keys $sigtypes]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ } else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ # Legacy default
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ return rmd160
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# Verify signature for a fetched archive using any of the public keys
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# set for each archive site or in pubkeys.conf.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+proc portarchivefetch::verify_signature {archive_path sig_path} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ global archive_sites archivefetch.pubkeys \
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ portfetch::mirror_sites::archive_sigtype \
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ portfetch::mirror_sites::archive_pubkey
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ # Chop off the .TMP before getting extension
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ set archivetype [file extension [file rootname $archive_path]]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ set sigtype [file extension $sig_path]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ set pubkeys [dict create]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ foreach site $archive_sites {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ set site_split [split $site :]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ set site [lindex $site_split 0]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ set tag [lindex $site_split end]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if {".$tag" eq $archivetype && [info exists archive_sigtype($site)]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ && [info exists archive_pubkey($site)]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ && ".$archive_sigtype($site)" eq $sigtype
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ } then {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ # Use dict to avoid duplicates if a key is added in both
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ # the archive site definition and pubkeys.conf.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ dict set pubkeys $archive_pubkey($site) 1
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ foreach pubkey ${archivefetch.pubkeys} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ set keytype [file extension $pubkey]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if {($keytype eq ".pub" && $sigtype eq ".sig") || ($keytype eq ".pem" && $sigtype eq ".rmd160")} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ dict set pubkeys $pubkey 1
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ # Succeed if the signature can be verified with any of the keys
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ foreach pubkey [dict keys $pubkeys] {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if {($sigtype eq ".sig" && [verify_signature_signify $archive_path $pubkey $sig_path])
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ || ($sigtype eq ".rmd160" && [verify_signature_openssl $archive_path $pubkey $sig_path])
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ } then {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ return 1
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ return 0
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span> # Perform a standard fetch, assembling fetch urls from
# the listed url variable and associated archive file
proc portarchivefetch::fetchfiles {args} {
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -232,11 +297,13 @@ proc portarchivefetch::fetchfiles {args} {
</span> ui_error [format [msgcat::mc "No defined site for tag: %s, using archive_sites"] $url_var]
set urlmap($url_var) $urlmap(archive_sites)
}
<span style='display:block; white-space:pre;background:#e0ffe0;'>+ if {![info exists sigtypes]} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ set sigtypes [get_all_sigtypes]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ set archive_tmp_path ${incoming_path}/${archive}.TMP
</span> set failed_sites 0
set archive_fetched 0
set lastError ""
<span style='display:block; white-space:pre;background:#ffe0e0;'>- # there should be an rmd160 digest of the archive signed with one of the trusted keys
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- set signature ${incoming_path}/${archive}.rmd160
</span> set sig_fetched 0
foreach site $urlmap($url_var) {
set orig_site $site
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -249,20 +316,20 @@ proc portarchivefetch::fetchfiles {args} {
</span> if {!$archive_fetched} {
ui_msg "$UI_PREFIX [format [msgcat::mc "Attempting to fetch %s from %s"] $archive ${site}]"
try {
<span style='display:block; white-space:pre;background:#ffe0e0;'>- curlwrap fetch $orig_site $credentials {*}$fetch_options $file_url ${incoming_path}/${archive}.TMP
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ curlwrap fetch $orig_site $credentials {*}$fetch_options $file_url $archive_tmp_path
</span> set archive_fetched 1
} trap {POSIX SIG SIGINT} {_ eOptions} {
ui_debug [msgcat::mc "Aborted fetching archive due to SIGINT"]
<span style='display:block; white-space:pre;background:#ffe0e0;'>- file delete -force ${incoming_path}/${archive}.TMP
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ file delete -force $archive_tmp_path
</span> throw [dict get $eOptions -errorcode] [dict get $eOptions -errorinfo]
} trap {POSIX SIG SIGTERM} {_ eOptions} {
ui_debug [msgcat::mc "Aborted fetching archive due to SIGTERM"]
<span style='display:block; white-space:pre;background:#ffe0e0;'>- file delete -force ${incoming_path}/${archive}.TMP
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ file delete -force $archive_tmp_path
</span> throw [dict get $eOptions -errorcode] [dict get $eOptions -errorinfo]
} on error {eMessage} {
ui_debug [msgcat::mc "Fetching archive failed: %s" $eMessage]
set lastError $eMessage
<span style='display:block; white-space:pre;background:#ffe0e0;'>- file delete -force ${incoming_path}/${archive}.TMP
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ file delete -force $archive_tmp_path
</span> incr failed_sites
if {$failed_sites > 2 && ![tbool ports_binary_only] && ![_archive_available]} {
break
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -271,46 +338,42 @@ proc portarchivefetch::fetchfiles {args} {
</span> }
# fetch signature
if {$archive_fetched} {
<span style='display:block; white-space:pre;background:#ffe0e0;'>- ui_msg "$UI_PREFIX [format [msgcat::mc "Attempting to fetch %s from %s"] ${archive}.rmd160 $site]"
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- try {
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- curlwrap fetch $orig_site $credentials {*}$fetch_options ${file_url}.rmd160 $signature
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- set sig_fetched 1
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ # TODO: record signature type for each URL somehow
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ foreach sigtype $sigtypes {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ set signature ${incoming_path}/${archive}.${sigtype}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ ui_msg "$UI_PREFIX [format [msgcat::mc "Attempting to fetch %s from %s"] ${archive}.${sigtype} $site]"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ try {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ curlwrap fetch $orig_site $credentials {*}$fetch_options ${file_url}.${sigtype} $signature
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ set sig_fetched 1
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ break
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ } trap {POSIX SIG SIGINT} {_ eOptions} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ ui_debug [msgcat::mc "Aborted fetching archive due to SIGINT"]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ file delete -force $archive_tmp_path $signature
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ throw [dict get $eOptions -errorcode] [dict get $eOptions -errorinfo]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ } trap {POSIX SIG SIGTERM} {_ eOptions} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ ui_debug [msgcat::mc "Aborted fetching archive due to SIGTERM"]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ file delete -force $archive_tmp_path $signature
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ throw [dict get $eOptions -errorcode] [dict get $eOptions -errorinfo]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ } on error {eMessage} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ ui_debug [msgcat::mc "Fetching archive signature failed: %s" $eMessage]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ set lastError $eMessage
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ file delete -force $signature
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if {$sig_fetched} {
</span> break
<span style='display:block; white-space:pre;background:#ffe0e0;'>- } trap {POSIX SIG SIGINT} {_ eOptions} {
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- ui_debug [msgcat::mc "Aborted fetching archive due to SIGINT"]
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- file delete -force ${incoming_path}/${archive}.TMP $signature
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- throw [dict get $eOptions -errorcode] [dict get $eOptions -errorinfo]
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- } trap {POSIX SIG SIGTERM} {_ eOptions} {
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- ui_debug [msgcat::mc "Aborted fetching archive due to SIGTERM"]
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- file delete -force ${incoming_path}/${archive}.TMP $signature
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- throw [dict get $eOptions -errorcode] [dict get $eOptions -errorinfo]
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- } on error {eMessage} {
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- ui_debug [msgcat::mc "Fetching archive signature failed: %s" $eMessage]
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- set lastError $eMessage
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- file delete -force $signature
</span> }
}
}
if {$archive_fetched && $sig_fetched} {
<span style='display:block; white-space:pre;background:#ffe0e0;'>- set openssl [findBinary openssl $portutil::autoconf::openssl_path]
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- set verified 0
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- global archivefetch.pubkeys
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- foreach pubkey ${archivefetch.pubkeys} {
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- if {![catch {exec $openssl dgst -ripemd160 -verify $pubkey -signature $signature "${incoming_path}/${archive}.TMP"} result]} {
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- set verified 1
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- break
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- } else {
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- ui_debug "failed verification with key $pubkey"
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- ui_debug "openssl output: $result"
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- }
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ set verified [verify_signature $archive_tmp_path $signature]
</span> file delete -force $signature
if {!$verified} {
# fall back to building from source (or error out later if binary only mode)
ui_warn "Failed to verify signature for archive!"
<span style='display:block; white-space:pre;background:#ffe0e0;'>- file delete -force "${incoming_path}/${archive}.TMP"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ file delete -force $archive_tmp_path
</span> break
<span style='display:block; white-space:pre;background:#ffe0e0;'>- } elseif {[catch {file rename -force "${incoming_path}/${archive}.TMP" "${archivefetch.fulldestpath}/${archive}"} result]} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ } elseif {[catch {file rename -force $archive_tmp_path ${archivefetch.fulldestpath}/${archive}} result]} {
</span> ui_debug "$::errorInfo"
return -code error "Failed to move downloaded archive into place: $result"
}
</pre><pre style='margin:0'>
</pre>