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.

git-svn-id: https://svn.macports.org/repository/macports/trunk/base@68996 d073be05-634f-4543-b044-5fe20cf6d1d6
This commit is contained in:
Joshua Root
2010-06-19 23:21:02 +00:00
parent 5204b4abaa
commit 1c6b345ea9
9 changed files with 143 additions and 24 deletions
+1
View File
@@ -34,6 +34,7 @@ include Mk/macports.subdir.mk
install::
[ ! -f $(DESTDIR)${sysconfdir}/macports/mp_version ] || rm -vf $(DESTDIR)${sysconfdir}/macports/mp_version
$(INSTALL) -o ${DSTUSR} -g ${DSTGRP} -m 444 setupenv.bash $(DESTDIR)${datadir}/macports/
$(INSTALL) -o ${DSTUSR} -g ${DSTGRP} -m 444 macports-pubkey.pem $(DESTDIR)${datadir}/macports/
# Only run these scripts when not building in a destroot
ifeq ($(DESTDIR),)
# Add [default] tag to the central MacPorts repository, if it isn't already
Vendored
+46 -1
View File
@@ -688,6 +688,7 @@ ZIP
UNZIP
TAR
SED
OPENSSL
RSYNC
RMDIR
PATCH
@@ -816,6 +817,7 @@ OBJCFLAGS
MTREE
CVS
SVN
OPENSSL
RSYNC
SED
TAR
@@ -1495,6 +1497,7 @@ Some influential environment variables:
MTREE path to mtree command
CVS path to cvs command
SVN path to svn command
OPENSSL path to openssl command
RSYNC path to rsync command
SED path to sed command
TAR path to tar command
@@ -4810,6 +4813,46 @@ $as_echo "no" >&6; }
fi
# Extract the first word of "openssl", so it can be a program name with args.
set dummy openssl; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if test "${ac_cv_path_OPENSSL+set}" = set; then :
$as_echo_n "(cached) " >&6
else
case $OPENSSL in
[\\/]* | ?:[\\/]*)
ac_cv_path_OPENSSL="$OPENSSL" # Let the user override the test with a path.
;;
*)
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
ac_cv_path_OPENSSL="$as_dir/$ac_word$ac_exec_ext"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
;;
esac
fi
OPENSSL=$ac_cv_path_OPENSSL
if test -n "$OPENSSL"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $OPENSSL" >&5
$as_echo "$OPENSSL" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
# Extract the first word of "sed", so it can be a program name with args.
set dummy sed; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
@@ -5607,6 +5650,7 @@ fi
# Extract the first word of "sed", so it can be a program name with args.
set dummy sed; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
@@ -9365,7 +9409,7 @@ prefix=$oldprefix
# Output
ac_config_files="$ac_config_files Doxyfile Makefile Mk/macports.autoconf.mk doc/prefix.mtree doc/macosx.mtree doc/macports.conf portmgr/freebsd/Makefile src/Makefile src/macports1.0/macports_autoconf.tcl src/port1.0/port_autoconf.tcl src/registry2.0/registry_autoconf.tcl src/programs/Makefile src/macports1.0/macports_fastload.tcl setupenv.bash"
ac_config_files="$ac_config_files Doxyfile Makefile Mk/macports.autoconf.mk doc/prefix.mtree doc/macosx.mtree doc/macports.conf doc/pubkeys.conf portmgr/freebsd/Makefile src/Makefile src/macports1.0/macports_autoconf.tcl src/port1.0/port_autoconf.tcl src/registry2.0/registry_autoconf.tcl src/programs/Makefile src/macports1.0/macports_fastload.tcl setupenv.bash"
ac_config_files="$ac_config_files src/pkg_mkindex.sh"
@@ -10062,6 +10106,7 @@ do
"doc/prefix.mtree") CONFIG_FILES="$CONFIG_FILES doc/prefix.mtree" ;;
"doc/macosx.mtree") CONFIG_FILES="$CONFIG_FILES doc/macosx.mtree" ;;
"doc/macports.conf") CONFIG_FILES="$CONFIG_FILES doc/macports.conf" ;;
"doc/pubkeys.conf") CONFIG_FILES="$CONFIG_FILES doc/pubkeys.conf" ;;
"portmgr/freebsd/Makefile") CONFIG_FILES="$CONFIG_FILES portmgr/freebsd/Makefile" ;;
"src/Makefile") CONFIG_FILES="$CONFIG_FILES src/Makefile" ;;
"src/macports1.0/macports_autoconf.tcl") CONFIG_FILES="$CONFIG_FILES src/macports1.0/macports_autoconf.tcl" ;;
+3
View File
@@ -103,6 +103,7 @@ AC_PATH_PROG(LIPO, [lipo], [])
AC_PATH_PROG(PATCH, [patch], [])
AC_PATH_PROG(RMDIR, [rmdir], [])
AC_PATH_PROG(RSYNC, [rsync], [])
AC_PATH_PROG(OPENSSL, [openssl], [])
AC_PATH_PROG(SED, [sed])
AC_PATH_PROG(TAR, [tar])
AC_PATH_PROG(UNZIP, [unzip])
@@ -127,6 +128,7 @@ AC_PATH_PROG(OPEN, [open], [])
AC_ARG_VAR(MTREE, [path to mtree command])
AC_ARG_VAR(CVS, [path to cvs command])
AC_ARG_VAR(SVN, [path to svn command])
AC_ARG_VAR(OPENSSL, [path to openssl command])
AC_ARG_VAR(RSYNC, [path to rsync command])
AC_ARG_VAR(SED, [path to sed command])
AC_ARG_VAR(TAR, [path to tar command])
@@ -346,6 +348,7 @@ AC_CONFIG_FILES([
doc/prefix.mtree
doc/macosx.mtree
doc/macports.conf
doc/pubkeys.conf
portmgr/freebsd/Makefile
src/Makefile
src/macports1.0/macports_autoconf.tcl
+9 -16
View File
@@ -1,6 +1,7 @@
MAN1= port.1
MAN5= macports.conf.5
MAN7= portfile.7 portstyle.7 porthier.7 portgroup.7
CONF= macports.conf pubkeys.conf sources.conf variants.conf
INSTALLDIR= ${DESTDIR}${prefix}
TOPSRCDIR= ..
@@ -16,7 +17,7 @@ clean:
test:
distclean: clean
rm -f prefix.mtree macports.conf macosx.mtree
rm -f macports.conf macosx.mtree prefix.mtree pubkeys.conf
%.1.gz: %.1
gzip -c $^ > $@
@@ -40,21 +41,13 @@ install: all
$(LN_S) share/man ${INSTALLDIR}/man; \
fi
$(INSTALL) -o ${DSTUSR} -g ${DSTGRP} -m 444 macports.conf ${DESTDIR}${mpconfigdir}/macports.conf.default
if test ! -e ${DESTDIR}${mpconfigdir}/macports.conf ; then \
set -x; \
$(INSTALL) -o ${DSTUSR} -g ${DSTGRP} -m 644 macports.conf ${DESTDIR}${mpconfigdir}; \
fi
$(INSTALL) -o ${DSTUSR} -g ${DSTGRP} -m 444 sources.conf ${DESTDIR}${mpconfigdir}/sources.conf.default
if test ! -e ${DESTDIR}${mpconfigdir}/sources.conf ; then \
set -x; \
$(INSTALL) -o ${DSTUSR} -g ${DSTGRP} -m 644 sources.conf ${DESTDIR}${mpconfigdir}; \
fi
$(INSTALL) -o ${DSTUSR} -g ${DSTGRP} -m 444 variants.conf ${DESTDIR}${mpconfigdir}/variants.conf.default
if test ! -e ${DESTDIR}${mpconfigdir}/variants.conf ; then \
set -x; \
$(INSTALL) -o ${DSTUSR} -g ${DSTGRP} -m 644 variants.conf ${DESTDIR}${mpconfigdir}; \
fi
for f in ${CONF}; do \
$(INSTALL) -o ${DSTUSR} -g ${DSTGRP} -m 444 $$f ${DESTDIR}${mpconfigdir}/$${f}.default; \
if test ! -e ${DESTDIR}${mpconfigdir}/$$f ; then \
set -x; \
$(INSTALL) -o ${DSTUSR} -g ${DSTGRP} -m 644 $$f ${DESTDIR}${mpconfigdir}; \
fi; \
done
# delete old uncompressed man pages if they exist
for m in ${MAN1}; do rm -f ${INSTALLDIR}/share/man/man1/$$m ; done
+10
View File
@@ -0,0 +1,10 @@
# Downloaded archives will only be used if they can be verified by a public
# key listed here. Use full paths, one per line.
@prefix_expanded@/share/macports/macports-pubkey.pem
# To distribute archives of your own, you need a key pair generated like so:
# openssl genrsa -des3 -out privkey.pem 2048
# openssl rsa -in privkey.pem -pubout -out pubkey.pem
# Then sign the archives like this:
# openssl dgst -ripemd160 -sign privkey.pem -out archive.tbz2.rmd160 archive.tbz2
+9
View File
@@ -0,0 +1,9 @@
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxPlw2FSMEavmtuHq5O+f
XPUXndBnUyoX8iJvQ7NlsNY+hleE307kOAly79WFq2aAGIyKB/Ob8V+qCLW4FGOO
4NfvOnte1WxQP8oVxOM9ai3CeN6AnUxDWUTYVJ1R+a5ZQVnJyGBKkV+jLlj0PD/z
d8PCJ/j7Wzizw3d4s9uBIR1zpLY8lVgTivsj5FM3RP6hS7ww7I47goQshoL+YFVg
NhE9I63oZXqFF130l9jJI9axHUqza7a42WCv04J8iv8SQhgv6fY9ZQnIBMQ4hPDH
klGusdf4HlCbUViKhxmUpfj6FJabh2jV48ymaoTZB767GJyACF4mvnNDkenEXf41
/wIDAQAB
-----END PUBLIC KEY-----
+18 -2
View File
@@ -51,8 +51,8 @@ namespace eval macports {
variable portinterp_options "\
portdbpath porturl portpath portbuildpath auto_path prefix prefix_frozen portsharepath \
registry.path registry.format registry.installtype portarchivemode portarchivepath \
portarchivetype portautoclean porttrace keeplogs portverbose destroot_umask rsync_server \
rsync_options rsync_dir startupitem_type place_worksymlink macportsuser \
portarchivetype archivefetch_pubkeys portautoclean porttrace keeplogs portverbose destroot_umask \
rsync_server rsync_options rsync_dir startupitem_type place_worksymlink macportsuser \
mp_remote_url mp_remote_submit_url configureccache configuredistcc configurepipe buildnicevalue buildmakejobs \
applications_dir current_phase frameworks_dir developer_dir universal_archs build_arch \
os_arch os_endian os_version os_major os_platform macosx_version macosx_deployment_target $user_options"
@@ -464,6 +464,7 @@ proc mportinit {{up_ui_options {}} {up_options {}} {up_variations {}}} {
global macports::os_platform
global macports::macosx_version
global macports::macosx_deployment_target
global macports::archivefetch_pubkeys
# Set the system encoding to utf-8
encoding system utf-8
@@ -602,6 +603,21 @@ proc mportinit {{up_ui_options {}} {up_options {}} {up_variations {}}} {
global macports::global_variations
array set macports::global_variations [array get variations]
# pubkeys.conf
set macports::archivefetch_pubkeys {}
if {[file isfile [file join ${macports_conf_path} pubkeys.conf]]} {
set fd [open [file join ${macports_conf_path} pubkeys.conf] r]
while {[gets $fd line] >= 0} {
set line [string trim $line]
if {![regexp {^[\ \t]*#.*$|^$} $line]} {
lappend macports::archivefetch_pubkeys $line
}
}
close $fd
} else {
ui_debug "pubkeys.conf does not exist."
}
if {![info exists portdbpath]} {
return -code error "portdbpath must be set in ${macports_conf_path}/macports.conf or in your ${macports_user_dir}/macports.conf"
}
+46 -5
View File
@@ -47,7 +47,7 @@ namespace eval portarchivefetch {
options archive_sites archivefetch.user archivefetch.password \
archivefetch.use_epsv archivefetch.ignore_sslcert \
archive_sites.mirror_subdir
archive_sites.mirror_subdir archivefetch.pubkeys
# user name & password
default archivefetch.user ""
@@ -56,6 +56,7 @@ default archivefetch.password ""
default archivefetch.use_epsv no
# Ignore SSL certificate
default archivefetch.ignore_sslcert no
default archivefetch.pubkeys {$archivefetch_pubkeys}
default archive_sites macports_archives
default archive_sites.listfile {"archive_sites.tcl"}
@@ -125,7 +126,7 @@ proc portarchivefetch::checkfiles {urls} {
# Perform a standard fetch, assembling fetch urls from
# the listed url variable and associated archive file
proc portarchivefetch::fetchfiles {args} {
global archivefetch.fulldestpath UI_PREFIX
global portarchivepath archivefetch.fulldestpath UI_PREFIX
global archivefetch.user archivefetch.password archivefetch.use_epsv \
archivefetch.ignore_sslcert
global portverbose ports_binary_only
@@ -141,7 +142,18 @@ proc portarchivefetch::fetchfiles {args} {
}
}
}
set incoming_path [file join ${portarchivepath} incoming]
if {![file isdirectory $incoming_path]} {
if {[catch {file mkdir $incoming_path} result]} {
elevateToRoot "archivefetch"
set elevated yes
if {[catch {file mkdir $incoming_path} result]} {
return -code error [format [msgcat::mc "Unable to create archive fetch path: %s"] $result]
}
}
}
chownAsRoot ${archivefetch.fulldestpath}
chownAsRoot $incoming_path
if {[info exists elevated] && $elevated == yes} {
dropPrivileges
}
@@ -168,6 +180,9 @@ proc portarchivefetch::fetchfiles {args} {
if {![file writable ${archivefetch.fulldestpath}]} {
return -code error [format [msgcat::mc "%s must be writable"] ${archivefetch.fulldestpath}]
}
if {![file writable $incoming_path]} {
return -code error [format [msgcat::mc "%s must be writable"] $incoming_path]
}
if {!$sorted} {
portfetch::sortsites archivefetch_urls {} archive_sites
set sorted yes
@@ -181,17 +196,43 @@ proc portarchivefetch::fetchfiles {args} {
ui_msg "$UI_PREFIX [format [msgcat::mc "Attempting to fetch %s from %s"] $archive $site]"
set file_url [portfetch::assemble_url $site $archive]
set effectiveURL ""
if {![catch {eval curl fetch --effective-url effectiveURL $fetch_options {$file_url} ${archivefetch.fulldestpath}/${archive}.TMP} result] &&
![catch {file rename -force "${archivefetch.fulldestpath}/${archive}.TMP" "${archivefetch.fulldestpath}/${archive}"} result]} {
if {![catch {eval curl fetch --effective-url effectiveURL $fetch_options {$file_url} {"${incoming_path}/${archive}.TMP"}} result]} {
# Successful fetch
set fetched 1
break
} else {
ui_debug "[msgcat::mc "Fetching archive failed:"]: $result"
file delete -force "${archivefetch.fulldestpath}/${archive}.TMP"
file delete -force "${incoming_path}/${archive}.TMP"
}
}
if {[info exists fetched]} {
# there should be an rmd160 digest of the archive signed with one of the trusted keys
set signature "${incoming_path}/${archive}.rmd160"
ui_msg "$UI_PREFIX [format [msgcat::mc "Attempting to fetch %s from %s"] ${archive}.rmd160 $site]"
# reusing $file_url from the last iteration of the loop above
if {[catch {eval curl fetch --effective-url effectiveURL $fetch_options {${file_url}.rmd160} {$signature}} result]} {
ui_debug "$::errorInfo"
return -code error "Failed to fetch signature for archive: $result"
}
set verified 0
foreach pubkey [option archivefetch.pubkeys] {
set openssl [findBinary openssl $portutil::autoconf::openssl_path]
if {![catch {exec $openssl dgst -ripemd160 -verify $pubkey -signature $signature "${incoming_path}/${archive}.TMP"} result]} {
set verified 1
break
} else {
ui_debug "failed verification with key $pubkey"
ui_debug "openssl output: $result"
}
}
if {!$verified} {
return -code error "Failed to verify signature for archive!"
}
if {[catch {file rename -force "${incoming_path}/${archive}.TMP" "${archivefetch.fulldestpath}/${archive}"} result]} {
ui_debug "$::errorInfo"
return -code error "Failed to move downloaded archive into place: $result"
}
file delete -force $signature
return 0
}
} else {
+1
View File
@@ -44,6 +44,7 @@ namespace eval portutil::autoconf {
variable hg_path "@HG@"
variable gzip_path "@GZIP@"
variable lipo_path "@LIPO@"
variable openssl_path "@OPENSSL@"
variable patch_path "@PATCH@"
variable gnupatch_path "@GNUPATCH@"
variable rmdir_path "@RMDIR@"