Index: client/pkgr.eagle ================================================================== --- client/pkgr.eagle +++ client/pkgr.eagle @@ -38,10 +38,46 @@ return true } else { return false } } + + proc isHarpyCertificate { value } { + if {[string length $value] == 0 || [string first [string trim { + @1 + }] == 0}] + } + } proc getLookupVarNamePrefix {} { return ::rpkg_; # TODO: Make non-global? } @@ -295,167 +331,177 @@ if {![info exists metadata(certificate)]} then { error "missing script certificate" } # - # NOTE: Attempt to create a completely unique array variable name to - # hold the package metadata in this scripting language as well - # as possibly in the other necessary scripting language(s). - # - set newVarName [appendArgs \ - [getLookupVarNamePrefix] metadata_ [getLookupVarNameSuffix]] - - set newProcName(1) [appendArgs \ - [getLookupVarNamePrefix] eagleHasSecurity_ [getLookupVarNameSuffix]] - - set newProcName(2) [appendArgs \ - [getLookupVarNamePrefix] tclMustBeReady_ [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)] { - try { - # - # NOTE: If there is no package ifneeded script, there is nothing we - # can do here. - # - if {[string length ${%metadata%(ifNeeded)}] > 0} then { - # - # NOTE: Save the security state for the interpreter. Then, attempt - # to enable it. This will fail if one of the needed plugins - # cannot be loaded. - # - set savedSecurity [{%eagleHasSecurity%}] - if {!$savedSecurity} then {source enableSecurity} - - try { - # - # NOTE: Figure out temporary file name for the downloaded script - # and its associated script certificate. - # - set fileName(1) [file tempname] - set fileName(2) [appendArgs $fileName(1) .harpy] - - try { - # - # NOTE: Write the downloaded script to a temporary file. - # - writeFile $fileName(1) ${%metadata%(ifNeeded)} - - # - # NOTE: Write the downloaded script certificateto a temporary - # file. - # - if {[string length ${%metadata%(certificate)}] > 0} then { - writeFile $fileName(2) ${%metadata%(certificate)} - } - - # - # NOTE: This seems stupid. Why are we reading the downloaded - # script from the temporary file when we already had it - # in memory? The reason is that we need to make sure - # that the Harpy policy engine has a chance to check the - # downloaded script against its associated certificate. - # This will raise a script error if the script signature - # is missing or invalid. - # - set script(inner) [interp readorgetscriptfile -- \ - "" $fileName(1)] - - # - # 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 { - # - # NOTE: The target language is Eagle, which is evaluating - # this script. No special handling is needed here. - # - return [uplevel #0 $script(inner)] - } - Tcl { - # - # NOTE: The target language is Tcl; therefore, a bit of - # special handling is needed here. - # - {%tclMustBeReady%}; return [tcl eval [tcl master] [list \ - uplevel #0 $script(inner)]] - } - default { - error "unsupported language" - } - } - } finally { - # - # NOTE: Delete any temporary files that we created during the - # signed script evaluation. - # - 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) - } - } - } finally { - # - # NOTE: Restore the saved security state for the interpreter. - # - if {!$savedSecurity} then {source disableSecurity} - unset -nocomplain savedSecurity - } - } - } finally { - rename {%tclMustBeReady%} "" - rename {%eagleHasSecurity%} "" - - unset -nocomplain {%metadata%} - } - }] - - # - # NOTE: Copy the package metadata into the fresh array variable, - # if necessary, marshalling it from native Tcl to Eagle. - # - if {[isEagle]} then { - array set $newVarName [array get metadata] - - proc $newProcName(1) {} [info body [appendArgs \ - [namespace current] ::eagleHasSecurity]] - - proc $newProcName(2) {} [info body [appendArgs \ - [namespace current] ::tclMustBeReady]] - - return [eval $script(outer)] - } else { - eagleMustBeReady - - eagle [list array set $newVarName [array get metadata]] - - eagle [list proc $newProcName(1) {} [info body [appendArgs \ - [namespace current] ::eagleHasSecurity]]] - - eagle [list proc $newProcName(2) {} [info body [appendArgs \ - [namespace current] ::tclMustBeReady]]] - - return [eagle $script(outer)] + # NOTE: Figure out the "type" of script certificate we are now dealing + # with. + # + if {[isHarpyCertificate $metadata(certificate)]} then { + # + # NOTE: Attempt to create a completely unique array variable name to + # hold the package metadata in this scripting language as well + # as possibly in the other necessary scripting language(s). + # + set newVarName [appendArgs \ + [getLookupVarNamePrefix] metadata_ [getLookupVarNameSuffix]] + + set newProcName(1) [appendArgs \ + [getLookupVarNamePrefix] eagleHasSecurity_ [getLookupVarNameSuffix]] + + set newProcName(2) [appendArgs \ + [getLookupVarNamePrefix] tclMustBeReady_ [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)] { + try { + # + # NOTE: If there is no package ifneeded script, there is nothing we + # can do here. + # + if {[string length ${%metadata%(ifNeeded)}] > 0} then { + # + # NOTE: Save the security state for the interpreter. Then, attempt + # to enable it. This will fail if one of the needed plugins + # cannot be loaded. + # + set savedSecurity [{%eagleHasSecurity%}] + if {!$savedSecurity} then {source enableSecurity} + + try { + # + # NOTE: Figure out temporary file name for the downloaded script + # and its associated script certificate. + # + set fileName(1) [file tempname] + set fileName(2) [appendArgs $fileName(1) .harpy] + + try { + # + # NOTE: Write the downloaded script to a temporary file. + # + writeFile $fileName(1) ${%metadata%(ifNeeded)} + + # + # NOTE: Write the downloaded script certificateto a temporary + # file. + # + if {[string length ${%metadata%(certificate)}] > 0} then { + writeFile $fileName(2) ${%metadata%(certificate)} + } + + # + # NOTE: This seems stupid. Why are we reading the downloaded + # script from the temporary file when we already had it + # in memory? The reason is that we need to make sure + # that the Harpy policy engine has a chance to check the + # downloaded script against its associated certificate. + # This will raise a script error if the script signature + # is missing or invalid. + # + set script(inner) [interp readorgetscriptfile -- \ + "" $fileName(1)] + + # + # 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 { + # + # NOTE: The target language is Eagle, which is evaluating + # this script. No special handling is needed here. + # + return [uplevel #0 $script(inner)] + } + Tcl { + # + # NOTE: The target language is Tcl; therefore, a bit of + # special handling is needed here. + # + {%tclMustBeReady%}; return [tcl eval [tcl master] [list \ + uplevel #0 $script(inner)]] + } + default { + error "unsupported language" + } + } + } finally { + # + # NOTE: Delete any temporary files that we created during the + # signed script evaluation. + # + 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) + } + } + } finally { + # + # NOTE: Restore the saved security state for the interpreter. + # + if {!$savedSecurity} then {source disableSecurity} + unset -nocomplain savedSecurity + } + } + } finally { + rename {%tclMustBeReady%} "" + rename {%eagleHasSecurity%} "" + + unset -nocomplain {%metadata%} + } + }] + + # + # NOTE: Copy the package metadata into the fresh array variable, + # if necessary, marshalling it from native Tcl to Eagle. + # + if {[isEagle]} then { + array set $newVarName [array get metadata] + + proc $newProcName(1) {} [info body [appendArgs \ + [namespace current] ::eagleHasSecurity]] + + proc $newProcName(2) {} [info body [appendArgs \ + [namespace current] ::tclMustBeReady]] + + return [eval $script(outer)] + } else { + eagleMustBeReady + + eagle [list array set $newVarName [array get metadata]] + + eagle [list proc $newProcName(1) {} [info body [appendArgs \ + [namespace current] ::eagleHasSecurity]]] + + eagle [list proc $newProcName(2) {} [info body [appendArgs \ + [namespace current] ::tclMustBeReady]]] + + return [eagle $script(outer)] + } + } elseif {[isPgpSignature $metadata(certificate)]} then { + error "not yet implemented" + } else { + error "unsupported script certificate" } } proc setupPackageUnknownHandler {} { variable autoHook @@ -635,10 +681,20 @@ variable autoRequireGaruda; # DEFAULT: true if {![info exists autoRequireGaruda]} then { set autoRequireGaruda true } + + # + # NOTE: The command to use when verifying OpenPGP signatures for the + # downloaded package scripts. + # + variable pgpCommand; # DEFAULT: gpg2 --verify {${fileName}} + + if {![info exists pgpCommand]} then { + set pgpCommand {gpg2 --verify {${fileName}}} + } } proc main { package version } { # # NOTE: Issue the lookup request to the remote package repository. Index: client/pkgr.eagle.harpy ================================================================== --- client/pkgr.eagle.harpy +++ client/pkgr.eagle.harpy @@ -19,32 +19,32 @@ None Mistachkin Systems - 9443ba61-1df8-4a59-a1f4-7a8bad774fb2 + ed124b1a-4d17-4891-8565-cca6d319e05d SHA512 Script - 2016-08-15T22:17:41.1374531Z + 2016-08-16T21:34:49.8991719Z -1.00:00:00 0x2c322765603b5278 - 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