summaryrefslogtreecommitdiffstats
path: root/library/safe.tcl
diff options
context:
space:
mode:
Diffstat (limited to 'library/safe.tcl')
-rw-r--r--library/safe.tcl150
1 files changed, 138 insertions, 12 deletions
diff --git a/library/safe.tcl b/library/safe.tcl
index 186c2e7..c29b394 100644
--- a/library/safe.tcl
+++ b/library/safe.tcl
@@ -12,7 +12,7 @@
# See the file "license.terms" for information on usage and redistribution
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
#
-# RCS: @(#) $Id: safe.tcl,v 1.16 2006/11/03 00:34:52 hobbs Exp $
+# RCS: @(#) $Id: safe.tcl,v 1.16.2.1 2008/06/25 17:18:43 dgp Exp $
#
# The implementation is based on namespaces. These naming conventions
@@ -369,12 +369,24 @@ namespace eval ::safe {
lappend slave_auto_path "\$[PathToken $i]"
incr i
}
+ # Extend the access list with the paths used to look for Tcl
+ # Modules. We safe the virtual form separately as well, as
+ # syncing it with the slave has to be defered until the
+ # necessary commands are present for setup.
+ foreach dir [::tcl::tm::list] {
+ lappend access_path $dir
+ Set [PathToken $i $slave] $dir
+ lappend slave_auto_path "\$[PathToken $i]"
+ lappend slave_tm_path "\$[PathToken $i]"
+ incr i
+ }
+ Set [TmPathListName $slave] $slave_tm_path
Set $nname $i
- Set [PathListName $slave] $access_path
+ Set [PathListName $slave] $access_path
Set [VirtualPathListName $slave] $slave_auto_path
- Set [StaticsOkName $slave] $staticsok
- Set [NestedOkName $slave] $nestedok
+ Set [StaticsOkName $slave] $staticsok
+ Set [NestedOkName $slave] $nestedok
Set [DeleteHookName $slave] $deletehook
SyncAccessPath $slave
@@ -439,7 +451,7 @@ proc ::safe::interpAddToAccessPath {slave path} {
# NB we need to add [namespace current], aliases are always
# absolute paths.
::interp alias $slave source {} [namespace current]::AliasSource $slave
- ::interp alias $slave load {} [namespace current]::AliasLoad $slave
+ ::interp alias $slave load {} [namespace current]::AliasLoad $slave
# This alias lets the slave use the encoding names, convertfrom,
# convertto, and system, but not "encoding system <name>" to set
@@ -448,6 +460,10 @@ proc ::safe::interpAddToAccessPath {slave path} {
::interp alias $slave encoding {} [namespace current]::AliasEncoding \
$slave
+ # Handling Tcl Modules, we need a restricted form of Glob.
+ ::interp alias $slave glob {} [namespace current]::AliasGlob \
+ $slave
+
# This alias lets the slave have access to a subset of the 'file'
# command functionality.
@@ -463,15 +479,25 @@ proc ::safe::interpAddToAccessPath {slave path} {
# by Tcl_MakeSafe(3)
- # Source init.tcl into the slave, to get auto_load and other
- # procedures defined:
+ # Source init.tcl and tm.tcl into the slave, to get auto_load
+ # and other procedures defined:
- if {[catch {::interp eval $slave\
+ if {[catch {::interp eval $slave \
{source [file join $tcl_library init.tcl]}} msg]} {
Log $slave "can't source init.tcl ($msg)"
error "can't source init.tcl into slave $slave ($msg)"
}
+ if {[catch {::interp eval $slave \
+ {source [file join $tcl_library tm.tcl]}} msg]} {
+ Log $slave "can't source tm.tcl ($msg)"
+ error "can't source tm.tcl into slave $slave ($msg)"
+ }
+
+ # Sync the paths used to search for Tcl modules. This can be
+ # done only now, after tm.tcl was loaded.
+ ::interp eval $slave [list ::tcl::tm::add {*}[Set [TmPathListName $slave]]]
+
return $slave
}
@@ -610,6 +636,10 @@ proc ::safe::setLogCmd {args} {
proc VirtualPathListName {slave} {
return "[InterpStateName $slave](access_path_slave)"
}
+ # returns the variable name of the complete tm path list
+ proc TmPathListName {slave} {
+ return "[InterpStateName $slave](tm_path_slave)"
+ }
# returns the variable name of the number of items
proc PathNumberName {slave} {
return "[InterpStateName $slave](access_path,n)"
@@ -707,19 +737,96 @@ proc ::safe::setLogCmd {args} {
}
}
+ # AliasGlob is the target of the "glob" alias in safe interpreters.
+
+ proc AliasGlob {slave args} {
+ Log $slave "GLOB ! $args" NOTICE
+ set cmd {}
+ set at 0
+
+ set dir {}
+ set virtualdir {}
+
+ while {$at < [llength $args]} {
+ switch -glob -- [set opt [lindex $args $at]] {
+ -nocomplain -
+ -join { lappend cmd $opt ; incr at }
+ -directory {
+ lappend cmd $opt ; incr at
+ set virtualdir [lindex $args $at]
+
+ # get the real path from the virtual one.
+ if {[catch {set dir [TranslatePath $slave $virtualdir]} msg]} {
+ Log $slave $msg
+ return -code error "permission denied"
+ }
+ # check that the path is in the access path of that slave
+ if {[catch {DirInAccessPath $slave $dir} msg]} {
+ Log $slave $msg
+ return -code error "permission denied"
+ }
+ lappend cmd $dir ; incr at
+ }
+ pkgIndex.tcl {
+ # Oops, this is globbing a subdirectory in regular
+ # package search. That is not wanted. Abort,
+ # handler does catch already (because glob was not
+ # defined before). See package.tcl, lines 484ff in
+ # tclPkgUnknown.
+ error "unknown command glob"
+ }
+ -* {
+ Log $slave "Safe base rejecting glob option '$opt'"
+ error "Safe base rejecting glob option '$opt'"
+ }
+ default {
+ lappend cmd $opt ; incr at
+ }
+ }
+ }
+
+ Log $slave "GLOB = $cmd" NOTICE
+
+ if {[catch {::interp invokehidden $slave glob {*}$cmd} msg]} {
+ Log $slave $msg
+ return -code error "script error"
+ }
+
+ Log $slave "GLOB @ $msg" NOTICE
+
+ # Translate path back to what the slave should see.
+ set res {}
+ foreach p $msg {
+ regsub -- ^$dir $p $virtualdir p
+ lappend res $p
+ }
+
+ Log $slave "GLOB @ $res" NOTICE
+ return $res
+ }
# AliasSource is the target of the "source" alias in safe interpreters.
proc AliasSource {slave args} {
set argc [llength $args]
- # Allow only "source filename"
+ # Extended for handling of Tcl Modules to allow not only
+ # "source filename", but "source -encoding E filename" as
+ # well.
+ if {[lindex $args 0] eq "-encoding"} {
+ incr argc -2
+ set encoding [lrange $args 0 1]
+ set at 2
+ } else {
+ set at 0
+ set encoding {}
+ }
if {$argc != 1} {
- set msg "wrong # args: should be \"source fileName\""
+ set msg "wrong # args: should be \"source ?-encoding E? fileName\""
Log $slave "$msg ($args)"
return -code error $msg
}
- set file [lindex $args 0]
+ set file [lindex $args $at]
# get the real path from the virtual one.
if {[catch {set file [TranslatePath $slave $file]} msg]} {
@@ -740,7 +847,7 @@ proc ::safe::setLogCmd {args} {
}
# passed all the tests , lets source it:
- if {[catch {::interp invokehidden $slave source $file} msg]} {
+ if {[catch {::interp invokehidden $slave source {*}$encoding $file} msg]} {
Log $slave $msg
return -code error "script error"
}
@@ -840,6 +947,25 @@ proc ::safe::setLogCmd {args} {
}
}
+ proc DirInAccessPath {slave dir} {
+ set access_path [GetAccessPath $slave]
+
+ if {[file isfile $dir]} {
+ error "\"$dir\": is a file"
+ }
+
+ # Normalize paths for comparison since lsearch knows nothing of
+ # potential pathname anomalies.
+ set norm_dir [file normalize $dir]
+ foreach path $access_path {
+ lappend norm_access_path [file normalize $path]
+ }
+
+ if {[lsearch -exact $norm_access_path $norm_dir] == -1} {
+ error "\"$dir\": not in access_path"
+ }
+ }
+
# This procedure enables access from a safe interpreter to only a subset of
# the subcommands of a command: