Index: client/pkgd.eagle ================================================================== --- client/pkgd.eagle +++ client/pkgd.eagle @@ -114,18 +114,22 @@ variable persistentDirectory variable quiet if {$persistent} then { set downloadRootDirectory [file join $persistentDirectory] - } elseif {[info exists ::env(PKGD_TEMP)]} then { - set downloadRootDirectory [file join $::env(PKGD_TEMP) pkgr] - } elseif {[info exists ::env(TEMP)]} then { - set downloadRootDirectory [file join $::env(TEMP) pkgr] - } elseif {[info exists ::env(TMP)]} then { - set downloadRootDirectory [file join $::env(TMP) pkgr] } else { - error "please set PKGD_TEMP (via environment) to temporary directory" + global env + + if {[info exists env(PKGD_TEMP)]} then { + set downloadRootDirectory [file join $env(PKGD_TEMP) pkgr] + } elseif {[info exists env(TEMP)]} then { + set downloadRootDirectory [file join $env(TEMP) pkgr] + } elseif {[info exists env(TMP)]} then { + set downloadRootDirectory [file join $env(TMP) pkgr] + } else { + error "please set PKGD_TEMP (via environment) to temporary directory" + } } set downloadDirectories [list] foreach fileName $fileNames { Index: client/pkgd.eagle.harpy ================================================================== --- client/pkgd.eagle.harpy +++ client/pkgd.eagle.harpy @@ -19,32 +19,32 @@ None Mistachkin Systems - 06b76c33-0e3b-4120-8142-0053f4d17521 + 36e6e8e4-39ac-4f29-928e-5c10e32cb1eb SHA512 Script - 2016-08-16T02:50:58.2761250Z + 2016-08-16T23:11:46.2517109Z -1.00:00:00 0x2c322765603b5278 - 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 Index: client/pkgr.eagle ================================================================== --- client/pkgr.eagle +++ client/pkgr.eagle @@ -30,16 +30,20 @@ package require Eagle.Library proc stringIsList { value } { if {[isEagle]} then { return [string is list $value] - } elseif {[info exists ::tcl_version] && $::tcl_version >= 8.5} then { - return [string is list $value] - } elseif {[catch {llength $value}] == 0} then { - return true } else { - return false + global tcl_version + + if {[info exists tcl_version] && $tcl_version >= 8.5} then { + return [string is list $value] + } elseif {[catch {llength $value}] == 0} then { + return true + } else { + return false + } } } proc isHarpyCertificate { value } { if {[string length $value] == 0 || [string first [string trim { @@ -58,10 +62,41 @@ return true } else { return false } } + + proc getFileTempName {} { + if {[isEagle]} then { + return [file tempname] + } else { + global env + + if {[info exists env(PKGR_TEMP)]} then { + set directory $env(PKGD_TEMP) + } elseif {[info exists env(TEMP)]} then { + set directory $env(TEMP) + } elseif {[info exists env(TMP)]} then { + set directory $env(TMP) + } else { + error "please set PKGR_TEMP (via environment) to temporary directory" + } + + set counter [expr {[pid] ^ int(rand() * 0xFFFF)}] + + while {1} { + set fileNameOnly [format tcl%04X.tmp $counter] + set fileName [file join $directory $fileNameOnly] + + if {![file exists $fileName]} then { + return $fileName + } + + incr counter + } + } + } proc verifyPgpSignature { fileName } { variable pgpCommand if {[isEagle]} then { @@ -349,18 +384,21 @@ [getLookupVarNamePrefix] eagleHasSecurity_ [getLookupVarNameSuffix]] set newProcName(2) [appendArgs \ [getLookupVarNamePrefix] tclMustBeReady_ [getLookupVarNameSuffix]] + set newProcName(3) [appendArgs \ + [getLookupVarNamePrefix] getFileTempName_ [getLookupVarNameSuffix]] + # # NOTE: Create the Eagle script block that will be used to securely # evaluate a signed package script. This must be evaluated in # Eagle because it uses several plugins only available there. # set script(outer) [string map [list \ %metadata% $newVarName %eagleHasSecurity% $newProcName(1) \ - %tclMustBeReady% $newProcName(2)] { + %tclMustBeReady% $newProcName(2) %getFileTempName% $newProcName(3)] { try { # # NOTE: If there is no package ifneeded script, there is nothing we # can do here. # @@ -376,21 +414,21 @@ try { # # NOTE: Figure out temporary file name for the downloaded script # and its associated script certificate. # - set fileName(1) [file tempname] + set fileName(1) [{%getFileTempName%}] set fileName(2) [appendArgs $fileName(1) .harpy] try { # - # NOTE: Write the downloaded script to a temporary file. + # NOTE: Write downloaded script to a temporary file. # writeFile $fileName(1) ${%metadata%(ifNeeded)} # - # NOTE: Write the downloaded script certificateto a temporary + # NOTE: Write downloaded script certificate to a temporary # file. # if {[string length ${%metadata%(certificate)}] > 0} then { writeFile $fileName(2) ${%metadata%(certificate)} } @@ -436,12 +474,12 @@ error "unsupported language" } } } finally { # - # NOTE: Delete any temporary files that we created during the - # signed script evaluation. + # NOTE: Delete the temporary files that we created for the + # signed script verification. # if {[string length $fileName(2)] > 0 && \ [file exists $fileName(2)]} then { catch {file delete $fileName(2)} unset -nocomplain fileName(2) @@ -460,10 +498,11 @@ if {!$savedSecurity} then {source disableSecurity} unset -nocomplain savedSecurity } } } finally { + rename {%getFileTempName%} "" rename {%tclMustBeReady%} "" rename {%eagleHasSecurity%} "" unset -nocomplain {%metadata%} } @@ -480,10 +519,13 @@ [namespace current] ::eagleHasSecurity]] proc $newProcName(2) {} [info body [appendArgs \ [namespace current] ::tclMustBeReady]] + proc $newProcName(3) {} [info body [appendArgs \ + [namespace current] ::getFileTempName]] + return [eval $script(outer)] } else { eagleMustBeReady eagle [list array set $newVarName [array get metadata]] @@ -492,14 +534,102 @@ [namespace current] ::eagleHasSecurity]]] eagle [list proc $newProcName(2) {} [info body [appendArgs \ [namespace current] ::tclMustBeReady]]] + eagle [list proc $newProcName(3) {} [info body [appendArgs \ + [namespace current] ::getFileTempName]]] + return [eagle $script(outer)] } } elseif {[isPgpSignature $metadata(certificate)]} then { - error "not yet implemented" + # + # NOTE: If there is no package ifneeded script, there is nothing we + # can do here. + # + if {[string length $metadata(ifNeeded)] > 0} then { + # + # NOTE: Figure out temporary file name for the downloaded script + # and its associated PGP signature. + # + set fileName(1) [getFileTempName] + set fileName(2) [appendArgs $fileName(1) .asc] + + # + # NOTE: Write downloaded script to a temporary file. + # + writeFile $fileName(1) $metadata(ifNeeded) + + # + # NOTE: Write downloaded script PGP signature a temporary file. + # + if {[string length $metadata(certificate)] > 0} then { + writeFile $fileName(2) $metadata(certificate) + } + + # + # NOTE: Attempt to verify the PGP signature for the package script. + # If this fails, raise an error. + # + if {![verifyPgpSignature $fileName(2)]} then { + error [appendArgs \ + "couldn't read file \"" $fileName(1) "\": bad PGP signature"] + } + + # + # NOTE: Delete the temporary files that we created for the PGP + # signature verification. + # + if {[string length $fileName(2)] > 0 && \ + [file exists $fileName(2)]} then { + catch {file delete $fileName(2)} + unset -nocomplain fileName(2) + } + + if {[string length $fileName(1)] > 0 && \ + [file exists $fileName(1)]} then { + catch {file delete $fileName(1)} + unset -nocomplain fileName(1) + } + + # + # NOTE: The PGP signature was verified; use the downloaded package + # script verbatim. + # + set script(inner) $metadata(ifNeeded) + + # + # NOTE: Determine the target language for the package script, which + # may or may not be the language that is currently evaluating + # this script (Eagle). The default language, when one was not + # explicitly specified, is Eagle. In the future, this may be + # changed, e.g. to use the file extension of the client script. + # + switch -exact -- $metadata(language) { + "" - + Eagle { + if {[isEagle]} then { + return [uplevel #0 $script(inner)] + } else { + eagleMustBeReady + + return [eagle [list uplevel #0 $script(inner)]] + } + } + Tcl { + if {[isEagle]} then { + tclMustBeReady; return [tcl eval [tcl master] [list \ + uplevel #0 $script(inner)]] + } else { + return [uplevel #0 $script(inner)] + } + } + default { + error "unsupported language" + } + } + } } else { error "unsupported script certificate" } } Index: client/pkgr.eagle.harpy ================================================================== --- client/pkgr.eagle.harpy +++ client/pkgr.eagle.harpy @@ -19,32 +19,32 @@ None Mistachkin Systems - ed124b1a-4d17-4891-8565-cca6d319e05d + 24efe138-647e-406e-a4ff-1d8728db1a30 SHA512 Script - 2016-08-16T21:34:49.8991719Z + 2016-08-16T23:06:52.1540547Z -1.00:00:00 0x2c322765603b5278 - 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