Ignore:
Timestamp:
Jun 19, 2010, 11:21:02 PM (10 years ago)
Author:
jmr@…
Message:

Added integrity checking for fetched archives via signed digests. New pubkeys.conf file allows configuring keys to trust. The private counterpart of the installed public key will of course need to live on our binary building server.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/base/src/package1.0/portarchivefetch.tcl

    r68965 r68996  
    4848options archive_sites archivefetch.user archivefetch.password \
    4949    archivefetch.use_epsv archivefetch.ignore_sslcert \
    50     archive_sites.mirror_subdir
     50    archive_sites.mirror_subdir archivefetch.pubkeys
    5151
    5252# user name & password
     
    5757# Ignore SSL certificate
    5858default archivefetch.ignore_sslcert no
     59default archivefetch.pubkeys {$archivefetch_pubkeys}
    5960
    6061default archive_sites macports_archives
     
    126127# the listed url variable and associated archive file
    127128proc portarchivefetch::fetchfiles {args} {
    128     global archivefetch.fulldestpath UI_PREFIX
     129    global portarchivepath archivefetch.fulldestpath UI_PREFIX
    129130    global archivefetch.user archivefetch.password archivefetch.use_epsv \
    130131           archivefetch.ignore_sslcert
     
    142143        }
    143144    }
     145    set incoming_path [file join ${portarchivepath} incoming]
     146    if {![file isdirectory $incoming_path]} {
     147        if {[catch {file mkdir $incoming_path} result]} {
     148            elevateToRoot "archivefetch"
     149            set elevated yes
     150            if {[catch {file mkdir $incoming_path} result]} {
     151                return -code error [format [msgcat::mc "Unable to create archive fetch path: %s"] $result]
     152            }
     153        }
     154    }
    144155    chownAsRoot ${archivefetch.fulldestpath}
     156    chownAsRoot $incoming_path
    145157    if {[info exists elevated] && $elevated == yes} {
    146158        dropPrivileges
     
    169181                return -code error [format [msgcat::mc "%s must be writable"] ${archivefetch.fulldestpath}]
    170182            }
     183            if {![file writable $incoming_path]} {
     184                return -code error [format [msgcat::mc "%s must be writable"] $incoming_path]
     185            }
    171186            if {!$sorted} {
    172187                portfetch::sortsites archivefetch_urls {} archive_sites
     
    182197                set file_url [portfetch::assemble_url $site $archive]
    183198                set effectiveURL ""
    184                 if {![catch {eval curl fetch --effective-url effectiveURL $fetch_options {$file_url} ${archivefetch.fulldestpath}/${archive}.TMP} result] &&
    185                     ![catch {file rename -force "${archivefetch.fulldestpath}/${archive}.TMP" "${archivefetch.fulldestpath}/${archive}"} result]} {
     199                if {![catch {eval curl fetch --effective-url effectiveURL $fetch_options {$file_url} {"${incoming_path}/${archive}.TMP"}} result]} {
    186200                    # Successful fetch
    187201                    set fetched 1
     
    189203                } else {
    190204                    ui_debug "[msgcat::mc "Fetching archive failed:"]: $result"
    191                     file delete -force "${archivefetch.fulldestpath}/${archive}.TMP"
     205                    file delete -force "${incoming_path}/${archive}.TMP"
    192206                }
    193207            }
    194208            if {[info exists fetched]} {
     209                # there should be an rmd160 digest of the archive signed with one of the trusted keys
     210                set signature "${incoming_path}/${archive}.rmd160"
     211                ui_msg "$UI_PREFIX [format [msgcat::mc "Attempting to fetch %s from %s"] ${archive}.rmd160 $site]"
     212                # reusing $file_url from the last iteration of the loop above
     213                if {[catch {eval curl fetch --effective-url effectiveURL $fetch_options {${file_url}.rmd160} {$signature}} result]} {
     214                    ui_debug "$::errorInfo"
     215                    return -code error "Failed to fetch signature for archive: $result"
     216                }
     217                set verified 0
     218                foreach pubkey [option archivefetch.pubkeys] {
     219                    set openssl [findBinary openssl $portutil::autoconf::openssl_path]
     220                    if {![catch {exec $openssl dgst -ripemd160 -verify $pubkey -signature $signature "${incoming_path}/${archive}.TMP"} result]} {
     221                        set verified 1
     222                        break
     223                    } else {
     224                        ui_debug "failed verification with key $pubkey"
     225                        ui_debug "openssl output: $result"
     226                    }
     227                }
     228                if {!$verified} {
     229                    return -code error "Failed to verify signature for archive!"
     230                }
     231                if {[catch {file rename -force "${incoming_path}/${archive}.TMP" "${archivefetch.fulldestpath}/${archive}"} result]} {
     232                    ui_debug "$::errorInfo"
     233                    return -code error "Failed to move downloaded archive into place: $result"
     234                }
     235                file delete -force $signature
    195236                return 0
    196237            }
Note: See TracChangeset for help on using the changeset viewer.